diff --git a/iTriangle/src/float/locator.rs b/iTriangle/src/float/locator.rs new file mode 100644 index 0000000..d44037c --- /dev/null +++ b/iTriangle/src/float/locator.rs @@ -0,0 +1,104 @@ +use alloc::vec::Vec; +use i_overlay::i_float::float::compatible::FloatPointCompatible; +use i_overlay::i_float::float::number::FloatNumber; +use i_overlay::{i_float::adapter::FloatPointAdapter, i_shape::float::adapter::PathToInt}; + +use crate::{ + float::triangulation::Triangulation, + int::triangulation::{IndexType, IntTriangulation}, + location::PointLocationInTriangulation, +}; + +pub trait PointInTriangulationLocator

{ + fn locate_points(&self, points: &[P]) -> Vec + where + P: FloatPointCompatible, + T: FloatNumber; +} + +impl Triangulation { + pub fn locate_points(&self, points: &[P]) -> Vec + where + P: FloatPointCompatible, + { + let adapter = FloatPointAdapter::with_iter(self.points.iter().chain(points.iter())); + + let int_triangulation = IntTriangulation { + points: self.points.to_int(&adapter), + indices: self.indices.clone(), + }; + + int_triangulation.locate_points(&points.to_int(&adapter)) + } +} + +impl PointInTriangulationLocator

for Triangulation { + #[inline] + fn locate_points(&self, points: &[P]) -> Vec + where + P: FloatPointCompatible, + T: FloatNumber, + { + Triangulation::locate_points::(self, points) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use crate::{ + float::triangulation::Triangulation, + location::{PointLocationInTriangulation, TriangleIndex}, + }; + + fn square_triangulation() -> Triangulation<[f64; 2], u16> { + Triangulation { + points: vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]], + indices: vec![0, 1, 2, 0, 2, 3], + } + } + + #[test] + fn test_locate_points() { + let triangulation = square_triangulation(); + let points_to_locate = vec![ + [3.0, 1.0], + [1.0, 3.0], + [2.0, 2.0], + [2.0, 0.0], + [0.0, 0.0], + [5.0, 1.0], + ]; + + let locations = triangulation.locate_points::(&points_to_locate); + + assert!(matches!( + locations[0], + PointLocationInTriangulation::InsideTriangle(t) if t == TriangleIndex::new(0) + )); + assert!(matches!( + locations[1], + PointLocationInTriangulation::InsideTriangle(t) if t == TriangleIndex::new(1) + )); + assert!(matches!( + locations[2], + PointLocationInTriangulation::OnInteriorEdge(a, b) + if a == TriangleIndex::new(0) && b == TriangleIndex::new(1) + )); + assert!(matches!( + locations[3], + PointLocationInTriangulation::OnExteriorEdge(t) + if t == TriangleIndex::new(0) + )); + assert!(matches!( + &locations[4], + PointLocationInTriangulation::OnVertex(triangles) + if triangles.as_slice() == [TriangleIndex::new(0), TriangleIndex::new(1)] + )); + assert!(matches!( + locations[5], + PointLocationInTriangulation::Outside + )); + } +} diff --git a/iTriangle/src/float/mod.rs b/iTriangle/src/float/mod.rs index 87f2935..b601b5a 100644 --- a/iTriangle/src/float/mod.rs +++ b/iTriangle/src/float/mod.rs @@ -4,6 +4,7 @@ pub mod circumcenter; pub mod convex; pub mod custom; pub mod delaunay; +pub mod locator; pub mod triangulatable; pub mod triangulation; pub mod triangulator; diff --git a/iTriangle/src/int/locator.rs b/iTriangle/src/int/locator.rs new file mode 100644 index 0000000..9e1c16f --- /dev/null +++ b/iTriangle/src/int/locator.rs @@ -0,0 +1,149 @@ +use alloc::vec; +use alloc::vec::Vec; +use i_overlay::i_float::triangle::Triangle; +use i_overlay::i_shape::int::IntPoint; + +use crate::{ + int::triangulation::{IndexType, IntTriangulation}, + location::{PointLocationInTriangulation, TriangleIndex}, +}; + +pub trait IntPointInTriangulationLocator { + fn locate_points(&self, points: &[IntPoint]) -> Vec; +} + +impl IntTriangulation { + pub fn locate_points(&self, points: &[IntPoint]) -> Vec { + let mut result = vec![PointLocationInTriangulation::Outside; points.len()]; + + for (index, triangle) in self.indices.chunks_exact(3).enumerate() { + let vertex0 = self.points[triangle[0].into_usize()]; + let vertex1 = self.points[triangle[1].into_usize()]; + let vertex2 = self.points[triangle[2].into_usize()]; + let triangle_index = TriangleIndex::new(index); + + for (point_index, &point) in points.iter().enumerate() { + if point == vertex0 || point == vertex1 || point == vertex2 { + match &mut result[point_index] { + PointLocationInTriangulation::Outside => { + result[point_index] = + PointLocationInTriangulation::OnVertex(vec![triangle_index]); + } + PointLocationInTriangulation::OnVertex(hits) => { + hits.push(triangle_index); + } + // Shouldn't happen. + _ => {} + } + + continue; + } + + if !Triangle::is_contain_point(point, vertex0, vertex1, vertex2) { + continue; + } + + if Triangle::is_contain_point_exclude_borders(point, vertex0, vertex1, vertex2) { + match &result[point_index] { + PointLocationInTriangulation::Outside => { + result[point_index] = + PointLocationInTriangulation::InsideTriangle(triangle_index); + } + // Shouldn't happen. + _ => {} + } + + continue; + } + + match &result[point_index] { + PointLocationInTriangulation::Outside => { + result[point_index] = + PointLocationInTriangulation::OnExteriorEdge(triangle_index); + } + PointLocationInTriangulation::OnExteriorEdge(i) => { + result[point_index] = + PointLocationInTriangulation::OnInteriorEdge(*i, triangle_index); + } + // Shouldn't happen. + _ => {} + } + } + } + + result + } +} + +impl IntPointInTriangulationLocator for IntTriangulation { + #[inline] + fn locate_points(&self, points: &[IntPoint]) -> Vec { + IntTriangulation::locate_points(self, points) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + use i_overlay::i_shape::int::IntPoint; + + use crate::{ + int::triangulation::IntTriangulation, + location::{PointLocationInTriangulation, TriangleIndex}, + }; + + fn square_triangulation() -> IntTriangulation { + IntTriangulation { + points: vec![ + IntPoint::new(0, 0), + IntPoint::new(4, 0), + IntPoint::new(4, 4), + IntPoint::new(0, 4), + ], + indices: vec![0, 1, 2, 0, 2, 3], + } + } + + #[test] + fn test_locate_points() { + let triangulation = square_triangulation(); + let points_to_locate = vec![ + IntPoint::new(3, 1), + IntPoint::new(1, 3), + IntPoint::new(2, 2), + IntPoint::new(2, 0), + IntPoint::new(0, 0), + IntPoint::new(5, 1), + ]; + + let locations = triangulation.locate_points(&points_to_locate); + + assert!(matches!( + locations[0], + PointLocationInTriangulation::InsideTriangle(t) if t == TriangleIndex::new(0) + )); + assert!(matches!( + locations[1], + PointLocationInTriangulation::InsideTriangle(t) if t == TriangleIndex::new(1) + )); + assert!(matches!( + locations[2], + PointLocationInTriangulation::OnInteriorEdge(a, b) + if a == TriangleIndex::new(0) && b == TriangleIndex::new(1) + )); + assert!(matches!( + locations[3], + PointLocationInTriangulation::OnExteriorEdge(t) + if t == TriangleIndex::new(0) + )); + assert!(matches!( + locations[4].clone(), + PointLocationInTriangulation::OnVertex(triangles) + if triangles.as_slice() == [TriangleIndex::new(0), TriangleIndex::new(1)] + )); + assert!(matches!( + locations[5], + PointLocationInTriangulation::Outside + )); + } +} diff --git a/iTriangle/src/int/mod.rs b/iTriangle/src/int/mod.rs index 68038d6..fd64741 100644 --- a/iTriangle/src/int/mod.rs +++ b/iTriangle/src/int/mod.rs @@ -1,6 +1,7 @@ mod binder; pub mod custom; pub mod earcut; +pub mod locator; mod meta; pub(crate) mod monotone; mod solver; diff --git a/iTriangle/src/lib.rs b/iTriangle/src/lib.rs index f318f6c..8bb0e90 100644 --- a/iTriangle/src/lib.rs +++ b/iTriangle/src/lib.rs @@ -6,6 +6,7 @@ pub mod float; pub mod geom; mod index; pub mod int; +pub mod location; pub mod tessellation; pub use i_overlay; diff --git a/iTriangle/src/location.rs b/iTriangle/src/location.rs new file mode 100644 index 0000000..415a2c4 --- /dev/null +++ b/iTriangle/src/location.rs @@ -0,0 +1,28 @@ +use alloc::vec::Vec; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct TriangleIndex(usize); + +impl TriangleIndex { + pub fn new(index: usize) -> Self { + Self(index) + } + + pub fn index(&self) -> usize { + self.0 + } + + pub fn to_vertex_indices(&self) -> [usize; 3] { + let offset = 3 * self.0; + [offset, offset + 1, offset + 2] + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum PointLocationInTriangulation { + Outside, + InsideTriangle(TriangleIndex), + OnExteriorEdge(TriangleIndex), + OnInteriorEdge(TriangleIndex, TriangleIndex), + OnVertex(Vec), +}