fj_core/algorithms/triangulate/
mod.rs1mod delaunay;
4mod polygon;
5
6use fj_interop::Mesh;
7use fj_math::Point;
8
9use crate::Core;
10
11use self::polygon::Polygon;
12
13use super::approx::{face::FaceApprox, Approx, Tolerance};
14
15pub trait Triangulate: Sized {
17 fn triangulate(self, core: &mut Core) -> Mesh<Point<3>> {
19 let mut mesh = Mesh::new();
20 self.triangulate_into_mesh(&mut mesh, core);
21 mesh
22 }
23
24 fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>, core: &mut Core);
29}
30
31impl<T> Triangulate for (T, Tolerance)
32where
33 T: Approx,
34 T::Approximation: IntoIterator<Item = FaceApprox>,
35{
36 fn triangulate_into_mesh(self, mesh: &mut Mesh<Point<3>>, core: &mut Core) {
37 let (approx, tolerance) = self;
38
39 let approx = approx.approx(tolerance, core);
40
41 for approx in approx {
42 approx.triangulate_into_mesh(mesh, core);
43 }
44 }
45}
46
47impl Triangulate for FaceApprox {
48 fn triangulate_into_mesh(
49 self,
50 mesh: &mut Mesh<Point<3>>,
51 _core: &mut Core,
52 ) {
53 let face_as_polygon = Polygon::new()
54 .with_exterior(
55 self.exterior
56 .points()
57 .into_iter()
58 .map(|point| point.local_form),
59 )
60 .with_interiors(self.interiors.iter().map(|interior| {
61 interior.points().into_iter().map(|point| point.local_form)
62 }));
63
64 let cycles = [self.exterior].into_iter().chain(self.interiors);
65 let mut triangles =
66 delaunay::triangulate(cycles, self.coord_handedness);
67 triangles.retain(|triangle| {
68 face_as_polygon
69 .contains_triangle(triangle.map(|point| point.point_surface))
70 });
71
72 let color = self.color.unwrap_or_default();
73
74 for triangle in triangles {
75 let points = triangle.map(|point| point.point_global);
76 mesh.push_triangle(points, color);
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use fj_interop::Mesh;
84 use fj_math::{Point, Scalar};
85
86 use crate::{
87 algorithms::approx::{Approx, Tolerance},
88 objects::{Cycle, Face},
89 operations::{
90 build::{BuildCycle, BuildFace},
91 update::{UpdateFace, UpdateRegion},
92 },
93 Core,
94 };
95
96 use super::Triangulate;
97
98 #[test]
99 fn simple() -> anyhow::Result<()> {
100 let mut core = Core::new();
101
102 let a = [0., 0.];
103 let b = [2., 0.];
104 let c = [2., 2.];
105 let d = [0., 1.];
106
107 let face =
108 Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
109 .update_region(
110 |region, core| {
111 region.update_exterior(
112 |_, core| Cycle::polygon([a, b, c, d], core),
113 core,
114 )
115 },
116 &mut core,
117 );
118
119 let a = Point::from(a).to_xyz();
120 let b = Point::from(b).to_xyz();
121 let c = Point::from(c).to_xyz();
122 let d = Point::from(d).to_xyz();
123
124 let triangles = triangulate(face, &mut core)?;
125
126 assert!(triangles.contains_triangle([a, b, d]));
127 assert!(triangles.contains_triangle([b, c, d]));
128 assert!(!triangles.contains_triangle([a, b, c]));
129 assert!(!triangles.contains_triangle([a, c, d]));
130
131 Ok(())
132 }
133
134 #[test]
135 fn simple_hole() -> anyhow::Result<()> {
136 let mut core = Core::new();
137
138 let a = [0., 0.];
139 let b = [4., 0.];
140 let c = [4., 4.];
141 let d = [0., 4.];
142
143 let e = [1., 1.];
144 let f = [1., 2.];
145 let g = [3., 3.];
146 let h = [3., 1.];
147
148 let surface = core.layers.objects.surfaces.xy_plane();
149
150 let face = Face::unbound(surface.clone(), &mut core).update_region(
151 |region, core| {
152 region
153 .update_exterior(
154 |_, core| Cycle::polygon([a, b, c, d], core),
155 core,
156 )
157 .add_interiors([Cycle::polygon([e, f, g, h], core)], core)
158 },
159 &mut core,
160 );
161
162 let triangles = triangulate(face, &mut core)?;
163
164 let a = core
165 .layers
166 .geometry
167 .of_surface(&surface)
168 .point_from_surface_coords(a);
169 let b = core
170 .layers
171 .geometry
172 .of_surface(&surface)
173 .point_from_surface_coords(b);
174 let e = core
175 .layers
176 .geometry
177 .of_surface(&surface)
178 .point_from_surface_coords(e);
179 let f = core
180 .layers
181 .geometry
182 .of_surface(&surface)
183 .point_from_surface_coords(f);
184 let g = core
185 .layers
186 .geometry
187 .of_surface(&surface)
188 .point_from_surface_coords(g);
189 let h = core
190 .layers
191 .geometry
192 .of_surface(&surface)
193 .point_from_surface_coords(h);
194
195 assert!(triangles.contains_triangle([a, b, e]));
205 assert!(triangles.contains_triangle([b, e, h]));
206
207 assert!(!triangles.contains_triangle([e, f, g]));
209 assert!(!triangles.contains_triangle([e, g, h]));
210 assert!(!triangles.contains_triangle([e, f, h]));
211 assert!(!triangles.contains_triangle([f, g, h]));
212
213 Ok(())
214 }
215
216 #[test]
217 fn sharp_concave_shape() -> anyhow::Result<()> {
218 let mut core = Core::new();
219
220 let a = [1., 0.];
232 let b = [2., 8.];
233 let c = [2., 9.];
234 let d = [1., 1.];
235 let e = [0., 9.];
236
237 let surface = core.layers.objects.surfaces.xy_plane();
238
239 let face = Face::unbound(surface.clone(), &mut core).update_region(
240 |region, core| {
241 region.update_exterior(
242 |_, core| Cycle::polygon([a, b, c, d, e], core),
243 core,
244 )
245 },
246 &mut core,
247 );
248
249 let triangles = triangulate(face, &mut core)?;
250
251 let a = core
252 .layers
253 .geometry
254 .of_surface(&surface)
255 .point_from_surface_coords(a);
256 let b = core
257 .layers
258 .geometry
259 .of_surface(&surface)
260 .point_from_surface_coords(b);
261 let c = core
262 .layers
263 .geometry
264 .of_surface(&surface)
265 .point_from_surface_coords(c);
266 let d = core
267 .layers
268 .geometry
269 .of_surface(&surface)
270 .point_from_surface_coords(d);
271 let e = core
272 .layers
273 .geometry
274 .of_surface(&surface)
275 .point_from_surface_coords(e);
276
277 assert!(triangles.contains_triangle([a, b, d]));
278 assert!(triangles.contains_triangle([a, d, e]));
279 assert!(triangles.contains_triangle([b, c, d]));
280
281 Ok(())
282 }
283
284 fn triangulate(
285 face: Face,
286 core: &mut Core,
287 ) -> anyhow::Result<Mesh<Point<3>>> {
288 let tolerance = Tolerance::from_scalar(Scalar::ONE)?;
289 Ok(face.approx(tolerance, core).triangulate(core))
290 }
291}