parry3d_f64/query/split/
split_trimesh.rs

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        // NOTE: the naming of the `ConstrainedDelaunayTriangulation::can_add_constraint` method is misleading.
53        if !self.delaunay.can_add_constraint(handle1, handle2) {
54            let _ = self.delaunay.add_constraint(handle1, handle2);
55        }
56    }
57}
58
59impl TriMesh {
60    /// Splits this `TriMesh` along the given canonical axis.
61    ///
62    /// This will split the Aabb by a plane with a normal with it’s `axis`-th component set to 1.
63    /// The splitting plane is shifted wrt. the origin by the `bias` (i.e. it passes through the point
64    /// equal to `normal * bias`).
65    ///
66    /// # Result
67    /// Returns the result of the split. The first mesh returned is the piece lying on the negative
68    /// half-space delimited by the splitting plane. The second mesh returned is the piece lying on the
69    /// positive half-space delimited by the splitting plane.
70    pub fn canonical_split(&self, axis: usize, bias: Real, epsilon: Real) -> SplitResult<Self> {
71        // TODO: optimize this.
72        self.local_split(&Vector::ith_axis(axis), bias, epsilon)
73    }
74
75    /// Splits this mesh, transformed by `position` by a plane identified by its normal `local_axis`
76    /// and the `bias` (i.e. the plane passes through the point equal to `normal * bias`).
77    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    /// Splits this mesh by a plane identified by its normal `local_axis`
90    /// and the `bias` (i.e. the plane passes through the point equal to `normal * bias`).
91    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        // 1. Partition the vertices.
104        let vertices = self.vertices();
105        let indices = self.indices();
106        let mut colors = vec![0u8; self.vertices().len()];
107
108        // Color 0 = on plane.
109        //       1 = on negative half-space.
110        //       2 = on positive half-space.
111        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        // Exit early if `self` isn’t crossed by the plane.
125        if !found_negative {
126            return SplitResult::Positive;
127        }
128
129        if !found_positive {
130            return SplitResult::Negative;
131        }
132
133        // 2. Split the triangles.
134        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            // First, find where the plane intersects the triangle.
142            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                    // NOTE: the case (_, 0) will be dealt with in the next loop iteration.
150                    (0, _) => FeatureId::Vertex(ia),
151                    _ => continue,
152                };
153
154                if intersection_features.0 == FeatureId::Unknown {
155                    intersection_features.0 = fid;
156                } else {
157                    // TODO: this assertion may fire if the triangle is coplanar with the edge?
158                    // assert_eq!(intersection_features.1, FeatureId::Unknown);
159                    intersection_features.1 = fid;
160                }
161            }
162
163            // Helper that intersects an edge with the plane.
164            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                        // Intersect the segment with the plane.
173                        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            // Perform the intersection, push new triangles, and update
187            // triangulation constraints if needed.
188            match intersection_features {
189                (_, FeatureId::Unknown) => {
190                    // The plane doesn’t intersect the triangle, or intersects it at
191                    // a single vertex, so we don’t have anything to do.
192                    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                    // The plane intersects the triangle along one of its edge.
199                    // We don’t have to split the triangle, but we need to add
200                    // a constraint to the triangulation.
201                    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                    // The plane splits the triangle into exactly two triangles.
210                    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                    // Compute the indices of the two triangles.
221                    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                    // The plane splits the triangle into 1 + 2 triangles.
233                    // First, make sure the edge indices are consecutive.
234                    if e2 != (e1 + 1) % 3 {
235                        core::mem::swap(&mut e1, &mut e2);
236                    }
237
238                    let ia = e2; // The first point of the second edge is the vertex shared by both edges.
239                    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        // 3. Partition the new triangles into two trimeshes.
264        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]; // Convert to usize.
291            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                // The colors are all 0, so push into both trimeshes.
302                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        // Push the triangulation if there is one.
308        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); // Flip orientation for the second half of the split.
330                    indices_rhs.push(idx2);
331                }
332            }
333        }
334
335        // TODO: none of the index buffers should be empty at this point unless perhaps
336        //       because of some rounding errors?
337        //       Should we just panic if they are empty?
338        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    /// Computes the intersection [`Polyline`]s between this mesh and the plane identified by
350    /// the given canonical axis.
351    ///
352    /// This will intersect the mesh by a plane with a normal with it’s `axis`-th component set to 1.
353    /// The splitting plane is shifted wrt. the origin by the `bias` (i.e. it passes through the point
354    /// equal to `normal * bias`).
355    ///
356    /// Note that the resultant polyline may have multiple connected components
357    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    /// Computes the intersection [`Polyline`]s between this mesh, transformed by `position`,
367    /// and a plane identified by its normal `axis` and the `bias`
368    /// (i.e. the plane passes through the point equal to `normal * bias`).
369    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    /// Computes the intersection [`Polyline`]s between this mesh
382    /// and a plane identified by its normal `local_axis`
383    /// and the `bias` (i.e. the plane passes through the point equal to `normal * bias`).
384    pub fn intersection_with_local_plane(
385        &self,
386        local_axis: &UnitVector<Real>,
387        bias: Real,
388        epsilon: Real,
389    ) -> IntersectResult<Polyline> {
390        // 1. Partition the vertices.
391        let vertices = self.vertices();
392        let indices = self.indices();
393        let mut colors = vec![0u8; self.vertices().len()];
394
395        // Color 0 = on plane.
396        //       1 = on negative half-space.
397        //       2 = on positive half-space.
398        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        // Exit early if `self` isn’t crossed by the plane.
412        if !found_negative {
413            return IntersectResult::Positive;
414        }
415
416        if !found_positive {
417            return IntersectResult::Negative;
418        }
419
420        // 2. Split the triangles.
421        let mut index_adjacencies: Vec<Vec<usize>> = Vec::new(); // Adjacency list of indices
422
423        // Helper functions for adding polyline segments to the adjacency list
424        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            // First, find where the plane intersects the triangle.
451            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                    // NOTE: the case (_, 0) will be dealt with in the next loop iteration.
459                    (0, _) => FeatureId::Vertex(ia),
460                    _ => continue,
461                };
462
463                if intersection_features.0 == FeatureId::Unknown {
464                    intersection_features.0 = fid;
465                } else {
466                    // TODO: this assertion may fire if the triangle is coplanar with the edge?
467                    // assert_eq!(intersection_features.1, FeatureId::Unknown);
468                    intersection_features.1 = fid;
469                }
470            }
471
472            // Helper that intersects an edge with the plane.
473            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                        // Intersect the segment with the plane.
480                        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            // Perform the intersection, push new triangles, and update
494            // triangulation constraints if needed.
495            match intersection_features {
496                (_, FeatureId::Unknown) => {
497                    // The plane doesn’t intersect the triangle, or intersects it at
498                    // a single vertex, so we don’t have anything to do.
499                    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                    // The plane intersects the triangle along one of its edge.
506                    // We don’t have to split the triangle, but we need to add
507                    // the edge to the polyline indices
508
509                    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                    // The plane splits the triangle into exactly two triangles.
530                    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                    // The plane splits the triangle into 1 + 2 triangles.
551                    // First, make sure the edge indices are consecutive.
552                    if e2 != (e1 + 1) % 3 {
553                        core::mem::swap(&mut e1, &mut e2);
554                    }
555
556                    let ia = e2; // The first point of the second edge is the vertex shared by both edges.
557                    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        // 3. Ensure consistent edge orientation by traversing the adjacency list
573        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                // Start a new component
579                // Traverse the adjencies until the loop closes
580
581                let first = idx;
582                let mut prev = first;
583                let mut next = neighbors.first(); // Arbitrary neighbor
584
585                '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                            // If the next index is same as the first, close the polyline and exit
596                            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    /// Computes the intersection mesh between an Aabb and this mesh.
609    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    /// Computes the intersection mesh between a cuboid and this mesh transformed by `position`.
630    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    /// Computes the intersection mesh between a cuboid and this mesh.
649    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            // `TriMesh::with_flags` can't fail for `cuboid.to_trimesh()`.
665            .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        // First, very naive version that outputs a triangle soup without
691        // index buffer (shared vertices are duplicated).
692        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            // There is no need to clip if the triangle is fully inside of the Aabb.
711            // Note that we can’t take a shortcut for the case where all the vertices are
712            // outside of the Aabb, because the Aabb can still instersect the edges or face.
713            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        // The clipping outputs points in the local-space of the cuboid.
730        // So we need to transform it back.
731        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}