use crate::float::delaunay::Delaunay;
use crate::int::uniform::IntUniformTriangulatable;
use i_overlay::core::integer::OverlayInt;
use i_overlay::i_float::adapter::FloatPointAdapter;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;
use i_overlay::i_float::float::rect::FloatRect;
use i_overlay::i_shape::float::adapter::PathToInt;
use i_overlay::i_shape::int::shape::IntShape;
use i_overlay::i_shape::source::resource::ShapeResource;
pub trait UniformTriangulatable<P: FloatPointCompatible> {
fn uniform_triangulate(&self, edge_length: P::Scalar) -> Delaunay<P> {
self.uniform_triangulate_as::<i32>(edge_length)
}
fn uniform_triangulate_as<I>(&self, edge_length: P::Scalar) -> Delaunay<P, I>
where
I: OverlayInt;
}
impl<S, P> UniformTriangulatable<P> for S
where
S: ShapeResource<P>,
P: FloatPointCompatible,
{
fn uniform_triangulate_as<I>(&self, edge_length: P::Scalar) -> Delaunay<P, I>
where
I: OverlayInt,
{
assert!(
edge_length.is_finite() && edge_length > P::Scalar::ZERO,
"edge_length must be finite and positive"
);
let rect =
FloatRect::with_iter(self.iter_paths().flatten()).unwrap_or_else(FloatRect::zero);
let adapter = FloatPointAdapter::<P, I>::new(rect);
let int_edge_length = adapter.round_len_to_int(edge_length);
assert!(
int_edge_length > I::ONE,
"edge_length is below the precision of the selected integer engine"
);
let shape: IntShape<I> = self
.iter_paths()
.map(|path| path.to_int(&adapter))
.collect();
let delaunay = shape.uniform_triangulate(int_edge_length.to_uint());
Delaunay { delaunay, adapter }
}
}
#[cfg(test)]
mod tests {
use super::UniformTriangulatable;
use alloc::vec;
#[test]
fn fills_square_with_boundary_and_grid_points() {
let contour = [[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]];
let triangulation = contour.uniform_triangulate(2.0).to_triangulation::<u32>();
assert!(triangulation.points.len() > 20);
triangulation.validate(100.0, 0.000_001);
}
#[test]
fn fills_shape_without_filling_hole() {
let shape = vec![
vec![[0.0, 0.0], [20.0, 0.0], [20.0, 20.0], [0.0, 20.0]],
vec![[7.0, 7.0], [7.0, 13.0], [13.0, 13.0], [13.0, 7.0]],
];
let triangulation = shape.uniform_triangulate(2.0).to_triangulation::<u32>();
assert!(triangulation.points.len() > 40);
triangulation.validate(364.0, 0.000_001);
}
#[test]
fn narrow_shape_falls_back_to_split_boundary() {
let contour = [[0.0, 0.0], [10.0, 0.0], [10.0, 0.5], [0.0, 0.5]];
let triangulation = contour.uniform_triangulate(2.0).to_triangulation::<u32>();
assert!(!triangulation.indices.is_empty());
triangulation.validate(5.0, 0.000_001);
}
}