mod delaunay;
mod polygon;
use fj_interop::Mesh;
use fj_math::Point;
use crate::Core;
use self::polygon::Polygon;
use super::approx::{face::FaceApprox, Approx, Tolerance};
pub trait Triangulate: Sized {
fn triangulate(self, core: &mut Core) -> Mesh<Point<3>> {
let mut mesh = Mesh::new();
self.triangulate_into_mesh(&mut mesh, core);
mesh
}
fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>, core: &mut Core);
}
impl<T> Triangulate for (T, Tolerance)
where
T: Approx,
T::Approximation: IntoIterator<Item = FaceApprox>,
{
fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>, core: &mut Core) {
let (approx, tolerance) = self;
let approx = approx.approx(tolerance, core);
for approx in approx {
approx.triangulate_into_mesh(mesh, core);
}
}
}
impl Triangulate for FaceApprox {
fn triangulate_into_mesh(
self,
mesh: &mut Mesh<Point<3>>,
_core: &mut Core,
) {
let face_as_polygon = Polygon::new()
.with_exterior(
self.exterior
.points()
.into_iter()
.map(|point| point.local_form),
)
.with_interiors(self.interiors.iter().map(|interior| {
interior.points().into_iter().map(|point| point.local_form)
}));
let cycles = [self.exterior].into_iter().chain(self.interiors);
let mut triangles =
delaunay::triangulate(cycles, self.coord_handedness);
triangles.retain(|triangle| {
face_as_polygon
.contains_triangle(triangle.map(|point| point.point_surface))
});
let color = self.color.unwrap_or_default();
for triangle in triangles {
let points = triangle.map(|point| point.point_global);
mesh.push_triangle(points, color);
}
}
}
#[cfg(test)]
mod tests {
use fj_interop::Mesh;
use fj_math::{Point, Scalar};
use crate::{
algorithms::approx::{Approx, Tolerance},
objects::{Cycle, Face},
operations::{
build::{BuildCycle, BuildFace},
update::{UpdateFace, UpdateRegion},
},
Core,
};
use super::Triangulate;
#[test]
fn simple() -> anyhow::Result<()> {
let mut core = Core::new();
let a = [0., 0.];
let b = [2., 0.];
let c = [2., 2.];
let d = [0., 1.];
let face =
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
.update_region(
|region, core| {
region.update_exterior(
|_, core| Cycle::polygon([a, b, c, d], core),
core,
)
},
&mut core,
);
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, &mut core)?;
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 mut core = Core::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 = core.layers.objects.surfaces.xy_plane();
let face = Face::unbound(surface.clone(), &mut core).update_region(
|region, core| {
region
.update_exterior(
|_, core| Cycle::polygon([a, b, c, d], core),
core,
)
.add_interiors([Cycle::polygon([e, f, g, h], core)], core)
},
&mut core,
);
let triangles = triangulate(face, &mut core)?;
let a = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(a);
let b = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(b);
let e = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(e);
let f = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(f);
let g = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(g);
let h = core
.layers
.geometry
.of_surface(&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(())
}
#[test]
fn sharp_concave_shape() -> anyhow::Result<()> {
let mut core = Core::new();
let a = [1., 0.];
let b = [2., 8.];
let c = [2., 9.];
let d = [1., 1.];
let e = [0., 9.];
let surface = core.layers.objects.surfaces.xy_plane();
let face = Face::unbound(surface.clone(), &mut core).update_region(
|region, core| {
region.update_exterior(
|_, core| Cycle::polygon([a, b, c, d, e], core),
core,
)
},
&mut core,
);
let triangles = triangulate(face, &mut core)?;
let a = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(a);
let b = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(b);
let c = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(c);
let d = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(d);
let e = core
.layers
.geometry
.of_surface(&surface)
.point_from_surface_coords(e);
assert!(triangles.contains_triangle([a, b, d]));
assert!(triangles.contains_triangle([a, d, e]));
assert!(triangles.contains_triangle([b, c, d]));
Ok(())
}
fn triangulate(
face: Face,
core: &mut Core,
) -> anyhow::Result<Mesh<Point<3>>> {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;
Ok(face.approx(tolerance, core).triangulate(core))
}
}