From b2a9bb279d294270761c142f24b8b08dcf353d4c Mon Sep 17 00:00:00 2001 From: Alan Gutierrez Date: Sun, 12 Oct 2025 23:41:07 -0500 Subject: [PATCH] Implement polygon tessellation. The `triangulate` function takes a polygon with floating-point lat/lon coordinates, converts to integer coordinates with millimeter precision (using 2^32 scaling), performs constrained Delaunay triangulation, and encodes the resulting triangles with boundary edge information for block kd-tree spatial indexing. It handles polygons with holes correctly, preserving which triangle edges lie on the original polygon boundaries versus internal tessellation edges. --- Cargo.toml | 2 ++ src/spatial/triangle.rs | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 977677d31..2cdb25b1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ measure_time = "0.9.0" arc-swap = "1.5.0" bon = "3.3.1" robust = "1.2" +i_triangle = "0.38.0" columnar = { version = "0.6", path = "./columnar", package = "tantivy-columnar" } sstable = { version = "0.6", path = "./sstable", package = "tantivy-sstable", optional = true } @@ -71,6 +72,7 @@ futures-util = { version = "0.3.28", optional = true } futures-channel = { version = "0.3.28", optional = true } fnv = "1.0.7" typetag = "0.2.21" +geo-types = "0.7.17" [target.'cfg(windows)'.dependencies] winapi = "0.3.9" diff --git a/src/spatial/triangle.rs b/src/spatial/triangle.rs index 689cdf2a7..e25d9734f 100644 --- a/src/spatial/triangle.rs +++ b/src/spatial/triangle.rs @@ -5,6 +5,9 @@ //! contain an additional vertex and packed reconstruction metadata, allowing exact triangle //! recovery when needed. +use geo_types::MultiPolygon; +use i_triangle::i_overlay::i_float::int::point::IntPoint; +use i_triangle::int::triangulatable::IntTriangulatable; use robust::{orient2d, Coord}; const MINY_MINX_MAXY_MAXX_Y_X: i32 = 0; @@ -300,8 +303,66 @@ impl Triangle { } } +/// Triangulates a geographic polygon into encoded triangles for block kd-tree indexing. +/// +/// This function is used by applications to convert a `MultiPolygon` into triangles for indexing +/// in the block kd-tree. Once triangulated, the encoded triangles can be inserted into the block +/// kd-tree and queries against the block kd-tree will return the associated `doc_id`. +/// +/// Takes a polygon with floating-point lat/lon coordinates, converts to integer coordinates with +/// millimeter precision (using 2^32 scaling), performs constrained Delaunay triangulation, and +/// encodes the resulting triangles with boundary edge information. +/// +/// Handles polygons with holes correctly, preserving which triangle edges lie on the original +/// polygon boundaries versus internal tessellation edges. +pub fn triangulate(doc_id: u32, geometry: G, triangles: &mut Vec) +where G: Into> { + let multi = geometry.into(); + for polygon in multi { + let exterior: Vec = polygon + .exterior() + .coords() + .map(|coord| { + let lat = (coord.y / (180.0 / (1i64 << 32) as f64)).floor() as i32; + let lon = (coord.x / (360.0 / (1i64 << 32) as f64)).floor() as i32; + IntPoint::new(lon, lat) + }) + .collect(); + let mut i_polygon = vec![exterior]; + for interior in polygon.interiors() { + let hole: Vec = interior + .coords() + .map(|coord| { + let lat = (coord.y / (180.0 / (1i64 << 32) as f64)).floor() as i32; + let lon = (coord.x / (360.0 / (1i64 << 32) as f64)).floor() as i32; + IntPoint::new(lon, lat) + }) + .collect(); + i_polygon.push(hole); + } + let delaunay = i_polygon.triangulate().into_delaunay(); + for (_, triangle) in delaunay.triangles.iter().enumerate() { + let bounds = [ + triangle.neighbors[0] == usize::MAX, + triangle.neighbors[1] == usize::MAX, + triangle.neighbors[2] == usize::MAX, + ]; + let v0 = &delaunay.points[triangle.vertices[0].index]; + let v1 = &delaunay.points[triangle.vertices[1].index]; + let v2 = &delaunay.points[triangle.vertices[2].index]; + triangles.push(Triangle::new( + doc_id, + [v0.y, v0.x, v1.y, v1.x, v2.y, v2.x], + bounds, + )) + } + } +} + #[cfg(test)] mod tests { + use geo_types::polygon; + use super::*; #[test] @@ -373,4 +434,14 @@ mod tests { assert_eq!(decoded_coords, coords); } } + + #[test] + fn triangulate_box() { + let polygon = polygon![ + (x: 0.0, y: 0.0), (x: 10.0, y: 0.0), (x: 10.0, y: 10.0), (x: 0.0, y: 10.0) + ]; + let mut triangles = Vec::new(); + triangulate(1, polygon, &mut triangles); + assert_eq!(triangles.len(), 2); + } }