mod delaunay;
mod polygon;
use fj_interop::mesh::Mesh;
use fj_math::Point;
use self::{delaunay::TriangulationPoint, polygon::Polygon};
use super::approx::{face::FaceApprox, Approx, Tolerance};
pub trait Triangulate: Sized {
fn triangulate(self) -> Mesh<Point<3>> {
let mut mesh = Mesh::new();
self.triangulate_into_mesh(&mut mesh);
mesh
}
fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>);
}
impl<T> Triangulate for (T, Tolerance)
where
T: Approx,
T::Approximation: IntoIterator<Item = FaceApprox>,
{
fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>) {
let (approx, tolerance) = self;
let approx = approx.approx(tolerance);
for approx in approx {
approx.triangulate_into_mesh(mesh);
}
}
}
impl Triangulate for FaceApprox {
fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>) {
let points: Vec<_> = self
.points()
.into_iter()
.map(|point| TriangulationPoint {
point_surface: point.local_form,
point_global: point.global_form,
})
.collect();
let face_as_polygon = Polygon::new()
.with_exterior(
self.exterior
.points()
.into_iter()
.map(|point| point.local_form),
)
.with_interiors(self.interiors.into_iter().map(|interior| {
interior.points().into_iter().map(|point| point.local_form)
}));
let mut triangles =
delaunay::triangulate(points, self.coord_handedness);
triangles.retain(|triangle| {
face_as_polygon
.contains_triangle(triangle.map(|point| point.point_surface))
});
for triangle in triangles {
let points = triangle.map(|point| point.point_global);
mesh.push_triangle(points, self.color);
}
}
}
#[cfg(test)]
mod tests {
use fj_interop::mesh::Mesh;
use fj_math::{Point, Scalar};
use crate::{
algorithms::approx::{Approx, Tolerance},
objects::{Face, Surface},
stores::Stores,
};
use super::Triangulate;
#[test]
fn simple() -> anyhow::Result<()> {
let stores = Stores::new();
let a = [0., 0.];
let b = [2., 0.];
let c = [2., 2.];
let d = [0., 1.];
let surface = stores.surfaces.insert(Surface::xy_plane());
let face = Face::builder(&stores, surface)
.with_exterior_polygon_from_points([a, b, c, d])
.build();
let a = Point::from(a).to_xyz();
let b = Point::from(b).to_xyz();
let c = Point::from(c).to_xyz();
let d = Point::from(d).to_xyz();
let triangles = triangulate(face)?;
assert!(triangles.contains_triangle([a, b, d]));
assert!(triangles.contains_triangle([b, c, d]));
assert!(!triangles.contains_triangle([a, b, c]));
assert!(!triangles.contains_triangle([a, c, d]));
Ok(())
}
#[test]
fn simple_hole() -> anyhow::Result<()> {
let stores = Stores::new();
let a = [0., 0.];
let b = [4., 0.];
let c = [4., 4.];
let d = [0., 4.];
let e = [1., 1.];
let f = [1., 2.];
let g = [3., 3.];
let h = [3., 1.];
let surface = stores.surfaces.insert(Surface::xy_plane());
let face = Face::builder(&stores, surface.clone())
.with_exterior_polygon_from_points([a, b, c, d])
.with_interior_polygon_from_points([e, f, g, h])
.build();
let triangles = triangulate(face)?;
let a = surface.point_from_surface_coords(a);
let b = surface.point_from_surface_coords(b);
let e = surface.point_from_surface_coords(e);
let f = surface.point_from_surface_coords(f);
let g = surface.point_from_surface_coords(g);
let h = surface.point_from_surface_coords(h);
assert!(triangles.contains_triangle([a, b, e]));
assert!(triangles.contains_triangle([b, e, h]));
assert!(!triangles.contains_triangle([e, f, g]));
assert!(!triangles.contains_triangle([e, g, h]));
assert!(!triangles.contains_triangle([e, f, h]));
assert!(!triangles.contains_triangle([f, g, h]));
Ok(())
}
#[ignore]
#[test]
fn sharp_concave_shape() -> anyhow::Result<()> {
let stores = Stores::new();
let a = [0., 0.];
let b = [0.4, 0.];
let c = [0.4, 1.0];
let d = [0.1, 0.1];
let e = [0., 0.8];
let surface = stores.surfaces.insert(Surface::xy_plane());
let face = Face::builder(&stores, surface.clone())
.with_exterior_polygon_from_points([a, b, c, d, e])
.build();
let triangles = triangulate(face)?;
let a3 = surface.point_from_surface_coords(a);
let b3 = surface.point_from_surface_coords(b);
let c3 = surface.point_from_surface_coords(c);
let d3 = surface.point_from_surface_coords(d);
let e3 = surface.point_from_surface_coords(e);
assert!(triangles.contains_triangle([a3, b3, d3]));
assert!(triangles.contains_triangle([b3, c3, d3]));
assert!(triangles.contains_triangle([a3, d3, e3]));
assert!(!triangles.contains_triangle([b3, e3, d3]));
Ok(())
}
fn triangulate(face: impl Into<Face>) -> anyhow::Result<Mesh<Point<3>>> {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;
Ok(face.into().approx(tolerance).triangulate())
}
}