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