1use super::functions::*;
6use crate::error::{Error, Result};
7use crate::triangle_mesh::TriangleMesh;
8use oxiphysics_core::math::Vec3;
9
10pub struct BspNode {
15 pub plane: BspPlane,
17 pub polygons: Vec<Vec<[f64; 3]>>,
19 pub front: Option<Box<BspNode>>,
21 pub back: Option<Box<BspNode>>,
23}
24impl BspNode {
25 pub fn new(plane: BspPlane) -> Self {
27 Self {
28 plane,
29 polygons: Vec::new(),
30 front: None,
31 back: None,
32 }
33 }
34 pub fn classify_point(&self, p: [f64; 3]) -> PlaneClass {
36 let d = self.plane.signed_dist(p);
37 if d > 1e-8 {
38 PlaneClass::Front
39 } else if d < -1e-8 {
40 PlaneClass::Back
41 } else {
42 PlaneClass::OnPlane
43 }
44 }
45 pub fn insert_polygon(&mut self, polygon: Vec<[f64; 3]>) {
50 if polygon.is_empty() {
51 return;
52 }
53 let classes: Vec<PlaneClass> = polygon.iter().map(|&p| self.classify_point(p)).collect();
54 let all_front = classes
55 .iter()
56 .all(|&c| c == PlaneClass::Front || c == PlaneClass::OnPlane);
57 let all_back = classes
58 .iter()
59 .all(|&c| c == PlaneClass::Back || c == PlaneClass::OnPlane);
60 if all_front && all_back {
61 self.polygons.push(polygon);
62 } else if all_front {
63 match &mut self.front {
64 Some(f) => f.insert_polygon(polygon),
65 None => {
66 let mut node = Box::new(BspNode::new(self.plane));
67 node.polygons.push(polygon);
68 self.front = Some(node);
69 }
70 }
71 } else if all_back {
72 match &mut self.back {
73 Some(b) => b.insert_polygon(polygon),
74 None => {
75 let mut node = Box::new(BspNode::new(self.plane));
76 node.polygons.push(polygon);
77 self.back = Some(node);
78 }
79 }
80 } else {
81 let (front_poly, back_poly) = split_polygon_by_plane(&polygon, &self.plane);
82 if !front_poly.is_empty() {
83 match &mut self.front {
84 Some(f) => f.insert_polygon(front_poly),
85 None => {
86 let mut node = Box::new(BspNode::new(self.plane));
87 node.polygons.push(front_poly);
88 self.front = Some(node);
89 }
90 }
91 }
92 if !back_poly.is_empty() {
93 match &mut self.back {
94 Some(b) => b.insert_polygon(back_poly),
95 None => {
96 let mut node = Box::new(BspNode::new(self.plane));
97 node.polygons.push(back_poly);
98 self.back = Some(node);
99 }
100 }
101 }
102 }
103 }
104 pub fn collect_front_polygons(&self) -> Vec<Vec<[f64; 3]>> {
106 let mut result = self.polygons.clone();
107 if let Some(f) = &self.front {
108 result.extend(f.collect_front_polygons());
109 }
110 result
111 }
112 pub fn collect_back_polygons(&self) -> Vec<Vec<[f64; 3]>> {
114 let mut result = self.polygons.clone();
115 if let Some(b) = &self.back {
116 result.extend(b.collect_back_polygons());
117 }
118 result
119 }
120 pub fn count_polygons(&self) -> usize {
122 let mut count = self.polygons.len();
123 if let Some(f) = &self.front {
124 count += f.count_polygons();
125 }
126 if let Some(b) = &self.back {
127 count += b.count_polygons();
128 }
129 count
130 }
131}
132pub struct CsgWithMaterials;
135impl CsgWithMaterials {
136 pub fn execute(
140 op: BooleanOp,
141 mesh_a: &TriangleMesh,
142 mat_a: CsgMaterial,
143 mesh_b: &TriangleMesh,
144 mat_b: CsgMaterial,
145 ) -> Result<(TriangleMesh, Vec<CsgMaterial>)> {
146 if mesh_a.vertices.is_empty() || mesh_b.vertices.is_empty() {
147 return Err(Error::General("Input mesh must not be empty".into()));
148 }
149 let verts_a: Vec<[f64; 3]> = mesh_a.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
150 let verts_b: Vec<[f64; 3]> = mesh_b.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
151 let mut result_verts: Vec<[f64; 3]> = Vec::new();
152 let mut result_tris: Vec<[usize; 3]> = Vec::new();
153 let mut result_mats: Vec<CsgMaterial> = Vec::new();
154 let collect_with_mat = |verts: &[[f64; 3]],
155 tris: &[[usize; 3]],
156 other_verts: &[[f64; 3]],
157 other_tris: &[[usize; 3]],
158 inside: bool,
159 flip: bool,
160 mat: &CsgMaterial,
161 rv: &mut Vec<[f64; 3]>,
162 rt: &mut Vec<[usize; 3]>,
163 rm: &mut Vec<CsgMaterial>| {
164 for tri in tris {
165 let centroid = triangle_centroid(verts[tri[0]], verts[tri[1]], verts[tri[2]]);
166 let is_inside = point_inside_mesh(centroid, other_verts, other_tris);
167 if is_inside == inside {
168 let base = rv.len();
169 rv.push(verts[tri[0]]);
170 rv.push(verts[tri[1]]);
171 rv.push(verts[tri[2]]);
172 if flip {
173 rt.push([base, base + 2, base + 1]);
174 } else {
175 rt.push([base, base + 1, base + 2]);
176 }
177 rm.push(mat.clone());
178 }
179 }
180 };
181 match op {
182 BooleanOp::Union => {
183 collect_with_mat(
184 &verts_a,
185 &mesh_a.indices,
186 &verts_b,
187 &mesh_b.indices,
188 false,
189 false,
190 &mat_a,
191 &mut result_verts,
192 &mut result_tris,
193 &mut result_mats,
194 );
195 collect_with_mat(
196 &verts_b,
197 &mesh_b.indices,
198 &verts_a,
199 &mesh_a.indices,
200 false,
201 false,
202 &mat_b,
203 &mut result_verts,
204 &mut result_tris,
205 &mut result_mats,
206 );
207 }
208 BooleanOp::Intersection => {
209 collect_with_mat(
210 &verts_a,
211 &mesh_a.indices,
212 &verts_b,
213 &mesh_b.indices,
214 true,
215 false,
216 &mat_a,
217 &mut result_verts,
218 &mut result_tris,
219 &mut result_mats,
220 );
221 collect_with_mat(
222 &verts_b,
223 &mesh_b.indices,
224 &verts_a,
225 &mesh_a.indices,
226 true,
227 false,
228 &mat_b,
229 &mut result_verts,
230 &mut result_tris,
231 &mut result_mats,
232 );
233 }
234 BooleanOp::Difference => {
235 collect_with_mat(
236 &verts_a,
237 &mesh_a.indices,
238 &verts_b,
239 &mesh_b.indices,
240 false,
241 false,
242 &mat_a,
243 &mut result_verts,
244 &mut result_tris,
245 &mut result_mats,
246 );
247 collect_with_mat(
248 &verts_b,
249 &mesh_b.indices,
250 &verts_a,
251 &mesh_a.indices,
252 true,
253 true,
254 &mat_b,
255 &mut result_verts,
256 &mut result_tris,
257 &mut result_mats,
258 );
259 }
260 }
261 let vertices: Vec<Vec3> = result_verts.iter().map(|&a| arr_to_vec3(a)).collect();
262 Ok((TriangleMesh::new(vertices, result_tris), result_mats))
263 }
264}
265#[derive(Debug, Clone)]
267pub struct ManifoldCheckResult {
268 pub is_manifold: bool,
270 pub n_boundary_edges: usize,
272 pub n_non_manifold_edges: usize,
274}
275pub struct MeshBoolean;
287impl MeshBoolean {
288 pub fn execute(
299 op: BooleanOp,
300 mesh_a: &TriangleMesh,
301 mesh_b: &TriangleMesh,
302 ) -> Result<TriangleMesh> {
303 if mesh_a.vertices.is_empty() || mesh_b.vertices.is_empty() {
304 return Err(Error::General("Input mesh must not be empty".into()));
305 }
306 let verts_a: Vec<[f64; 3]> = mesh_a.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
307 let verts_b: Vec<[f64; 3]> = mesh_b.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
308 let mut result_verts: Vec<[f64; 3]> = Vec::new();
309 let mut result_tris: Vec<[usize; 3]> = Vec::new();
310 match op {
311 BooleanOp::Union => {
312 Self::collect_outside(
313 &verts_a,
314 &mesh_a.indices,
315 &verts_b,
316 &mesh_b.indices,
317 &mut result_verts,
318 &mut result_tris,
319 );
320 let offset = result_verts.len();
321 Self::collect_outside_with_offset(
322 &verts_b,
323 &mesh_b.indices,
324 &verts_a,
325 &mesh_a.indices,
326 &mut result_verts,
327 &mut result_tris,
328 offset,
329 );
330 }
331 BooleanOp::Intersection => {
332 Self::collect_inside(
333 &verts_a,
334 &mesh_a.indices,
335 &verts_b,
336 &mesh_b.indices,
337 &mut result_verts,
338 &mut result_tris,
339 );
340 let offset = result_verts.len();
341 Self::collect_inside_with_offset(
342 &verts_b,
343 &mesh_b.indices,
344 &verts_a,
345 &mesh_a.indices,
346 &mut result_verts,
347 &mut result_tris,
348 offset,
349 );
350 }
351 BooleanOp::Difference => {
352 Self::collect_outside(
353 &verts_a,
354 &mesh_a.indices,
355 &verts_b,
356 &mesh_b.indices,
357 &mut result_verts,
358 &mut result_tris,
359 );
360 let offset = result_verts.len();
361 Self::collect_inside_flipped_with_offset(
362 &verts_b,
363 &mesh_b.indices,
364 &verts_a,
365 &mesh_a.indices,
366 &mut result_verts,
367 &mut result_tris,
368 offset,
369 );
370 }
371 }
372 if result_verts.is_empty() {
373 return Ok(TriangleMesh::new(vec![], vec![]));
374 }
375 let vertices: Vec<Vec3> = result_verts.iter().map(|&a| arr_to_vec3(a)).collect();
376 Ok(TriangleMesh::new(vertices, result_tris))
377 }
378 fn collect_outside(
379 verts: &[[f64; 3]],
380 tris: &[[usize; 3]],
381 other_verts: &[[f64; 3]],
382 other_tris: &[[usize; 3]],
383 out_verts: &mut Vec<[f64; 3]>,
384 out_tris: &mut Vec<[usize; 3]>,
385 ) {
386 for tri in tris {
387 let centroid = triangle_centroid(verts[tri[0]], verts[tri[1]], verts[tri[2]]);
388 if !point_inside_mesh(centroid, other_verts, other_tris) {
389 let base = out_verts.len();
390 out_verts.push(verts[tri[0]]);
391 out_verts.push(verts[tri[1]]);
392 out_verts.push(verts[tri[2]]);
393 out_tris.push([base, base + 1, base + 2]);
394 }
395 }
396 }
397 fn collect_outside_with_offset(
398 verts: &[[f64; 3]],
399 tris: &[[usize; 3]],
400 other_verts: &[[f64; 3]],
401 other_tris: &[[usize; 3]],
402 out_verts: &mut Vec<[f64; 3]>,
403 out_tris: &mut Vec<[usize; 3]>,
404 _offset: usize,
405 ) {
406 Self::collect_outside(verts, tris, other_verts, other_tris, out_verts, out_tris);
407 }
408 fn collect_inside(
409 verts: &[[f64; 3]],
410 tris: &[[usize; 3]],
411 other_verts: &[[f64; 3]],
412 other_tris: &[[usize; 3]],
413 out_verts: &mut Vec<[f64; 3]>,
414 out_tris: &mut Vec<[usize; 3]>,
415 ) {
416 for tri in tris {
417 let centroid = triangle_centroid(verts[tri[0]], verts[tri[1]], verts[tri[2]]);
418 if point_inside_mesh(centroid, other_verts, other_tris) {
419 let base = out_verts.len();
420 out_verts.push(verts[tri[0]]);
421 out_verts.push(verts[tri[1]]);
422 out_verts.push(verts[tri[2]]);
423 out_tris.push([base, base + 1, base + 2]);
424 }
425 }
426 }
427 fn collect_inside_with_offset(
428 verts: &[[f64; 3]],
429 tris: &[[usize; 3]],
430 other_verts: &[[f64; 3]],
431 other_tris: &[[usize; 3]],
432 out_verts: &mut Vec<[f64; 3]>,
433 out_tris: &mut Vec<[usize; 3]>,
434 _offset: usize,
435 ) {
436 Self::collect_inside(verts, tris, other_verts, other_tris, out_verts, out_tris);
437 }
438 fn collect_inside_flipped_with_offset(
439 verts: &[[f64; 3]],
440 tris: &[[usize; 3]],
441 other_verts: &[[f64; 3]],
442 other_tris: &[[usize; 3]],
443 out_verts: &mut Vec<[f64; 3]>,
444 out_tris: &mut Vec<[usize; 3]>,
445 _offset: usize,
446 ) {
447 for tri in tris {
448 let centroid = triangle_centroid(verts[tri[0]], verts[tri[1]], verts[tri[2]]);
449 if point_inside_mesh(centroid, other_verts, other_tris) {
450 let base = out_verts.len();
451 out_verts.push(verts[tri[0]]);
452 out_verts.push(verts[tri[1]]);
453 out_verts.push(verts[tri[2]]);
454 out_tris.push([base, base + 2, base + 1]);
455 }
456 }
457 }
458}
459pub struct HalfEdgeMesh {
461 pub vertices: Vec<[f64; 3]>,
463 pub half_edges: Vec<HalfEdge>,
465 pub face_starts: Vec<usize>,
467}
468impl Default for HalfEdgeMesh {
469 fn default() -> Self {
470 Self::new()
471 }
472}
473impl HalfEdgeMesh {
474 pub fn from_triangle_mesh(verts: &[[f64; 3]], tris: &[[usize; 3]]) -> Self {
476 let mut half_edges: Vec<HalfEdge> = Vec::new();
477 let mut face_starts: Vec<usize> = Vec::with_capacity(tris.len());
478 for (fi, tri) in tris.iter().enumerate() {
479 let base = half_edges.len();
480 face_starts.push(base);
481 for k in 0..3 {
482 half_edges.push(HalfEdge {
483 vertex: tri[(k + 1) % 3],
484 face: fi,
485 next: base + (k + 1) % 3,
486 twin: usize::MAX,
487 });
488 }
489 }
490 let mut edge_map: std::collections::HashMap<(usize, usize), usize> =
491 std::collections::HashMap::new();
492 for (hi, he) in half_edges.iter().enumerate() {
493 let base = face_starts[he.face];
494 let local = hi - base;
495 let prev_local = (local + 2) % 3;
496 let from_v = tris[he.face][prev_local];
497 let to_v = he.vertex;
498 edge_map.entry((from_v, to_v)).or_insert(hi);
499 }
500 let edge_map_clone = edge_map.clone();
501 for (&(from, to), &hi) in &edge_map_clone {
502 if let Some(&twin_hi) = edge_map.get(&(to, from)) {
503 half_edges[hi].twin = twin_hi;
504 half_edges[twin_hi].twin = hi;
505 }
506 }
507 HalfEdgeMesh {
508 vertices: verts.to_vec(),
509 half_edges,
510 face_starts,
511 }
512 }
513 pub fn n_faces(&self) -> usize {
515 self.face_starts.len()
516 }
517 pub fn n_vertices(&self) -> usize {
519 self.vertices.len()
520 }
521 pub fn face_vertices(&self, fi: usize) -> [usize; 3] {
523 let base = self.face_starts[fi];
524 let he0 = &self.half_edges[base];
525 let he1 = &self.half_edges[he0.next];
526 let he2 = &self.half_edges[he1.next];
527 [he2.vertex, he0.vertex, he1.vertex]
528 }
529 pub fn face_area(&self, fi: usize) -> f64 {
531 let [v0, v1, v2] = self.face_vertices(fi);
532 let a = self.vertices[v0];
533 let b = self.vertices[v1];
534 let c = self.vertices[v2];
535 let n = cross3(sub3(b, a), sub3(c, a));
536 len3(n) * 0.5
537 }
538 pub fn boundary_half_edges(&self) -> Vec<usize> {
540 self.half_edges
541 .iter()
542 .enumerate()
543 .filter(|(_, he)| he.twin == usize::MAX)
544 .map(|(i, _)| i)
545 .collect()
546 }
547 pub fn is_manifold(&self) -> bool {
549 self.boundary_half_edges().is_empty()
550 }
551 pub fn new() -> Self {
553 Self {
554 vertices: Vec::new(),
555 half_edges: Vec::new(),
556 face_starts: Vec::new(),
557 }
558 }
559 pub fn add_vertex(&mut self, pos: [f64; 3]) -> usize {
561 let idx = self.vertices.len();
562 self.vertices.push(pos);
563 idx
564 }
565 pub fn add_triangle(&mut self, v0: usize, v1: usize, v2: usize) -> usize {
567 let fi = self.face_starts.len();
568 let base = self.half_edges.len();
569 self.face_starts.push(base);
570 self.half_edges.push(HalfEdge {
571 vertex: v1,
572 face: fi,
573 next: base + 1,
574 twin: usize::MAX,
575 });
576 self.half_edges.push(HalfEdge {
577 vertex: v2,
578 face: fi,
579 next: base + 2,
580 twin: usize::MAX,
581 });
582 self.half_edges.push(HalfEdge {
583 vertex: v0,
584 face: fi,
585 next: base,
586 twin: usize::MAX,
587 });
588 fi
589 }
590 pub fn build_twin_links(&mut self) {
592 for he in self.half_edges.iter_mut() {
594 he.twin = usize::MAX;
595 }
596 let mut edge_map: std::collections::HashMap<(usize, usize), usize> =
597 std::collections::HashMap::new();
598 let edge_data: Vec<(usize, usize, usize)> = self
600 .half_edges
601 .iter()
602 .enumerate()
603 .map(|(hi, he)| {
604 let fi = he.face;
605 let base = self.face_starts[fi];
606 let local = hi - base;
607 let prev_local = (local + 2) % 3;
608 let from_v = self.half_edges[base + prev_local].vertex;
609 let to_v = he.vertex;
610 (hi, from_v, to_v)
611 })
612 .collect();
613 for (hi, from_v, to_v) in edge_data {
615 if let Some(&twin_hi) = edge_map.get(&(to_v, from_v)) {
616 self.half_edges[hi].twin = twin_hi;
617 self.half_edges[twin_hi].twin = hi;
618 }
619 edge_map.insert((from_v, to_v), hi);
620 }
621 }
622 pub fn num_faces(&self) -> usize {
624 self.n_faces()
625 }
626 pub fn face_count(&self) -> usize {
628 self.n_faces()
629 }
630 pub fn vertex_count(&self) -> usize {
632 self.n_vertices()
633 }
634 pub fn active_vertex_count(&self) -> usize {
636 self.n_vertices()
637 }
638 pub fn active_face_count(&self) -> usize {
640 self.n_faces()
641 }
642 pub fn face_normal(&self, fi: usize) -> [f64; 3] {
644 let [v0, v1, v2] = self.face_vertices(fi);
645 let a = self.vertices[v0];
646 let b = self.vertices[v1];
647 let c = self.vertices[v2];
648 let n = cross3(sub3(b, a), sub3(c, a));
649 normalize3(n)
650 }
651 pub fn face_centroid(&self, fi: usize) -> [f64; 3] {
653 let [v0, v1, v2] = self.face_vertices(fi);
654 let a = self.vertices[v0];
655 let b = self.vertices[v1];
656 let c = self.vertices[v2];
657 [
658 (a[0] + b[0] + c[0]) / 3.0,
659 (a[1] + b[1] + c[1]) / 3.0,
660 (a[2] + b[2] + c[2]) / 3.0,
661 ]
662 }
663 pub fn boundary_edges(&self) -> Vec<(usize, usize)> {
665 let mut result = Vec::new();
666 for (hi, he) in self.half_edges.iter().enumerate() {
667 if he.twin == usize::MAX {
668 let fi = he.face;
669 let base = self.face_starts[fi];
670 let local = hi - base;
671 let prev_local = (local + 2) % 3;
672 let from_v = self.half_edges[base + prev_local].vertex;
673 let to_v = he.vertex;
674 result.push((from_v, to_v));
675 }
676 }
677 result
678 }
679 pub fn boundary_loops(&self) -> Vec<Vec<usize>> {
681 let edges = self.boundary_edges();
682 let mut adj: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
683 for &(from, to) in &edges {
684 adj.insert(from, to);
685 }
686 let mut visited: std::collections::HashSet<usize> = std::collections::HashSet::new();
687 let mut loops = Vec::new();
688 for &(start, _) in &edges {
689 if visited.contains(&start) {
690 continue;
691 }
692 let mut loop_verts = Vec::new();
693 let mut cur = start;
694 loop {
695 if visited.contains(&cur) {
696 break;
697 }
698 visited.insert(cur);
699 loop_verts.push(cur);
700 if let Some(&next) = adj.get(&cur) {
701 cur = next;
702 } else {
703 break;
704 }
705 }
706 if !loop_verts.is_empty() {
707 loops.push(loop_verts);
708 }
709 }
710 loops
711 }
712 pub fn is_boundary_vertex(&self, v: usize) -> bool {
714 for he in &self.half_edges {
715 if he.vertex == v && he.twin == usize::MAX {
716 return true;
717 }
718 }
719 for (hi, he) in self.half_edges.iter().enumerate() {
721 if he.twin == usize::MAX {
722 let fi = he.face;
723 let base = self.face_starts[fi];
724 let local = hi - base;
725 let prev_local = (local + 2) % 3;
726 if self.half_edges[base + prev_local].vertex == v {
727 return true;
728 }
729 }
730 }
731 false
732 }
733 pub fn vertex_neighbors(&self, v: usize) -> Vec<usize> {
735 let mut neighbors = std::collections::HashSet::new();
736 for he in &self.half_edges {
737 let fi = he.face;
738 let base = self.face_starts[fi];
739 let local_idx = (0..3).find(|&k| {
740 let idx = base + k;
741 idx < self.half_edges.len() && self.half_edges[idx].vertex == v
742 });
743 if local_idx.is_some() {
744 let fv = self.face_vertices(fi);
746 for &fvi in &fv {
747 if fvi != v {
748 neighbors.insert(fvi);
749 }
750 }
751 }
752 }
753 neighbors.into_iter().collect()
754 }
755 pub fn extract_triangles(&self) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
757 let verts = self.vertices.clone();
758 let tris: Vec<[usize; 3]> = (0..self.n_faces())
759 .map(|fi| self.face_vertices(fi))
760 .collect();
761 (verts, tris)
762 }
763}
764#[derive(Debug, Clone, Copy, PartialEq, Eq)]
766pub enum PlaneClass {
767 Front,
769 Back,
771 OnPlane,
773}
774#[derive(Debug, Clone)]
776pub struct CsgMaterial {
777 pub id: u32,
779 pub name: String,
781 pub density: f64,
783}
784#[derive(Debug, Clone, Copy, PartialEq, Eq)]
786pub enum BooleanOp {
787 Union,
789 Intersection,
791 Difference,
793}
794#[derive(Debug, Clone, Copy)]
796pub struct BspPlane {
797 pub normal: [f64; 3],
799 pub offset: f64,
801}
802impl BspPlane {
803 pub fn new(normal: [f64; 3], offset: f64) -> Self {
805 Self {
806 normal: normalize3(normal),
807 offset,
808 }
809 }
810 #[inline]
812 pub fn signed_dist(&self, p: [f64; 3]) -> f64 {
813 dot3(self.normal, p) - self.offset
814 }
815 pub fn classify(&self, p: [f64; 3]) -> f64 {
817 self.signed_dist(p)
818 }
819}
820#[derive(Debug, Clone)]
824pub struct HalfEdge {
825 pub vertex: usize,
827 pub face: usize,
829 pub next: usize,
831 pub twin: usize,
833}
834pub struct RobustMeshBoolean;
840impl RobustMeshBoolean {
841 pub fn execute(
843 op: BooleanOp,
844 mesh_a: &TriangleMesh,
845 mesh_b: &TriangleMesh,
846 epsilon: f64,
847 ) -> Result<TriangleMesh> {
848 if mesh_a.vertices.is_empty() || mesh_b.vertices.is_empty() {
849 return Err(Error::General("Input mesh must not be empty".into()));
850 }
851 let verts_a: Vec<[f64; 3]> = mesh_a.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
852 let verts_b: Vec<[f64; 3]> = mesh_b.vertices.iter().map(|v| vec3_to_arr(*v)).collect();
853 let mut result_verts: Vec<[f64; 3]> = Vec::new();
854 let mut result_tris: Vec<[usize; 3]> = Vec::new();
855 let robust_inside = |point: [f64; 3], verts: &[[f64; 3]], tris: &[[usize; 3]]| -> bool {
856 Self::robust_point_inside(point, verts, tris, epsilon)
857 };
858 match op {
859 BooleanOp::Union => {
860 for tri in &mesh_a.indices {
861 let c = triangle_centroid(verts_a[tri[0]], verts_a[tri[1]], verts_a[tri[2]]);
862 if !robust_inside(c, &verts_b, &mesh_b.indices) {
863 let base = result_verts.len();
864 result_verts.push(verts_a[tri[0]]);
865 result_verts.push(verts_a[tri[1]]);
866 result_verts.push(verts_a[tri[2]]);
867 result_tris.push([base, base + 1, base + 2]);
868 }
869 }
870 for tri in &mesh_b.indices {
871 let c = triangle_centroid(verts_b[tri[0]], verts_b[tri[1]], verts_b[tri[2]]);
872 if !robust_inside(c, &verts_a, &mesh_a.indices) {
873 let base = result_verts.len();
874 result_verts.push(verts_b[tri[0]]);
875 result_verts.push(verts_b[tri[1]]);
876 result_verts.push(verts_b[tri[2]]);
877 result_tris.push([base, base + 1, base + 2]);
878 }
879 }
880 }
881 BooleanOp::Intersection => {
882 for tri in &mesh_a.indices {
883 let c = triangle_centroid(verts_a[tri[0]], verts_a[tri[1]], verts_a[tri[2]]);
884 if robust_inside(c, &verts_b, &mesh_b.indices) {
885 let base = result_verts.len();
886 result_verts.push(verts_a[tri[0]]);
887 result_verts.push(verts_a[tri[1]]);
888 result_verts.push(verts_a[tri[2]]);
889 result_tris.push([base, base + 1, base + 2]);
890 }
891 }
892 for tri in &mesh_b.indices {
893 let c = triangle_centroid(verts_b[tri[0]], verts_b[tri[1]], verts_b[tri[2]]);
894 if robust_inside(c, &verts_a, &mesh_a.indices) {
895 let base = result_verts.len();
896 result_verts.push(verts_b[tri[0]]);
897 result_verts.push(verts_b[tri[1]]);
898 result_verts.push(verts_b[tri[2]]);
899 result_tris.push([base, base + 1, base + 2]);
900 }
901 }
902 }
903 BooleanOp::Difference => {
904 for tri in &mesh_a.indices {
905 let c = triangle_centroid(verts_a[tri[0]], verts_a[tri[1]], verts_a[tri[2]]);
906 if !robust_inside(c, &verts_b, &mesh_b.indices) {
907 let base = result_verts.len();
908 result_verts.push(verts_a[tri[0]]);
909 result_verts.push(verts_a[tri[1]]);
910 result_verts.push(verts_a[tri[2]]);
911 result_tris.push([base, base + 1, base + 2]);
912 }
913 }
914 for tri in &mesh_b.indices {
915 let c = triangle_centroid(verts_b[tri[0]], verts_b[tri[1]], verts_b[tri[2]]);
916 if robust_inside(c, &verts_a, &mesh_a.indices) {
917 let base = result_verts.len();
918 result_verts.push(verts_b[tri[0]]);
919 result_verts.push(verts_b[tri[1]]);
920 result_verts.push(verts_b[tri[2]]);
921 result_tris.push([base, base + 2, base + 1]);
922 }
923 }
924 }
925 }
926 let vertices: Vec<Vec3> = result_verts.iter().map(|&a| arr_to_vec3(a)).collect();
927 Ok(TriangleMesh::new(vertices, result_tris))
928 }
929 fn robust_point_inside(
934 point: [f64; 3],
935 verts: &[[f64; 3]],
936 tris: &[[usize; 3]],
937 epsilon: f64,
938 ) -> bool {
939 for tri in tris {
940 let v0 = verts[tri[0]];
941 let v1 = verts[tri[1]];
942 let v2 = verts[tri[2]];
943 let normal = triangle_normal(v0, v1, v2);
944 let len = len3(normal);
945 if len < 1e-14 {
946 continue;
947 }
948 let n = scale3(normal, 1.0 / len);
949 let dist = (dot3(n, point) - dot3(n, v0)).abs();
950 if dist < epsilon {
951 return false;
952 }
953 }
954 point_inside_mesh(point, verts, tris)
955 }
956}
957pub struct ManifoldBoolean;
962impl ManifoldBoolean {
963 pub fn execute(
965 op: BooleanOp,
966 mesh_a: &TriangleMesh,
967 mesh_b: &TriangleMesh,
968 ) -> Result<TriangleMesh> {
969 if mesh_a.vertices.is_empty() || mesh_b.vertices.is_empty() {
970 return Err(Error::General("Input mesh must not be empty".into()));
971 }
972 let _check_a = check_manifold(&mesh_a.indices);
973
974 let check_b = check_manifold(&mesh_b.indices);
975 let _ = check_b;
976 MeshBoolean::execute(op, mesh_a, mesh_b)
977 }
978 pub fn execute_strict(
980 op: BooleanOp,
981 mesh_a: &TriangleMesh,
982 mesh_b: &TriangleMesh,
983 ) -> Result<TriangleMesh> {
984 if mesh_a.vertices.is_empty() || mesh_b.vertices.is_empty() {
985 return Err(Error::General("Input mesh must not be empty".into()));
986 }
987 let check_a = check_manifold(&mesh_a.indices);
988 if !check_a.is_manifold {
989 return Err(Error::General(format!(
990 "mesh_a is non-manifold: {} boundary edges, {} non-manifold edges",
991 check_a.n_boundary_edges, check_a.n_non_manifold_edges
992 )));
993 }
994 let check_b = check_manifold(&mesh_b.indices);
995 if !check_b.is_manifold {
996 return Err(Error::General(format!(
997 "mesh_b is non-manifold: {} boundary edges, {} non-manifold edges",
998 check_b.n_boundary_edges, check_b.n_non_manifold_edges
999 )));
1000 }
1001 MeshBoolean::execute(op, mesh_a, mesh_b)
1002 }
1003}