1use threecrate_core::{TriangleMesh, Result, Point3f, Vector3f};
15
16const EPSILON: f32 = 1e-5;
18
19#[derive(Debug, Clone)]
25struct Plane {
26 normal: Vector3f,
27 w: f32,
28}
29
30impl Plane {
31 fn from_points(a: &Point3f, b: &Point3f, c: &Point3f) -> Option<Self> {
32 let n = (b - a).cross(&(c - a));
33 let len = n.magnitude();
34 if len < EPSILON * EPSILON {
35 return None; }
37 let normal = n / len;
38 let w = normal.dot(&a.coords);
39 Some(Plane { normal, w })
40 }
41
42 fn flip(&mut self) {
43 self.normal = -self.normal;
44 self.w = -self.w;
45 }
46
47 #[inline]
48 fn signed_distance(&self, p: &Point3f) -> f32 {
49 self.normal.dot(&p.coords) - self.w
50 }
51
52 fn split_polygon(
55 &self,
56 poly: &Polygon,
57 coplanar_front: &mut Vec<Polygon>,
58 coplanar_back: &mut Vec<Polygon>,
59 front: &mut Vec<Polygon>,
60 back: &mut Vec<Polygon>,
61 ) {
62 let verts = &poly.vertices;
63 let n = verts.len();
64
65 let dists: Vec<f32> = verts.iter().map(|v| self.signed_distance(v)).collect();
67 let mut poly_mask: u8 = 0;
68 for &d in &dists {
69 if d > EPSILON {
70 poly_mask |= 1; } else if d < -EPSILON {
72 poly_mask |= 2; }
74 }
76
77 match poly_mask {
78 0 => {
79 if self.normal.dot(&poly.plane.normal) > 0.0 {
81 coplanar_front.push(poly.clone());
82 } else {
83 coplanar_back.push(poly.clone());
84 }
85 }
86 1 => front.push(poly.clone()),
87 2 => back.push(poly.clone()),
88 _ => {
89 let mut f_verts: Vec<Point3f> = Vec::new();
91 let mut b_verts: Vec<Point3f> = Vec::new();
92
93 for i in 0..n {
94 let j = (i + 1) % n;
95 let di = dists[i];
96 let dj = dists[j];
97 let vi = verts[i];
98 let vj = verts[j];
99
100 let i_front = di > EPSILON;
101 let i_back = di < -EPSILON;
102 let j_front = dj > EPSILON;
103 let j_back = dj < -EPSILON;
104
105 if !i_back {
106 f_verts.push(vi);
107 }
108 if !i_front {
109 b_verts.push(vi);
110 }
111
112 if (i_front && j_back) || (i_back && j_front) {
114 let t = di / (di - dj);
115 let mid = vi + (vj - vi) * t;
116 f_verts.push(mid);
117 b_verts.push(mid);
118 }
119 }
120
121 front.extend(Polygon::fan_triangulate(f_verts));
122 back.extend(Polygon::fan_triangulate(b_verts));
123 }
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
130struct Polygon {
131 vertices: Vec<Point3f>,
132 plane: Plane,
133}
134
135impl Polygon {
136 fn new(vertices: Vec<Point3f>) -> Option<Self> {
137 if vertices.len() < 3 {
138 return None;
139 }
140 let plane = Plane::from_points(&vertices[0], &vertices[1], &vertices[2])?;
141 Some(Polygon { vertices, plane })
142 }
143
144 fn flip(&mut self) {
145 self.vertices.reverse();
146 self.plane.flip();
147 }
148
149 fn fan_triangulate(verts: Vec<Point3f>) -> Vec<Polygon> {
151 if verts.len() < 3 {
152 return Vec::new();
153 }
154 let mut out = Vec::with_capacity(verts.len() - 2);
155 for i in 1..verts.len() - 1 {
156 if let Some(p) = Polygon::new(vec![verts[0], verts[i], verts[i + 1]]) {
157 out.push(p);
158 }
159 }
160 out
161 }
162}
163
164struct BspNode {
169 plane: Option<Plane>,
170 front: Option<Box<BspNode>>,
171 back: Option<Box<BspNode>>,
172 polygons: Vec<Polygon>,
173}
174
175impl BspNode {
176 fn new() -> Self {
177 BspNode { plane: None, front: None, back: None, polygons: Vec::new() }
178 }
179
180 fn invert(&mut self) {
181 for p in &mut self.polygons {
182 p.flip();
183 }
184 if let Some(ref mut plane) = self.plane {
185 plane.flip();
186 }
187 if let Some(ref mut f) = self.front {
188 f.invert();
189 }
190 if let Some(ref mut b) = self.back {
191 b.invert();
192 }
193 std::mem::swap(&mut self.front, &mut self.back);
194 }
195
196 fn clip_polygons(&self, polygons: Vec<Polygon>) -> Vec<Polygon> {
198 let plane = match &self.plane {
199 Some(p) => p,
200 None => return polygons,
201 };
202
203 let mut f: Vec<Polygon> = Vec::new();
204 let mut b: Vec<Polygon> = Vec::new();
205
206 for poly in polygons {
207 let mut cf = Vec::new();
208 let mut cb = Vec::new();
209 let mut pf = Vec::new();
210 let mut pb = Vec::new();
211 plane.split_polygon(&poly, &mut cf, &mut cb, &mut pf, &mut pb);
212 f.extend(cf);
213 f.extend(pf);
214 b.extend(cb);
215 b.extend(pb);
216 }
217
218 let mut result = match &self.front {
219 Some(node) => node.clip_polygons(f),
220 None => f,
221 };
222 let back_result = match &self.back {
223 Some(node) => node.clip_polygons(b),
224 None => Vec::new(), };
226 result.extend(back_result);
227 result
228 }
229
230 fn clip_to(&mut self, other: &BspNode) {
232 self.polygons = other.clip_polygons(std::mem::take(&mut self.polygons));
233 if let Some(ref mut f) = self.front {
234 f.clip_to(other);
235 }
236 if let Some(ref mut b) = self.back {
237 b.clip_to(other);
238 }
239 }
240
241 fn all_polygons(&self) -> Vec<Polygon> {
243 let mut out = self.polygons.clone();
244 if let Some(ref f) = self.front {
245 out.extend(f.all_polygons());
246 }
247 if let Some(ref b) = self.back {
248 out.extend(b.all_polygons());
249 }
250 out
251 }
252
253 fn build(&mut self, polygons: Vec<Polygon>) {
255 if polygons.is_empty() {
256 return;
257 }
258
259 if self.plane.is_none() {
260 self.plane = Some(polygons[0].plane.clone());
261 }
262 let plane = self.plane.clone().unwrap();
263
264 let mut front = Vec::new();
265 let mut back = Vec::new();
266
267 for poly in polygons {
268 let mut cf = Vec::new();
269 let mut cb = Vec::new();
270 let mut pf = Vec::new();
271 let mut pb = Vec::new();
272 plane.split_polygon(&poly, &mut cf, &mut cb, &mut pf, &mut pb);
273 self.polygons.extend(cf);
274 self.polygons.extend(cb);
275 front.extend(pf);
276 back.extend(pb);
277 }
278
279 if !front.is_empty() {
280 if self.front.is_none() {
281 self.front = Some(Box::new(BspNode::new()));
282 }
283 self.front.as_mut().unwrap().build(front);
284 }
285 if !back.is_empty() {
286 if self.back.is_none() {
287 self.back = Some(Box::new(BspNode::new()));
288 }
289 self.back.as_mut().unwrap().build(back);
290 }
291 }
292}
293
294fn mesh_to_polygons(mesh: &TriangleMesh) -> Vec<Polygon> {
299 mesh.faces
300 .iter()
301 .filter_map(|face| {
302 Polygon::new(vec![
303 mesh.vertices[face[0]],
304 mesh.vertices[face[1]],
305 mesh.vertices[face[2]],
306 ])
307 })
308 .collect()
309}
310
311fn polygons_to_mesh(polygons: Vec<Polygon>) -> TriangleMesh {
312 let capacity = polygons.iter().map(|p| p.vertices.len().saturating_sub(2)).sum::<usize>();
315 let mut vertices = Vec::with_capacity(capacity * 3);
316 let mut faces = Vec::with_capacity(capacity);
317
318 for poly in &polygons {
319 let n = poly.vertices.len();
320 if n < 3 {
321 continue;
322 }
323 for i in 1..n - 1 {
325 let base = vertices.len();
326 vertices.push(poly.vertices[0]);
327 vertices.push(poly.vertices[i]);
328 vertices.push(poly.vertices[i + 1]);
329 faces.push([base, base + 1, base + 2]);
330 }
331 }
332
333 TriangleMesh::from_vertices_and_faces(vertices, faces)
334}
335
336fn build_bsp(mesh: &TriangleMesh) -> BspNode {
337 let mut node = BspNode::new();
338 node.build(mesh_to_polygons(mesh));
339 node
340}
341
342#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub enum BooleanOp {
349 Union,
351 Intersection,
353 Difference,
355}
356
357pub fn mesh_boolean(
367 a: &TriangleMesh,
368 b: &TriangleMesh,
369 op: BooleanOp,
370) -> Result<TriangleMesh> {
371 match op {
372 BooleanOp::Union => mesh_union(a, b),
373 BooleanOp::Intersection => mesh_intersection(a, b),
374 BooleanOp::Difference => mesh_difference(a, b),
375 }
376}
377
378pub fn mesh_union(a: &TriangleMesh, b: &TriangleMesh) -> Result<TriangleMesh> {
395 if a.is_empty() {
396 return Ok(b.clone());
397 }
398 if b.is_empty() {
399 return Ok(a.clone());
400 }
401
402 let mut a_bsp = build_bsp(a);
403 let mut b_bsp = build_bsp(b);
404
405 a_bsp.clip_to(&b_bsp);
406 b_bsp.clip_to(&a_bsp);
407 b_bsp.invert();
408 b_bsp.clip_to(&a_bsp);
409 b_bsp.invert();
410 a_bsp.build(b_bsp.all_polygons());
411
412 Ok(polygons_to_mesh(a_bsp.all_polygons()))
413}
414
415pub fn mesh_intersection(a: &TriangleMesh, b: &TriangleMesh) -> Result<TriangleMesh> {
432 if a.is_empty() || b.is_empty() {
433 return Ok(TriangleMesh::new());
434 }
435
436 let mut a_bsp = build_bsp(a);
437 let mut b_bsp = build_bsp(b);
438
439 a_bsp.invert();
440 b_bsp.clip_to(&a_bsp);
441 b_bsp.invert();
442 a_bsp.clip_to(&b_bsp);
443 b_bsp.clip_to(&a_bsp);
444 a_bsp.build(b_bsp.all_polygons());
445 a_bsp.invert();
446
447 Ok(polygons_to_mesh(a_bsp.all_polygons()))
448}
449
450pub fn mesh_difference(a: &TriangleMesh, b: &TriangleMesh) -> Result<TriangleMesh> {
467 if a.is_empty() {
468 return Ok(TriangleMesh::new());
469 }
470 if b.is_empty() {
471 return Ok(a.clone());
472 }
473
474 let mut a_bsp = build_bsp(a);
475 let mut b_bsp = build_bsp(b);
476
477 a_bsp.invert();
478 a_bsp.clip_to(&b_bsp);
479 b_bsp.clip_to(&a_bsp);
480 b_bsp.invert();
481 b_bsp.clip_to(&a_bsp);
482 b_bsp.invert();
483 a_bsp.build(b_bsp.all_polygons());
484 a_bsp.invert();
485
486 Ok(polygons_to_mesh(a_bsp.all_polygons()))
487}
488
489#[cfg(test)]
494mod tests {
495 use super::*;
496
497 fn make_box(min: Point3f, max: Point3f) -> TriangleMesh {
500 let (x0, y0, z0) = (min.x, min.y, min.z);
501 let (x1, y1, z1) = (max.x, max.y, max.z);
502 let verts = vec![
503 Point3f::new(x0, y0, z0), Point3f::new(x1, y0, z0), Point3f::new(x1, y1, z0), Point3f::new(x0, y1, z0), Point3f::new(x0, y0, z1), Point3f::new(x1, y0, z1), Point3f::new(x1, y1, z1), Point3f::new(x0, y1, z1), ];
512 let faces = vec![
514 [0, 2, 1], [0, 3, 2],
516 [4, 5, 6], [4, 6, 7],
518 [0, 1, 5], [0, 5, 4],
520 [3, 7, 6], [3, 6, 2],
522 [0, 4, 7], [0, 7, 3],
524 [1, 2, 6], [1, 6, 5],
526 ];
527 TriangleMesh::from_vertices_and_faces(verts, faces)
528 }
529
530 #[test]
533 fn test_union_empty_a() {
534 let empty = TriangleMesh::new();
535 let b = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
536 let result = mesh_union(&empty, &b).unwrap();
537 assert!(!result.is_empty());
538 }
539
540 #[test]
541 fn test_union_empty_b() {
542 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
543 let empty = TriangleMesh::new();
544 let result = mesh_union(&a, &empty).unwrap();
545 assert!(!result.is_empty());
546 }
547
548 #[test]
549 fn test_intersection_empty() {
550 let empty = TriangleMesh::new();
551 let b = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
552 let result = mesh_intersection(&empty, &b).unwrap();
553 assert!(result.is_empty());
554 }
555
556 #[test]
557 fn test_difference_empty_a() {
558 let empty = TriangleMesh::new();
559 let b = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
560 let result = mesh_difference(&empty, &b).unwrap();
561 assert!(result.is_empty());
562 }
563
564 #[test]
565 fn test_difference_empty_b() {
566 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
567 let empty = TriangleMesh::new();
568 let result = mesh_difference(&a, &empty).unwrap();
569 assert!(!result.is_empty());
570 }
571
572 #[test]
575 fn test_union_non_overlapping() {
576 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
578 let b = make_box(Point3f::new(5.0, 0.0, 0.0), Point3f::new(6.0, 1.0, 1.0));
579 let result = mesh_union(&a, &b).unwrap();
580 assert!(!result.is_empty());
582 assert!(result.face_count() >= 24, "expected ≥24 faces, got {}", result.face_count());
583 }
584
585 #[test]
586 fn test_intersection_non_overlapping() {
587 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
588 let b = make_box(Point3f::new(5.0, 0.0, 0.0), Point3f::new(6.0, 1.0, 1.0));
589 let result = mesh_intersection(&a, &b).unwrap();
590 assert!(result.is_empty(), "non-overlapping intersection should be empty, got {} faces", result.face_count());
592 }
593
594 #[test]
595 fn test_difference_non_overlapping() {
596 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(1.0, 1.0, 1.0));
597 let b = make_box(Point3f::new(5.0, 0.0, 0.0), Point3f::new(6.0, 1.0, 1.0));
598 let result = mesh_difference(&a, &b).unwrap();
599 assert!(!result.is_empty());
601 assert!(result.face_count() >= 12, "expected ≥12 faces, got {}", result.face_count());
602 }
603
604 #[test]
607 fn test_union_overlapping() {
608 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(2.0, 2.0, 2.0));
610 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(3.0, 3.0, 3.0));
611 let result = mesh_union(&a, &b).unwrap();
612 assert!(!result.is_empty(), "union of overlapping boxes must not be empty");
613 }
614
615 #[test]
616 fn test_intersection_overlapping() {
617 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(2.0, 2.0, 2.0));
618 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(3.0, 3.0, 3.0));
619 let result = mesh_intersection(&a, &b).unwrap();
620 assert!(!result.is_empty(), "intersection of overlapping boxes must not be empty");
621 assert!(result.face_count() <= a.face_count() + b.face_count() + 50);
623 }
624
625 #[test]
626 fn test_difference_overlapping() {
627 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(2.0, 2.0, 2.0));
628 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(3.0, 3.0, 3.0));
629 let result = mesh_difference(&a, &b).unwrap();
630 assert!(!result.is_empty(), "A minus partially-overlapping B must not be empty");
631 }
632
633 #[test]
634 fn test_difference_a_fully_contains_b() {
635 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(4.0, 4.0, 4.0));
637 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(2.0, 2.0, 2.0));
638 let result = mesh_difference(&a, &b).unwrap();
639 assert!(!result.is_empty(), "difference with interior hole must not be empty");
640 }
641
642 #[test]
643 fn test_intersection_a_fully_contains_b() {
644 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(4.0, 4.0, 4.0));
646 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(2.0, 2.0, 2.0));
647 let result = mesh_intersection(&a, &b).unwrap();
648 assert!(!result.is_empty(), "intersection when B inside A must not be empty");
649 }
650
651 #[test]
654 fn test_mesh_boolean_dispatch() {
655 let a = make_box(Point3f::new(0.0, 0.0, 0.0), Point3f::new(2.0, 2.0, 2.0));
656 let b = make_box(Point3f::new(1.0, 1.0, 1.0), Point3f::new(3.0, 3.0, 3.0));
657
658 let u = mesh_boolean(&a, &b, BooleanOp::Union).unwrap();
659 let i = mesh_boolean(&a, &b, BooleanOp::Intersection).unwrap();
660 let d = mesh_boolean(&a, &b, BooleanOp::Difference).unwrap();
661
662 assert!(!u.is_empty());
663 assert!(!i.is_empty());
664 assert!(!d.is_empty());
665 }
666}