1use crate::bounding_volume::Aabb;
2use crate::math::{Pose, Real, Vector, VectorExt};
3use crate::query::{IntersectResult, PointQuery, SplitResult};
4use crate::shape::{Cuboid, FeatureId, Polyline, Segment, Shape, TriMesh, TriMeshFlags, Triangle};
5use crate::transformation::{intersect_meshes, MeshIntersectionError};
6use crate::utils::{hashmap::HashMap, SortedPair, WBasis};
7use alloc::{vec, vec::Vec};
8use core::cmp::Ordering;
9use spade::{handles::FixedVertexHandle, ConstrainedDelaunayTriangulation, Triangulation as _};
10
11struct Triangulation {
12 delaunay: ConstrainedDelaunayTriangulation<spade::Point2<Real>>,
13 basis: [Vector; 2],
14 basis_origin: Vector,
15 spade2index: HashMap<FixedVertexHandle, u32>,
16 index2spade: HashMap<u32, FixedVertexHandle>,
17}
18
19impl Triangulation {
20 fn new(axis: Vector, basis_origin: Vector) -> Self {
21 Triangulation {
22 delaunay: ConstrainedDelaunayTriangulation::new(),
23 basis: axis.orthonormal_basis(),
24 basis_origin,
25 spade2index: HashMap::default(),
26 index2spade: HashMap::default(),
27 }
28 }
29
30 fn project(&self, pt: Vector) -> spade::Point2<Real> {
31 let dpt = pt - self.basis_origin;
32 spade::Point2::new(dpt.dot(self.basis[0]), dpt.dot(self.basis[1]))
33 }
34
35 fn add_edge(&mut self, id1: u32, id2: u32, points: &[Vector]) {
36 let proj1 = self.project(points[id1 as usize]);
37 let proj2 = self.project(points[id2 as usize]);
38
39 let handle1 = *self.index2spade.entry(id1).or_insert_with(|| {
40 let h = self.delaunay.insert(proj1).unwrap();
41 let _ = self.spade2index.insert(h, id1);
42 h
43 });
44
45 let handle2 = *self.index2spade.entry(id2).or_insert_with(|| {
46 let h = self.delaunay.insert(proj2).unwrap();
47 let _ = self.spade2index.insert(h, id2);
48 h
49 });
50
51 if !self.delaunay.can_add_constraint(handle1, handle2) {
53 let _ = self.delaunay.add_constraint(handle1, handle2);
54 }
55 }
56}
57
58impl TriMesh {
59 pub fn canonical_split(&self, axis: usize, bias: Real, epsilon: Real) -> SplitResult<Self> {
70 self.local_split(Vector::ith(axis, 1.0), bias, epsilon)
72 }
73
74 pub fn split(
77 &self,
78 position: &Pose,
79 axis: Vector,
80 bias: Real,
81 epsilon: Real,
82 ) -> SplitResult<Self> {
83 let local_axis = position.rotation.inverse() * axis;
84 let added_bias = -position.translation.dot(axis);
85 self.local_split(local_axis, bias + added_bias, epsilon)
86 }
87
88 pub fn local_split(&self, local_axis: Vector, bias: Real, epsilon: Real) -> SplitResult<Self> {
91 let mut triangulation = if self.pseudo_normals_if_oriented().is_some() {
92 Some(Triangulation::new(local_axis, self.vertices()[0]))
93 } else {
94 None
95 };
96
97 let vertices = self.vertices();
99 let indices = self.indices();
100 let mut colors = vec![0u8; self.vertices().len()];
101
102 let mut found_negative = false;
106 let mut found_positive = false;
107 for (i, pt) in vertices.iter().enumerate() {
108 let dist_to_plane = pt.dot(local_axis) - bias;
109 if dist_to_plane < -epsilon {
110 found_negative = true;
111 colors[i] = 1;
112 } else if dist_to_plane > epsilon {
113 found_positive = true;
114 colors[i] = 2;
115 }
116 }
117
118 if !found_negative {
120 return SplitResult::Positive;
121 }
122
123 if !found_positive {
124 return SplitResult::Negative;
125 }
126
127 let mut intersections_found = HashMap::default();
129 let mut new_indices = indices.to_vec();
130 let mut new_vertices = vertices.to_vec();
131
132 for (tri_id, idx) in indices.iter().enumerate() {
133 let mut intersection_features = (FeatureId::Unknown, FeatureId::Unknown);
134
135 for ia in 0..3 {
137 let ib = (ia + 1) % 3;
138 let idx_a = idx[ia as usize];
139 let idx_b = idx[ib as usize];
140
141 let fid = match (colors[idx_a as usize], colors[idx_b as usize]) {
142 (1, 2) | (2, 1) => FeatureId::Edge(ia),
143 (0, _) => FeatureId::Vertex(ia),
145 _ => continue,
146 };
147
148 if intersection_features.0 == FeatureId::Unknown {
149 intersection_features.0 = fid;
150 } else {
151 intersection_features.1 = fid;
154 }
155 }
156
157 let mut intersect_edge = |idx_a, idx_b| {
159 *intersections_found
160 .entry(SortedPair::new(idx_a, idx_b))
161 .or_insert_with(|| {
162 let segment = Segment::new(
163 new_vertices[idx_a as usize],
164 new_vertices[idx_b as usize],
165 );
166 if let Some((intersection, _)) = segment
168 .local_split_and_get_intersection(local_axis, bias, epsilon)
169 .1
170 {
171 new_vertices.push(intersection);
172 colors.push(0);
173 (new_vertices.len() - 1) as u32
174 } else {
175 unreachable!()
176 }
177 })
178 };
179
180 match intersection_features {
183 (_, FeatureId::Unknown) => {
184 assert!(
187 matches!(intersection_features.0, FeatureId::Unknown)
188 || matches!(intersection_features.0, FeatureId::Vertex(_))
189 );
190 }
191 (FeatureId::Vertex(v1), FeatureId::Vertex(v2)) => {
192 if let Some(triangulation) = &mut triangulation {
196 let id1 = idx[v1 as usize];
197 let id2 = idx[v2 as usize];
198 triangulation.add_edge(id1, id2, &new_vertices);
199 }
200 }
201 (FeatureId::Vertex(iv), FeatureId::Edge(ie))
202 | (FeatureId::Edge(ie), FeatureId::Vertex(iv)) => {
203 let ia = ie;
205 let ib = (ie + 1) % 3;
206 let ic = (ie + 2) % 3;
207 let idx_a = idx[ia as usize];
208 let idx_b = idx[ib as usize];
209 let idx_c = idx[ic as usize];
210 assert_eq!(iv, ic);
211
212 let intersection_idx = intersect_edge(idx_a, idx_b);
213
214 let new_tri_a = [idx_c, idx_a, intersection_idx];
216 let new_tri_b = [idx_b, idx_c, intersection_idx];
217
218 new_indices[tri_id] = new_tri_a;
219 new_indices.push(new_tri_b);
220
221 if let Some(triangulation) = &mut triangulation {
222 triangulation.add_edge(intersection_idx, idx_c, &new_vertices);
223 }
224 }
225 (FeatureId::Edge(mut e1), FeatureId::Edge(mut e2)) => {
226 if e2 != (e1 + 1) % 3 {
229 core::mem::swap(&mut e1, &mut e2);
230 }
231
232 let ia = e2; let ib = (e2 + 1) % 3;
234 let ic = (e2 + 2) % 3;
235 let idx_a = idx[ia as usize];
236 let idx_b = idx[ib as usize];
237 let idx_c = idx[ic as usize];
238
239 let intersection1 = intersect_edge(idx_c, idx_a);
240 let intersection2 = intersect_edge(idx_a, idx_b);
241
242 let new_tri1 = [idx_a, intersection2, intersection1];
243 let new_tri2 = [intersection2, idx_b, idx_c];
244 let new_tri3 = [intersection2, idx_c, intersection1];
245 new_indices[tri_id] = new_tri1;
246 new_indices.push(new_tri2);
247 new_indices.push(new_tri3);
248
249 if let Some(triangulation) = &mut triangulation {
250 triangulation.add_edge(intersection1, intersection2, &new_vertices);
251 }
252 }
253 _ => unreachable!(),
254 }
255 }
256
257 let mut vertices_lhs = vec![];
259 let mut vertices_rhs = vec![];
260 let mut indices_lhs = vec![];
261 let mut indices_rhs = vec![];
262 let mut remap = vec![];
263
264 for i in 0..new_vertices.len() {
265 match colors[i] {
266 0 => {
267 remap.push((vertices_lhs.len() as u32, vertices_rhs.len() as u32));
268 vertices_lhs.push(new_vertices[i]);
269 vertices_rhs.push(new_vertices[i]);
270 }
271 1 => {
272 remap.push((vertices_lhs.len() as u32, u32::MAX));
273 vertices_lhs.push(new_vertices[i]);
274 }
275 2 => {
276 remap.push((u32::MAX, vertices_rhs.len() as u32));
277 vertices_rhs.push(new_vertices[i]);
278 }
279 _ => unreachable!(),
280 }
281 }
282
283 for idx in new_indices {
284 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]]];
286 let remap = [remap[idx[0]], remap[idx[1]], remap[idx[2]]];
287
288 if colors[0] == 1 || colors[1] == 1 || colors[2] == 1 {
289 assert!(colors[0] != 2 && colors[1] != 2 && colors[2] != 2);
290 indices_lhs.push([remap[0].0, remap[1].0, remap[2].0]);
291 } else if colors[0] == 2 || colors[1] == 2 || colors[2] == 2 {
292 assert!(colors[0] != 1 && colors[1] != 1 && colors[2] != 1);
293 indices_rhs.push([remap[0].1, remap[1].1, remap[2].1]);
294 } else {
295 indices_lhs.push([remap[0].0, remap[1].0, remap[2].0]);
297 indices_rhs.push([remap[0].1, remap[1].1, remap[2].1]);
298 }
299 }
300
301 if let Some(triangulation) = triangulation {
303 for face in triangulation.delaunay.inner_faces() {
304 let vtx = face.vertices();
305 let mut idx1 = [0; 3];
306 let mut idx2 = [0; 3];
307 for k in 0..3 {
308 let vid = triangulation.spade2index[&vtx[k].fix()];
309 assert_eq!(colors[vid as usize], 0);
310 idx1[k] = remap[vid as usize].0;
311 idx2[k] = remap[vid as usize].1;
312 }
313
314 let tri = Triangle::new(
315 vertices_lhs[idx1[0] as usize],
316 vertices_lhs[idx1[1] as usize],
317 vertices_lhs[idx1[2] as usize],
318 );
319
320 if self.contains_local_point(tri.center()) {
321 indices_lhs.push(idx1);
322
323 idx2.swap(1, 2); indices_rhs.push(idx2);
325 }
326 }
327 }
328
329 if indices_rhs.is_empty() {
333 SplitResult::Negative
334 } else if indices_lhs.is_empty() {
335 SplitResult::Positive
336 } else {
337 let mesh_lhs = TriMesh::new(vertices_lhs, indices_lhs).unwrap();
338 let mesh_rhs = TriMesh::new(vertices_rhs, indices_rhs).unwrap();
339 SplitResult::Pair(mesh_lhs, mesh_rhs)
340 }
341 }
342
343 pub fn canonical_intersection_with_plane(
352 &self,
353 axis: usize,
354 bias: Real,
355 epsilon: Real,
356 ) -> IntersectResult<Polyline> {
357 self.intersection_with_local_plane(Vector::ith(axis, 1.0), bias, epsilon)
358 }
359
360 pub fn intersection_with_plane(
364 &self,
365 position: &Pose,
366 axis: Vector,
367 bias: Real,
368 epsilon: Real,
369 ) -> IntersectResult<Polyline> {
370 let local_axis = position.rotation.inverse() * axis;
371 let added_bias = -position.translation.dot(axis);
372 self.intersection_with_local_plane(local_axis, bias + added_bias, epsilon)
373 }
374
375 pub fn intersection_with_local_plane(
379 &self,
380 local_axis: Vector,
381 bias: Real,
382 epsilon: Real,
383 ) -> IntersectResult<Polyline> {
384 let vertices = self.vertices();
386 let indices = self.indices();
387 let mut colors = vec![0u8; self.vertices().len()];
388
389 let mut found_negative = false;
393 let mut found_positive = false;
394 for (i, pt) in vertices.iter().enumerate() {
395 let dist_to_plane = pt.dot(local_axis) - bias;
396 if dist_to_plane < -epsilon {
397 found_negative = true;
398 colors[i] = 1;
399 } else if dist_to_plane > epsilon {
400 found_positive = true;
401 colors[i] = 2;
402 }
403 }
404
405 if !found_negative {
407 return IntersectResult::Positive;
408 }
409
410 if !found_positive {
411 return IntersectResult::Negative;
412 }
413
414 let mut index_adjacencies: Vec<Vec<usize>> = Vec::new(); let mut add_segment_adjacencies = |idx_a: usize, idx_b| {
419 assert!(idx_a <= index_adjacencies.len());
420
421 match idx_a.cmp(&index_adjacencies.len()) {
422 Ordering::Less => index_adjacencies[idx_a].push(idx_b),
423 Ordering::Equal => index_adjacencies.push(vec![idx_b]),
424 Ordering::Greater => {}
425 }
426 };
427 let mut add_segment_adjacencies_symmetric = |idx_a: usize, idx_b| {
428 if idx_a < idx_b {
429 add_segment_adjacencies(idx_a, idx_b);
430 add_segment_adjacencies(idx_b, idx_a);
431 } else {
432 add_segment_adjacencies(idx_b, idx_a);
433 add_segment_adjacencies(idx_a, idx_b);
434 }
435 };
436
437 let mut intersections_found = HashMap::default();
438 let mut existing_vertices_found = HashMap::default();
439 let mut new_vertices = Vec::new();
440
441 for idx in indices.iter() {
442 let mut intersection_features = (FeatureId::Unknown, FeatureId::Unknown);
443
444 for ia in 0..3 {
446 let ib = (ia + 1) % 3;
447 let idx_a = idx[ia as usize];
448 let idx_b = idx[ib as usize];
449
450 let fid = match (colors[idx_a as usize], colors[idx_b as usize]) {
451 (1, 2) | (2, 1) => FeatureId::Edge(ia),
452 (0, _) => FeatureId::Vertex(ia),
454 _ => continue,
455 };
456
457 if intersection_features.0 == FeatureId::Unknown {
458 intersection_features.0 = fid;
459 } else {
460 intersection_features.1 = fid;
463 }
464 }
465
466 let mut intersect_edge = |idx_a, idx_b| {
468 *intersections_found
469 .entry(SortedPair::new(idx_a, idx_b))
470 .or_insert_with(|| {
471 let segment =
472 Segment::new(vertices[idx_a as usize], vertices[idx_b as usize]);
473 if let Some((intersection, _)) = segment
475 .local_split_and_get_intersection(local_axis, bias, epsilon)
476 .1
477 {
478 new_vertices.push(intersection);
479 colors.push(0);
480 new_vertices.len() - 1
481 } else {
482 unreachable!()
483 }
484 })
485 };
486
487 match intersection_features {
490 (_, FeatureId::Unknown) => {
491 assert!(
494 matches!(intersection_features.0, FeatureId::Unknown)
495 || matches!(intersection_features.0, FeatureId::Vertex(_))
496 );
497 }
498 (FeatureId::Vertex(iv1), FeatureId::Vertex(iv2)) => {
499 let id1 = idx[iv1 as usize];
504 let id2 = idx[iv2 as usize];
505
506 let out_id1 = *existing_vertices_found.entry(id1).or_insert_with(|| {
507 let v1 = vertices[id1 as usize];
508
509 new_vertices.push(v1);
510 new_vertices.len() - 1
511 });
512 let out_id2 = *existing_vertices_found.entry(id2).or_insert_with(|| {
513 let v2 = vertices[id2 as usize];
514
515 new_vertices.push(v2);
516 new_vertices.len() - 1
517 });
518
519 add_segment_adjacencies_symmetric(out_id1, out_id2);
520 }
521 (FeatureId::Vertex(iv), FeatureId::Edge(ie))
522 | (FeatureId::Edge(ie), FeatureId::Vertex(iv)) => {
523 let ia = ie;
525 let ib = (ie + 1) % 3;
526 let ic = (ie + 2) % 3;
527 let idx_a = idx[ia as usize];
528 let idx_b = idx[ib as usize];
529 let idx_c = idx[ic as usize];
530 assert_eq!(iv, ic);
531
532 let intersection_idx = intersect_edge(idx_a, idx_b);
533
534 let out_idx_c = *existing_vertices_found.entry(idx_c).or_insert_with(|| {
535 let v2 = vertices[idx_c as usize];
536
537 new_vertices.push(v2);
538 new_vertices.len() - 1
539 });
540
541 add_segment_adjacencies_symmetric(out_idx_c, intersection_idx);
542 }
543 (FeatureId::Edge(mut e1), FeatureId::Edge(mut e2)) => {
544 if e2 != (e1 + 1) % 3 {
547 core::mem::swap(&mut e1, &mut e2);
548 }
549
550 let ia = e2; let ib = (e2 + 1) % 3;
552 let ic = (e2 + 2) % 3;
553 let idx_a = idx[ia as usize];
554 let idx_b = idx[ib as usize];
555 let idx_c = idx[ic as usize];
556
557 let intersection1 = intersect_edge(idx_c, idx_a);
558 let intersection2 = intersect_edge(idx_a, idx_b);
559
560 add_segment_adjacencies_symmetric(intersection1, intersection2);
561 }
562 _ => unreachable!(),
563 }
564 }
565
566 let mut polyline_indices: Vec<[u32; 2]> = Vec::with_capacity(index_adjacencies.len() + 1);
568
569 let mut seen = vec![false; index_adjacencies.len()];
570 for (idx, neighbors) in index_adjacencies.iter().enumerate() {
571 if !seen[idx] {
572 let first = idx;
576 let mut prev = first;
577 let mut next = neighbors.first(); 'traversal: while let Some(current) = next {
580 seen[*current] = true;
581 polyline_indices.push([prev as u32, *current as u32]);
582
583 for neighbor in index_adjacencies[*current].iter() {
584 if *neighbor != prev && *neighbor != first {
585 prev = *current;
586 next = Some(neighbor);
587 continue 'traversal;
588 } else if *neighbor != prev && *neighbor == first {
589 polyline_indices.push([*current as u32, first as u32]);
591 next = None;
592 continue 'traversal;
593 }
594 }
595 }
596 }
597 }
598
599 IntersectResult::Intersect(Polyline::new(new_vertices, Some(polyline_indices)))
600 }
601
602 pub fn intersection_with_aabb(
604 &self,
605 position: &Pose,
606 flip_mesh: bool,
607 aabb: &Aabb,
608 flip_cuboid: bool,
609 epsilon: Real,
610 ) -> Result<Option<Self>, MeshIntersectionError> {
611 let cuboid = Cuboid::new(aabb.half_extents());
612 let cuboid_pos = Pose::from_translation(aabb.center());
613 self.intersection_with_cuboid(
614 position,
615 flip_mesh,
616 &cuboid,
617 &cuboid_pos,
618 flip_cuboid,
619 epsilon,
620 )
621 }
622
623 pub fn intersection_with_cuboid(
625 &self,
626 position: &Pose,
627 flip_mesh: bool,
628 cuboid: &Cuboid,
629 cuboid_position: &Pose,
630 flip_cuboid: bool,
631 epsilon: Real,
632 ) -> Result<Option<Self>, MeshIntersectionError> {
633 self.intersection_with_local_cuboid(
634 flip_mesh,
635 cuboid,
636 &position.inv_mul(cuboid_position),
637 flip_cuboid,
638 epsilon,
639 )
640 }
641
642 pub fn intersection_with_local_cuboid(
644 &self,
645 flip_mesh: bool,
646 cuboid: &Cuboid,
647 cuboid_position: &Pose,
648 flip_cuboid: bool,
649 _epsilon: Real,
650 ) -> Result<Option<Self>, MeshIntersectionError> {
651 if self.topology().is_some() && self.pseudo_normals_if_oriented().is_some() {
652 let (cuboid_vtx, cuboid_idx) = cuboid.to_trimesh();
653 let cuboid_trimesh = TriMesh::with_flags(
654 cuboid_vtx,
655 cuboid_idx,
656 TriMeshFlags::HALF_EDGE_TOPOLOGY | TriMeshFlags::ORIENTED,
657 )
658 .unwrap();
660
661 let intersect_meshes = intersect_meshes(
662 &Pose::IDENTITY,
663 self,
664 flip_mesh,
665 cuboid_position,
666 &cuboid_trimesh,
667 flip_cuboid,
668 );
669 return intersect_meshes;
670 }
671
672 let cuboid_aabb = cuboid.compute_aabb(cuboid_position);
673 let intersecting_tris: Vec<_> = self.bvh().intersect_aabb(&cuboid_aabb).collect();
674
675 if intersecting_tris.is_empty() {
676 return Ok(None);
677 }
678
679 let vertices = self.vertices();
682 let indices = self.indices();
683
684 let mut clip_workspace = vec![];
685 let mut new_vertices = vec![];
686 let mut new_indices = vec![];
687 let aabb = cuboid.local_aabb();
688 let inv_pos = cuboid_position.inverse();
689 let mut to_clip = vec![];
690
691 for tri in intersecting_tris {
692 let idx = indices[tri as usize];
693 to_clip.extend_from_slice(&[
694 inv_pos * vertices[idx[0] as usize],
695 inv_pos * vertices[idx[1] as usize],
696 inv_pos * vertices[idx[2] as usize],
697 ]);
698
699 if !(aabb.contains_local_point(to_clip[0])
703 && aabb.contains_local_point(to_clip[1])
704 && aabb.contains_local_point(to_clip[2]))
705 {
706 aabb.clip_polygon_with_workspace(&mut to_clip, &mut clip_workspace);
707 }
708
709 if to_clip.len() >= 3 {
710 let base_i = new_vertices.len();
711 for i in 1..to_clip.len() - 1 {
712 new_indices.push([base_i as u32, (base_i + i) as u32, (base_i + i + 1) as u32]);
713 }
714 new_vertices.append(&mut to_clip);
715 }
716 }
717
718 for pt in &mut new_vertices {
721 *pt = cuboid_position * *pt;
722 }
723
724 Ok(if new_vertices.len() >= 3 {
725 Some(TriMesh::new(new_vertices, new_indices).unwrap())
726 } else {
727 None
728 })
729 }
730}