parry3d/query/epa/
epa3.rs

1//! Three-dimensional penetration depth queries using the Expanding Polytope Algorithm.
2
3use crate::math::{Isometry, Point, Real, Vector};
4use crate::query::gjk::{self, CSOPoint, ConstantOrigin, VoronoiSimplex};
5use crate::query::PointQueryWithLocation;
6use crate::shape::{SupportMap, Triangle, TrianglePointLocation};
7use crate::utils;
8use alloc::collections::BinaryHeap;
9use alloc::vec::Vec;
10use core::cmp::Ordering;
11use na::{self, Unit};
12use num::Bounded;
13
14#[derive(Copy, Clone, PartialEq)]
15struct FaceId {
16    id: usize,
17    neg_dist: Real,
18}
19
20impl FaceId {
21    fn new(id: usize, neg_dist: Real) -> Option<Self> {
22        if neg_dist > gjk::eps_tol() {
23            None
24        } else {
25            Some(FaceId { id, neg_dist })
26        }
27    }
28}
29
30impl Eq for FaceId {}
31
32impl PartialOrd for FaceId {
33    #[inline]
34    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
35        Some(self.cmp(other))
36    }
37}
38
39impl Ord for FaceId {
40    #[inline]
41    fn cmp(&self, other: &Self) -> Ordering {
42        if self.neg_dist < other.neg_dist {
43            Ordering::Less
44        } else if self.neg_dist > other.neg_dist {
45            Ordering::Greater
46        } else {
47            Ordering::Equal
48        }
49    }
50}
51
52#[derive(Clone, Debug)]
53struct Face {
54    pts: [usize; 3],
55    adj: [usize; 3],
56    normal: Unit<Vector<Real>>,
57    bcoords: [Real; 3],
58    deleted: bool,
59}
60
61impl Face {
62    pub fn new_with_proj(
63        vertices: &[CSOPoint],
64        bcoords: [Real; 3],
65        pts: [usize; 3],
66        adj: [usize; 3],
67    ) -> Self {
68        let normal;
69
70        if let Some(n) = utils::ccw_face_normal([
71            &vertices[pts[0]].point,
72            &vertices[pts[1]].point,
73            &vertices[pts[2]].point,
74        ]) {
75            normal = n;
76        } else {
77            // This is a bit of a hack for degenerate faces.
78            // TODO: It will work OK with our current code, though
79            // we should do this in another way to avoid any risk
80            // of misusing the face normal in the future.
81            normal = Unit::new_unchecked(na::zero());
82        }
83
84        Face {
85            pts,
86            bcoords,
87            adj,
88            normal,
89            deleted: false,
90        }
91    }
92
93    pub fn new(vertices: &[CSOPoint], pts: [usize; 3], adj: [usize; 3]) -> (Self, bool) {
94        let tri = Triangle::new(
95            vertices[pts[0]].point,
96            vertices[pts[1]].point,
97            vertices[pts[2]].point,
98        );
99        let (proj, loc) = tri.project_local_point_and_get_location(&Point::<Real>::origin(), true);
100
101        match loc {
102            TrianglePointLocation::OnVertex(_) | TrianglePointLocation::OnEdge(_, _) => {
103                let eps_tol = crate::math::DEFAULT_EPSILON * 100.0; // Same as in closest_points
104                (
105                    // barycentric_coordinates is guaranteed to work in OnVertex and OnEdge locations
106                    Self::new_with_proj(vertices, loc.barycentric_coordinates().unwrap(), pts, adj),
107                    proj.is_inside_eps(&Point::<Real>::origin(), eps_tol),
108                )
109            }
110            TrianglePointLocation::OnFace(_, bcoords) => {
111                (Self::new_with_proj(vertices, bcoords, pts, adj), true)
112            }
113            _ => (Self::new_with_proj(vertices, [0.0; 3], pts, adj), false),
114        }
115    }
116
117    pub fn closest_points(&self, vertices: &[CSOPoint]) -> (Point<Real>, Point<Real>) {
118        (
119            vertices[self.pts[0]].orig1 * self.bcoords[0]
120                + vertices[self.pts[1]].orig1.coords * self.bcoords[1]
121                + vertices[self.pts[2]].orig1.coords * self.bcoords[2],
122            vertices[self.pts[0]].orig2 * self.bcoords[0]
123                + vertices[self.pts[1]].orig2.coords * self.bcoords[1]
124                + vertices[self.pts[2]].orig2.coords * self.bcoords[2],
125        )
126    }
127
128    pub fn contains_point(&self, id: usize) -> bool {
129        self.pts[0] == id || self.pts[1] == id || self.pts[2] == id
130    }
131
132    pub fn next_ccw_pt_id(&self, id: usize) -> usize {
133        if self.pts[0] == id {
134            1
135        } else if self.pts[1] == id {
136            2
137        } else {
138            if self.pts[2] != id {
139                log::debug!(
140                    "Hit unexpected state in EPA: found index {}, expected: {}.",
141                    self.pts[2],
142                    id
143                );
144            }
145
146            0
147        }
148    }
149
150    pub fn can_be_seen_by(&self, vertices: &[CSOPoint], point: usize, opp_pt_id: usize) -> bool {
151        let p0 = &vertices[self.pts[opp_pt_id]].point;
152        let p1 = &vertices[self.pts[(opp_pt_id + 1) % 3]].point;
153        let p2 = &vertices[self.pts[(opp_pt_id + 2) % 3]].point;
154        let pt = &vertices[point].point;
155
156        // NOTE: it is important that we return true for the case where
157        // the dot product is zero. This is because degenerate faces will
158        // have a zero normal, causing the dot product to be zero.
159        // So return true for these case will let us skip the triangle
160        // during silhouette computation.
161        (*pt - *p0).dot(&self.normal) >= -gjk::eps_tol()
162            || Triangle::new(*p1, *p2, *pt).is_affinely_dependent()
163    }
164}
165
166struct SilhouetteEdge {
167    face_id: usize,
168    opp_pt_id: usize,
169}
170
171impl SilhouetteEdge {
172    pub fn new(face_id: usize, opp_pt_id: usize) -> Self {
173        SilhouetteEdge { face_id, opp_pt_id }
174    }
175}
176
177/// The Expanding Polytope Algorithm in 3D.
178#[derive(Default)]
179pub struct EPA {
180    vertices: Vec<CSOPoint>,
181    faces: Vec<Face>,
182    silhouette: Vec<SilhouetteEdge>,
183    heap: BinaryHeap<FaceId>,
184}
185
186impl EPA {
187    /// Creates a new instance of the 3D Expanding Polytope Algorithm.
188    pub fn new() -> Self {
189        Self::default()
190    }
191
192    fn reset(&mut self) {
193        self.vertices.clear();
194        self.faces.clear();
195        self.heap.clear();
196        self.silhouette.clear();
197    }
198
199    /// Projects the origin on boundary of the given shape.
200    ///
201    /// The origin is assumed to be inside of the shape. If it is outside
202    /// use the GJK algorithm instead.
203    /// Return `None` if the origin is not inside of the shape or if
204    /// the EPA algorithm failed to compute the projection.
205    ///
206    /// Return the projected point in the local-space of `g`.
207    pub fn project_origin<G: ?Sized + SupportMap>(
208        &mut self,
209        m: &Isometry<Real>,
210        g: &G,
211        simplex: &VoronoiSimplex,
212    ) -> Option<Point<Real>> {
213        self.closest_points(&m.inverse(), g, &ConstantOrigin, simplex)
214            .map(|(p, _, _)| p)
215    }
216
217    /// Projects the origin on a shape using the EPA algorithm.
218    ///
219    /// The origin is assumed to be located inside of the shape.
220    /// Returns `None` if the EPA fails to converge or if `g1` and `g2` are not penetrating.
221    pub fn closest_points<G1, G2>(
222        &mut self,
223        pos12: &Isometry<Real>,
224        g1: &G1,
225        g2: &G2,
226        simplex: &VoronoiSimplex,
227    ) -> Option<(Point<Real>, Point<Real>, Unit<Vector<Real>>)>
228    where
229        G1: ?Sized + SupportMap,
230        G2: ?Sized + SupportMap,
231    {
232        let _eps = crate::math::DEFAULT_EPSILON;
233        let _eps_tol = _eps * 100.0;
234
235        self.reset();
236
237        /*
238         * Initialization.
239         */
240        for i in 0..simplex.dimension() + 1 {
241            self.vertices.push(*simplex.point(i));
242        }
243
244        if simplex.dimension() == 0 {
245            let mut n: Vector<Real> = na::zero();
246            n[1] = 1.0;
247            return Some((Point::origin(), Point::origin(), Unit::new_unchecked(n)));
248        } else if simplex.dimension() == 3 {
249            let dp1 = self.vertices[1] - self.vertices[0];
250            let dp2 = self.vertices[2] - self.vertices[0];
251            let dp3 = self.vertices[3] - self.vertices[0];
252
253            if dp1.cross(&dp2).dot(&dp3) > 0.0 {
254                self.vertices.swap(1, 2)
255            }
256
257            let pts1 = [0, 1, 2];
258            let pts2 = [1, 3, 2];
259            let pts3 = [0, 2, 3];
260            let pts4 = [0, 3, 1];
261
262            let adj1 = [3, 1, 2];
263            let adj2 = [3, 2, 0];
264            let adj3 = [0, 1, 3];
265            let adj4 = [2, 1, 0];
266
267            let (face1, proj_inside1) = Face::new(&self.vertices, pts1, adj1);
268            let (face2, proj_inside2) = Face::new(&self.vertices, pts2, adj2);
269            let (face3, proj_inside3) = Face::new(&self.vertices, pts3, adj3);
270            let (face4, proj_inside4) = Face::new(&self.vertices, pts4, adj4);
271
272            self.faces.push(face1);
273            self.faces.push(face2);
274            self.faces.push(face3);
275            self.faces.push(face4);
276
277            if proj_inside1 {
278                let dist1 = self.faces[0].normal.dot(&self.vertices[0].point.coords);
279                self.heap.push(FaceId::new(0, -dist1)?);
280            }
281
282            if proj_inside2 {
283                let dist2 = self.faces[1].normal.dot(&self.vertices[1].point.coords);
284                self.heap.push(FaceId::new(1, -dist2)?);
285            }
286
287            if proj_inside3 {
288                let dist3 = self.faces[2].normal.dot(&self.vertices[2].point.coords);
289                self.heap.push(FaceId::new(2, -dist3)?);
290            }
291
292            if proj_inside4 {
293                let dist4 = self.faces[3].normal.dot(&self.vertices[3].point.coords);
294                self.heap.push(FaceId::new(3, -dist4)?);
295            }
296
297            if !(proj_inside1 || proj_inside2 || proj_inside3 || proj_inside4) {
298                // Related issues:
299                // https://github.com/dimforge/parry/issues/253
300                // https://github.com/dimforge/parry/issues/246
301                log::debug!("Hit unexpected state in EPA: failed to project the origin on the initial simplex.");
302                return None;
303            }
304        } else {
305            if simplex.dimension() == 1 {
306                let dpt = self.vertices[1] - self.vertices[0];
307
308                Vector::orthonormal_subspace_basis(&[dpt], |dir| {
309                    // XXX: dir should already be unit on nalgebra!
310                    let dir = Unit::new_unchecked(*dir);
311                    self.vertices
312                        .push(CSOPoint::from_shapes(pos12, g1, g2, &dir));
313                    false
314                });
315            }
316
317            let pts1 = [0, 1, 2];
318            let pts2 = [0, 2, 1];
319
320            let adj1 = [1, 1, 1];
321            let adj2 = [0, 0, 0];
322
323            let (face1, _) = Face::new(&self.vertices, pts1, adj1);
324            let (face2, _) = Face::new(&self.vertices, pts2, adj2);
325            self.faces.push(face1);
326            self.faces.push(face2);
327
328            self.heap.push(FaceId::new(0, 0.0)?);
329            self.heap.push(FaceId::new(1, 0.0)?);
330        }
331
332        let mut niter = 0;
333        let mut max_dist = Real::max_value();
334        let mut best_face_id = *self.heap.peek()?;
335        let mut old_dist = 0.0;
336
337        /*
338         * Run the expansion.
339         */
340        while let Some(face_id) = self.heap.pop() {
341            // Create new faces.
342            let face = self.faces[face_id.id].clone();
343
344            if face.deleted {
345                continue;
346            }
347
348            let cso_point = CSOPoint::from_shapes(pos12, g1, g2, &face.normal);
349            let support_point_id = self.vertices.len();
350            self.vertices.push(cso_point);
351
352            let candidate_max_dist = cso_point.point.coords.dot(&face.normal);
353
354            if candidate_max_dist < max_dist {
355                best_face_id = face_id;
356                max_dist = candidate_max_dist;
357            }
358
359            let curr_dist = -face_id.neg_dist;
360
361            if max_dist - curr_dist < _eps_tol ||
362                // Accept the intersection as the algorithm is stuck and no new points will be found
363                // This happens because of numerical stability issue
364                ((curr_dist - old_dist).abs() < _eps && candidate_max_dist < max_dist)
365            {
366                let best_face = &self.faces[best_face_id.id];
367                let points = best_face.closest_points(&self.vertices);
368                return Some((points.0, points.1, best_face.normal));
369            }
370
371            old_dist = curr_dist;
372
373            self.faces[face_id.id].deleted = true;
374
375            let adj_opp_pt_id1 = self.faces[face.adj[0]].next_ccw_pt_id(face.pts[0]);
376            let adj_opp_pt_id2 = self.faces[face.adj[1]].next_ccw_pt_id(face.pts[1]);
377            let adj_opp_pt_id3 = self.faces[face.adj[2]].next_ccw_pt_id(face.pts[2]);
378
379            self.compute_silhouette(support_point_id, face.adj[0], adj_opp_pt_id1);
380            self.compute_silhouette(support_point_id, face.adj[1], adj_opp_pt_id2);
381            self.compute_silhouette(support_point_id, face.adj[2], adj_opp_pt_id3);
382
383            let first_new_face_id = self.faces.len();
384
385            if self.silhouette.is_empty() {
386                // TODO: Something went very wrong because we failed to extract a silhouette…
387                return None;
388            }
389
390            for edge in &self.silhouette {
391                if !self.faces[edge.face_id].deleted {
392                    let new_face_id = self.faces.len();
393
394                    let face_adj = &mut self.faces[edge.face_id];
395                    let pt_id1 = face_adj.pts[(edge.opp_pt_id + 2) % 3];
396                    let pt_id2 = face_adj.pts[(edge.opp_pt_id + 1) % 3];
397
398                    let pts = [pt_id1, pt_id2, support_point_id];
399                    let adj = [edge.face_id, new_face_id + 1, new_face_id - 1];
400                    let new_face = Face::new(&self.vertices, pts, adj);
401
402                    face_adj.adj[(edge.opp_pt_id + 1) % 3] = new_face_id;
403
404                    self.faces.push(new_face.0);
405
406                    if new_face.1 {
407                        let pt = self.vertices[self.faces[new_face_id].pts[0]].point.coords;
408                        let dist = self.faces[new_face_id].normal.dot(&pt);
409                        if dist < curr_dist {
410                            // TODO: if we reach this point, there were issues due to
411                            // numerical errors.
412                            let points = face.closest_points(&self.vertices);
413                            return Some((points.0, points.1, face.normal));
414                        }
415
416                        self.heap.push(FaceId::new(new_face_id, -dist)?);
417                    }
418                }
419            }
420
421            if first_new_face_id == self.faces.len() {
422                // Something went very wrong because all the edges
423                // from the silhouette belonged to deleted faces.
424                return None;
425            }
426
427            self.faces[first_new_face_id].adj[2] = self.faces.len() - 1;
428            self.faces.last_mut().unwrap().adj[1] = first_new_face_id;
429
430            self.silhouette.clear();
431            // self.check_topology(); // NOTE: for debugging only.
432
433            niter += 1;
434            if niter > 100 {
435                // if we reached this point, our algorithm didn't converge to what precision we wanted.
436                // still return an intersection point, as it's probably close enough.
437                break;
438            }
439        }
440
441        let best_face = &self.faces[best_face_id.id];
442        let points = best_face.closest_points(&self.vertices);
443        Some((points.0, points.1, best_face.normal))
444    }
445
446    fn compute_silhouette(&mut self, point: usize, id: usize, opp_pt_id: usize) {
447        if !self.faces[id].deleted {
448            if !self.faces[id].can_be_seen_by(&self.vertices, point, opp_pt_id) {
449                self.silhouette.push(SilhouetteEdge::new(id, opp_pt_id));
450            } else {
451                self.faces[id].deleted = true;
452
453                let adj_pt_id1 = (opp_pt_id + 2) % 3;
454                let adj_pt_id2 = opp_pt_id;
455
456                let adj1 = self.faces[id].adj[adj_pt_id1];
457                let adj2 = self.faces[id].adj[adj_pt_id2];
458
459                let adj_opp_pt_id1 =
460                    self.faces[adj1].next_ccw_pt_id(self.faces[id].pts[adj_pt_id1]);
461                let adj_opp_pt_id2 =
462                    self.faces[adj2].next_ccw_pt_id(self.faces[id].pts[adj_pt_id2]);
463
464                self.compute_silhouette(point, adj1, adj_opp_pt_id1);
465                self.compute_silhouette(point, adj2, adj_opp_pt_id2);
466            }
467        }
468    }
469
470    #[allow(dead_code)]
471    #[cfg(feature = "std")]
472    fn print_silhouette(&self) {
473        use std::{print, println};
474
475        print!("Silhouette points: ");
476        for i in 0..self.silhouette.len() {
477            let edge = &self.silhouette[i];
478            let face = &self.faces[edge.face_id];
479
480            if !face.deleted {
481                print!(
482                    "({}, {}) ",
483                    face.pts[(edge.opp_pt_id + 2) % 3],
484                    face.pts[(edge.opp_pt_id + 1) % 3]
485                );
486            }
487        }
488        println!();
489    }
490
491    #[allow(dead_code)]
492    fn check_topology(&self) {
493        for i in 0..self.faces.len() {
494            let face = &self.faces[i];
495            if face.deleted {
496                continue;
497            }
498
499            // println!("checking {}-th face.", i);
500            let adj1 = &self.faces[face.adj[0]];
501            let adj2 = &self.faces[face.adj[1]];
502            let adj3 = &self.faces[face.adj[2]];
503
504            assert!(!adj1.deleted);
505            assert!(!adj2.deleted);
506            assert!(!adj3.deleted);
507
508            assert!(face.pts[0] != face.pts[1]);
509            assert!(face.pts[0] != face.pts[2]);
510            assert!(face.pts[1] != face.pts[2]);
511
512            assert!(adj1.contains_point(face.pts[0]));
513            assert!(adj1.contains_point(face.pts[1]));
514
515            assert!(adj2.contains_point(face.pts[1]));
516            assert!(adj2.contains_point(face.pts[2]));
517
518            assert!(adj3.contains_point(face.pts[2]));
519            assert!(adj3.contains_point(face.pts[0]));
520
521            let opp_pt_id1 = adj1.next_ccw_pt_id(face.pts[0]);
522            let opp_pt_id2 = adj2.next_ccw_pt_id(face.pts[1]);
523            let opp_pt_id3 = adj3.next_ccw_pt_id(face.pts[2]);
524
525            assert!(!face.contains_point(adj1.pts[opp_pt_id1]));
526            assert!(!face.contains_point(adj2.pts[opp_pt_id2]));
527            assert!(!face.contains_point(adj3.pts[opp_pt_id3]));
528
529            assert!(adj1.adj[(opp_pt_id1 + 1) % 3] == i);
530            assert!(adj2.adj[(opp_pt_id2 + 1) % 3] == i);
531            assert!(adj3.adj[(opp_pt_id3 + 1) % 3] == i);
532        }
533    }
534}