1use crate::bounding_volume::Aabb;
2use crate::math::{Isometry, Point, Real, UnitVector, Vector};
3use crate::query::visitors::BoundingVolumeIntersectionsVisitor;
4use crate::query::{IntersectResult, PointQuery, SplitResult};
5use crate::shape::{Cuboid, FeatureId, Polyline, Segment, Shape, TriMesh, TriMeshFlags, Triangle};
6use crate::transformation::{intersect_meshes, MeshIntersectionError};
7use crate::utils::{hashmap::HashMap, SortedPair, WBasis};
8use alloc::{vec, vec::Vec};
9use core::cmp::Ordering;
10use spade::{handles::FixedVertexHandle, ConstrainedDelaunayTriangulation, Triangulation as _};
11
12struct Triangulation {
13 delaunay: ConstrainedDelaunayTriangulation<spade::Point2<Real>>,
14 basis: [Vector<Real>; 2],
15 basis_origin: Point<Real>,
16 spade2index: HashMap<FixedVertexHandle, u32>,
17 index2spade: HashMap<u32, FixedVertexHandle>,
18}
19
20impl Triangulation {
21 fn new(axis: UnitVector<Real>, basis_origin: Point<Real>) -> Self {
22 Triangulation {
23 delaunay: ConstrainedDelaunayTriangulation::new(),
24 basis: axis.orthonormal_basis(),
25 basis_origin,
26 spade2index: HashMap::default(),
27 index2spade: HashMap::default(),
28 }
29 }
30
31 fn project(&self, pt: Point<Real>) -> spade::Point2<Real> {
32 let dpt = pt - self.basis_origin;
33 spade::Point2::new(dpt.dot(&self.basis[0]), dpt.dot(&self.basis[1]))
34 }
35
36 fn add_edge(&mut self, id1: u32, id2: u32, points: &[Point<Real>]) {
37 let proj1 = self.project(points[id1 as usize]);
38 let proj2 = self.project(points[id2 as usize]);
39
40 let handle1 = *self.index2spade.entry(id1).or_insert_with(|| {
41 let h = self.delaunay.insert(proj1).unwrap();
42 let _ = self.spade2index.insert(h, id1);
43 h
44 });
45
46 let handle2 = *self.index2spade.entry(id2).or_insert_with(|| {
47 let h = self.delaunay.insert(proj2).unwrap();
48 let _ = self.spade2index.insert(h, id2);
49 h
50 });
51
52 if !self.delaunay.can_add_constraint(handle1, handle2) {
54 let _ = self.delaunay.add_constraint(handle1, handle2);
55 }
56 }
57}
58
59impl TriMesh {
60 pub fn canonical_split(&self, axis: usize, bias: Real, epsilon: Real) -> SplitResult<Self> {
71 self.local_split(&Vector::ith_axis(axis), bias, epsilon)
73 }
74
75 pub fn split(
78 &self,
79 position: &Isometry<Real>,
80 axis: &UnitVector<Real>,
81 bias: Real,
82 epsilon: Real,
83 ) -> SplitResult<Self> {
84 let local_axis = position.inverse_transform_unit_vector(axis);
85 let added_bias = -position.translation.vector.dot(axis);
86 self.local_split(&local_axis, bias + added_bias, epsilon)
87 }
88
89 pub fn local_split(
92 &self,
93 local_axis: &UnitVector<Real>,
94 bias: Real,
95 epsilon: Real,
96 ) -> SplitResult<Self> {
97 let mut triangulation = if self.pseudo_normals_if_oriented().is_some() {
98 Some(Triangulation::new(*local_axis, self.vertices()[0]))
99 } else {
100 None
101 };
102
103 let vertices = self.vertices();
105 let indices = self.indices();
106 let mut colors = vec![0u8; self.vertices().len()];
107
108 let mut found_negative = false;
112 let mut found_positive = false;
113 for (i, pt) in vertices.iter().enumerate() {
114 let dist_to_plane = pt.coords.dot(local_axis) - bias;
115 if dist_to_plane < -epsilon {
116 found_negative = true;
117 colors[i] = 1;
118 } else if dist_to_plane > epsilon {
119 found_positive = true;
120 colors[i] = 2;
121 }
122 }
123
124 if !found_negative {
126 return SplitResult::Positive;
127 }
128
129 if !found_positive {
130 return SplitResult::Negative;
131 }
132
133 let mut intersections_found = HashMap::default();
135 let mut new_indices = indices.to_vec();
136 let mut new_vertices = vertices.to_vec();
137
138 for (tri_id, idx) in indices.iter().enumerate() {
139 let mut intersection_features = (FeatureId::Unknown, FeatureId::Unknown);
140
141 for ia in 0..3 {
143 let ib = (ia + 1) % 3;
144 let idx_a = idx[ia as usize];
145 let idx_b = idx[ib as usize];
146
147 let fid = match (colors[idx_a as usize], colors[idx_b as usize]) {
148 (1, 2) | (2, 1) => FeatureId::Edge(ia),
149 (0, _) => FeatureId::Vertex(ia),
151 _ => continue,
152 };
153
154 if intersection_features.0 == FeatureId::Unknown {
155 intersection_features.0 = fid;
156 } else {
157 intersection_features.1 = fid;
160 }
161 }
162
163 let mut intersect_edge = |idx_a, idx_b| {
165 *intersections_found
166 .entry(SortedPair::new(idx_a, idx_b))
167 .or_insert_with(|| {
168 let segment = Segment::new(
169 new_vertices[idx_a as usize],
170 new_vertices[idx_b as usize],
171 );
172 if let Some((intersection, _)) = segment
174 .local_split_and_get_intersection(local_axis, bias, epsilon)
175 .1
176 {
177 new_vertices.push(intersection);
178 colors.push(0);
179 (new_vertices.len() - 1) as u32
180 } else {
181 unreachable!()
182 }
183 })
184 };
185
186 match intersection_features {
189 (_, FeatureId::Unknown) => {
190 assert!(
193 matches!(intersection_features.0, FeatureId::Unknown)
194 || matches!(intersection_features.0, FeatureId::Vertex(_))
195 );
196 }
197 (FeatureId::Vertex(v1), FeatureId::Vertex(v2)) => {
198 if let Some(triangulation) = &mut triangulation {
202 let id1 = idx[v1 as usize];
203 let id2 = idx[v2 as usize];
204 triangulation.add_edge(id1, id2, &new_vertices);
205 }
206 }
207 (FeatureId::Vertex(iv), FeatureId::Edge(ie))
208 | (FeatureId::Edge(ie), FeatureId::Vertex(iv)) => {
209 let ia = ie;
211 let ib = (ie + 1) % 3;
212 let ic = (ie + 2) % 3;
213 let idx_a = idx[ia as usize];
214 let idx_b = idx[ib as usize];
215 let idx_c = idx[ic as usize];
216 assert_eq!(iv, ic);
217
218 let intersection_idx = intersect_edge(idx_a, idx_b);
219
220 let new_tri_a = [idx_c, idx_a, intersection_idx];
222 let new_tri_b = [idx_b, idx_c, intersection_idx];
223
224 new_indices[tri_id] = new_tri_a;
225 new_indices.push(new_tri_b);
226
227 if let Some(triangulation) = &mut triangulation {
228 triangulation.add_edge(intersection_idx, idx_c, &new_vertices);
229 }
230 }
231 (FeatureId::Edge(mut e1), FeatureId::Edge(mut e2)) => {
232 if e2 != (e1 + 1) % 3 {
235 core::mem::swap(&mut e1, &mut e2);
236 }
237
238 let ia = e2; let ib = (e2 + 1) % 3;
240 let ic = (e2 + 2) % 3;
241 let idx_a = idx[ia as usize];
242 let idx_b = idx[ib as usize];
243 let idx_c = idx[ic as usize];
244
245 let intersection1 = intersect_edge(idx_c, idx_a);
246 let intersection2 = intersect_edge(idx_a, idx_b);
247
248 let new_tri1 = [idx_a, intersection2, intersection1];
249 let new_tri2 = [intersection2, idx_b, idx_c];
250 let new_tri3 = [intersection2, idx_c, intersection1];
251 new_indices[tri_id] = new_tri1;
252 new_indices.push(new_tri2);
253 new_indices.push(new_tri3);
254
255 if let Some(triangulation) = &mut triangulation {
256 triangulation.add_edge(intersection1, intersection2, &new_vertices);
257 }
258 }
259 _ => unreachable!(),
260 }
261 }
262
263 let mut vertices_lhs = vec![];
265 let mut vertices_rhs = vec![];
266 let mut indices_lhs = vec![];
267 let mut indices_rhs = vec![];
268 let mut remap = vec![];
269
270 for i in 0..new_vertices.len() {
271 match colors[i] {
272 0 => {
273 remap.push((vertices_lhs.len() as u32, vertices_rhs.len() as u32));
274 vertices_lhs.push(new_vertices[i]);
275 vertices_rhs.push(new_vertices[i]);
276 }
277 1 => {
278 remap.push((vertices_lhs.len() as u32, u32::MAX));
279 vertices_lhs.push(new_vertices[i]);
280 }
281 2 => {
282 remap.push((u32::MAX, vertices_rhs.len() as u32));
283 vertices_rhs.push(new_vertices[i]);
284 }
285 _ => unreachable!(),
286 }
287 }
288
289 for idx in new_indices {
290 let idx = [idx[0] as usize, idx[1] as usize, idx[2] as usize]; let colors = [colors[idx[0]], colors[idx[1]], colors[idx[2]]];
292 let remap = [remap[idx[0]], remap[idx[1]], remap[idx[2]]];
293
294 if colors[0] == 1 || colors[1] == 1 || colors[2] == 1 {
295 assert!(colors[0] != 2 && colors[1] != 2 && colors[2] != 2);
296 indices_lhs.push([remap[0].0, remap[1].0, remap[2].0]);
297 } else if colors[0] == 2 || colors[1] == 2 || colors[2] == 2 {
298 assert!(colors[0] != 1 && colors[1] != 1 && colors[2] != 1);
299 indices_rhs.push([remap[0].1, remap[1].1, remap[2].1]);
300 } else {
301 indices_lhs.push([remap[0].0, remap[1].0, remap[2].0]);
303 indices_rhs.push([remap[0].1, remap[1].1, remap[2].1]);
304 }
305 }
306
307 if let Some(triangulation) = triangulation {
309 for face in triangulation.delaunay.inner_faces() {
310 let vtx = face.vertices();
311 let mut idx1 = [0; 3];
312 let mut idx2 = [0; 3];
313 for k in 0..3 {
314 let vid = triangulation.spade2index[&vtx[k].fix()];
315 assert_eq!(colors[vid as usize], 0);
316 idx1[k] = remap[vid as usize].0;
317 idx2[k] = remap[vid as usize].1;
318 }
319
320 let tri = Triangle::new(
321 vertices_lhs[idx1[0] as usize],
322 vertices_lhs[idx1[1] as usize],
323 vertices_lhs[idx1[2] as usize],
324 );
325
326 if self.contains_local_point(&tri.center()) {
327 indices_lhs.push(idx1);
328
329 idx2.swap(1, 2); indices_rhs.push(idx2);
331 }
332 }
333 }
334
335 if indices_rhs.is_empty() {
339 SplitResult::Negative
340 } else if indices_lhs.is_empty() {
341 SplitResult::Positive
342 } else {
343 let mesh_lhs = TriMesh::new(vertices_lhs, indices_lhs).unwrap();
344 let mesh_rhs = TriMesh::new(vertices_rhs, indices_rhs).unwrap();
345 SplitResult::Pair(mesh_lhs, mesh_rhs)
346 }
347 }
348
349 pub fn canonical_intersection_with_plane(
358 &self,
359 axis: usize,
360 bias: Real,
361 epsilon: Real,
362 ) -> IntersectResult<Polyline> {
363 self.intersection_with_local_plane(&Vector::ith_axis(axis), bias, epsilon)
364 }
365
366 pub fn intersection_with_plane(
370 &self,
371 position: &Isometry<Real>,
372 axis: &UnitVector<Real>,
373 bias: Real,
374 epsilon: Real,
375 ) -> IntersectResult<Polyline> {
376 let local_axis = position.inverse_transform_unit_vector(axis);
377 let added_bias = -position.translation.vector.dot(axis);
378 self.intersection_with_local_plane(&local_axis, bias + added_bias, epsilon)
379 }
380
381 pub fn intersection_with_local_plane(
385 &self,
386 local_axis: &UnitVector<Real>,
387 bias: Real,
388 epsilon: Real,
389 ) -> IntersectResult<Polyline> {
390 let vertices = self.vertices();
392 let indices = self.indices();
393 let mut colors = vec![0u8; self.vertices().len()];
394
395 let mut found_negative = false;
399 let mut found_positive = false;
400 for (i, pt) in vertices.iter().enumerate() {
401 let dist_to_plane = pt.coords.dot(local_axis) - bias;
402 if dist_to_plane < -epsilon {
403 found_negative = true;
404 colors[i] = 1;
405 } else if dist_to_plane > epsilon {
406 found_positive = true;
407 colors[i] = 2;
408 }
409 }
410
411 if !found_negative {
413 return IntersectResult::Positive;
414 }
415
416 if !found_positive {
417 return IntersectResult::Negative;
418 }
419
420 let mut index_adjacencies: Vec<Vec<usize>> = Vec::new(); let mut add_segment_adjacencies = |idx_a: usize, idx_b| {
425 assert!(idx_a <= index_adjacencies.len());
426
427 match idx_a.cmp(&index_adjacencies.len()) {
428 Ordering::Less => index_adjacencies[idx_a].push(idx_b),
429 Ordering::Equal => index_adjacencies.push(vec![idx_b]),
430 Ordering::Greater => {}
431 }
432 };
433 let mut add_segment_adjacencies_symmetric = |idx_a: usize, idx_b| {
434 if idx_a < idx_b {
435 add_segment_adjacencies(idx_a, idx_b);
436 add_segment_adjacencies(idx_b, idx_a);
437 } else {
438 add_segment_adjacencies(idx_b, idx_a);
439 add_segment_adjacencies(idx_a, idx_b);
440 }
441 };
442
443 let mut intersections_found = HashMap::default();
444 let mut existing_vertices_found = HashMap::default();
445 let mut new_vertices = Vec::new();
446
447 for idx in indices.iter() {
448 let mut intersection_features = (FeatureId::Unknown, FeatureId::Unknown);
449
450 for ia in 0..3 {
452 let ib = (ia + 1) % 3;
453 let idx_a = idx[ia as usize];
454 let idx_b = idx[ib as usize];
455
456 let fid = match (colors[idx_a as usize], colors[idx_b as usize]) {
457 (1, 2) | (2, 1) => FeatureId::Edge(ia),
458 (0, _) => FeatureId::Vertex(ia),
460 _ => continue,
461 };
462
463 if intersection_features.0 == FeatureId::Unknown {
464 intersection_features.0 = fid;
465 } else {
466 intersection_features.1 = fid;
469 }
470 }
471
472 let mut intersect_edge = |idx_a, idx_b| {
474 *intersections_found
475 .entry(SortedPair::new(idx_a, idx_b))
476 .or_insert_with(|| {
477 let segment =
478 Segment::new(vertices[idx_a as usize], vertices[idx_b as usize]);
479 if let Some((intersection, _)) = segment
481 .local_split_and_get_intersection(local_axis, bias, epsilon)
482 .1
483 {
484 new_vertices.push(intersection);
485 colors.push(0);
486 new_vertices.len() - 1
487 } else {
488 unreachable!()
489 }
490 })
491 };
492
493 match intersection_features {
496 (_, FeatureId::Unknown) => {
497 assert!(
500 matches!(intersection_features.0, FeatureId::Unknown)
501 || matches!(intersection_features.0, FeatureId::Vertex(_))
502 );
503 }
504 (FeatureId::Vertex(iv1), FeatureId::Vertex(iv2)) => {
505 let id1 = idx[iv1 as usize];
510 let id2 = idx[iv2 as usize];
511
512 let out_id1 = *existing_vertices_found.entry(id1).or_insert_with(|| {
513 let v1 = vertices[id1 as usize];
514
515 new_vertices.push(v1);
516 new_vertices.len() - 1
517 });
518 let out_id2 = *existing_vertices_found.entry(id2).or_insert_with(|| {
519 let v2 = vertices[id2 as usize];
520
521 new_vertices.push(v2);
522 new_vertices.len() - 1
523 });
524
525 add_segment_adjacencies_symmetric(out_id1, out_id2);
526 }
527 (FeatureId::Vertex(iv), FeatureId::Edge(ie))
528 | (FeatureId::Edge(ie), FeatureId::Vertex(iv)) => {
529 let ia = ie;
531 let ib = (ie + 1) % 3;
532 let ic = (ie + 2) % 3;
533 let idx_a = idx[ia as usize];
534 let idx_b = idx[ib as usize];
535 let idx_c = idx[ic as usize];
536 assert_eq!(iv, ic);
537
538 let intersection_idx = intersect_edge(idx_a, idx_b);
539
540 let out_idx_c = *existing_vertices_found.entry(idx_c).or_insert_with(|| {
541 let v2 = vertices[idx_c as usize];
542
543 new_vertices.push(v2);
544 new_vertices.len() - 1
545 });
546
547 add_segment_adjacencies_symmetric(out_idx_c, intersection_idx);
548 }
549 (FeatureId::Edge(mut e1), FeatureId::Edge(mut e2)) => {
550 if e2 != (e1 + 1) % 3 {
553 core::mem::swap(&mut e1, &mut e2);
554 }
555
556 let ia = e2; let ib = (e2 + 1) % 3;
558 let ic = (e2 + 2) % 3;
559 let idx_a = idx[ia as usize];
560 let idx_b = idx[ib as usize];
561 let idx_c = idx[ic as usize];
562
563 let intersection1 = intersect_edge(idx_c, idx_a);
564 let intersection2 = intersect_edge(idx_a, idx_b);
565
566 add_segment_adjacencies_symmetric(intersection1, intersection2);
567 }
568 _ => unreachable!(),
569 }
570 }
571
572 let mut polyline_indices: Vec<[u32; 2]> = Vec::with_capacity(index_adjacencies.len() + 1);
574
575 let mut seen = vec![false; index_adjacencies.len()];
576 for (idx, neighbors) in index_adjacencies.iter().enumerate() {
577 if !seen[idx] {
578 let first = idx;
582 let mut prev = first;
583 let mut next = neighbors.first(); 'traversal: while let Some(current) = next {
586 seen[*current] = true;
587 polyline_indices.push([prev as u32, *current as u32]);
588
589 for neighbor in index_adjacencies[*current].iter() {
590 if *neighbor != prev && *neighbor != first {
591 prev = *current;
592 next = Some(neighbor);
593 continue 'traversal;
594 } else if *neighbor != prev && *neighbor == first {
595 polyline_indices.push([*current as u32, first as u32]);
597 next = None;
598 continue 'traversal;
599 }
600 }
601 }
602 }
603 }
604
605 IntersectResult::Intersect(Polyline::new(new_vertices, Some(polyline_indices)))
606 }
607
608 pub fn intersection_with_aabb(
610 &self,
611 position: &Isometry<Real>,
612 flip_mesh: bool,
613 aabb: &Aabb,
614 flip_cuboid: bool,
615 epsilon: Real,
616 ) -> Result<Option<Self>, MeshIntersectionError> {
617 let cuboid = Cuboid::new(aabb.half_extents());
618 let cuboid_pos = Isometry::from(aabb.center());
619 self.intersection_with_cuboid(
620 position,
621 flip_mesh,
622 &cuboid,
623 &cuboid_pos,
624 flip_cuboid,
625 epsilon,
626 )
627 }
628
629 pub fn intersection_with_cuboid(
631 &self,
632 position: &Isometry<Real>,
633 flip_mesh: bool,
634 cuboid: &Cuboid,
635 cuboid_position: &Isometry<Real>,
636 flip_cuboid: bool,
637 epsilon: Real,
638 ) -> Result<Option<Self>, MeshIntersectionError> {
639 self.intersection_with_local_cuboid(
640 flip_mesh,
641 cuboid,
642 &position.inv_mul(cuboid_position),
643 flip_cuboid,
644 epsilon,
645 )
646 }
647
648 pub fn intersection_with_local_cuboid(
650 &self,
651 flip_mesh: bool,
652 cuboid: &Cuboid,
653 cuboid_position: &Isometry<Real>,
654 flip_cuboid: bool,
655 _epsilon: Real,
656 ) -> Result<Option<Self>, MeshIntersectionError> {
657 if self.topology().is_some() && self.pseudo_normals_if_oriented().is_some() {
658 let (cuboid_vtx, cuboid_idx) = cuboid.to_trimesh();
659 let cuboid_trimesh = TriMesh::with_flags(
660 cuboid_vtx,
661 cuboid_idx,
662 TriMeshFlags::HALF_EDGE_TOPOLOGY | TriMeshFlags::ORIENTED,
663 )
664 .unwrap();
666
667 let intersect_meshes = intersect_meshes(
668 &Isometry::identity(),
669 self,
670 flip_mesh,
671 cuboid_position,
672 &cuboid_trimesh,
673 flip_cuboid,
674 );
675 return intersect_meshes;
676 }
677
678 let cuboid_aabb = cuboid.compute_aabb(cuboid_position);
679 let mut intersecting_tris = vec![];
680 let mut visitor = BoundingVolumeIntersectionsVisitor::new(&cuboid_aabb, |id| {
681 intersecting_tris.push(*id);
682 true
683 });
684 let _ = self.qbvh().traverse_depth_first(&mut visitor);
685
686 if intersecting_tris.is_empty() {
687 return Ok(None);
688 }
689
690 let vertices = self.vertices();
693 let indices = self.indices();
694
695 let mut clip_workspace = vec![];
696 let mut new_vertices = vec![];
697 let mut new_indices = vec![];
698 let aabb = cuboid.local_aabb();
699 let inv_pos = cuboid_position.inverse();
700 let mut to_clip = vec![];
701
702 for tri in intersecting_tris {
703 let idx = indices[tri as usize];
704 to_clip.extend_from_slice(&[
705 inv_pos * vertices[idx[0] as usize],
706 inv_pos * vertices[idx[1] as usize],
707 inv_pos * vertices[idx[2] as usize],
708 ]);
709
710 if !(aabb.contains_local_point(&to_clip[0])
714 && aabb.contains_local_point(&to_clip[1])
715 && aabb.contains_local_point(&to_clip[2]))
716 {
717 aabb.clip_polygon_with_workspace(&mut to_clip, &mut clip_workspace);
718 }
719
720 if to_clip.len() >= 3 {
721 let base_i = new_vertices.len();
722 for i in 1..to_clip.len() - 1 {
723 new_indices.push([base_i as u32, (base_i + i) as u32, (base_i + i + 1) as u32]);
724 }
725 new_vertices.append(&mut to_clip);
726 }
727 }
728
729 for pt in &mut new_vertices {
732 *pt = cuboid_position * *pt;
733 }
734
735 Ok(if new_vertices.len() >= 3 {
736 Some(TriMesh::new(new_vertices, new_indices).unwrap())
737 } else {
738 None
739 })
740 }
741}