Skip to main content

linear_sim/collision/proximity/
gjk.rs

1use sorted_vec::partial::ReverseSortedVec;
2
3use crate::object;
4use crate::math::*;
5
6use super::Proximity;
7
8#[derive(Clone, Copy, Debug, PartialEq)]
9struct SimplexMinkowski {
10  pub points  : [PointMinkowski; 4],
11  pub npoints : u8
12}
13
14/// Represents a point in the Minkowski difference A - B of shapes A and B
15#[derive(Clone, Copy, Debug, Default, PartialEq)]
16struct PointMinkowski {
17  pub point_a : Point3 <f64>,
18  pub point_b : Point3 <f64>
19  // TODO: benchmark if it's more performant to pre-compute point
20  //pub point  : Point3 <f64>
21}
22
23impl SimplexMinkowski {
24  const fn push (&mut self, point : PointMinkowski) {
25    self.points[self.npoints as usize] = point;
26    self.npoints += 1;
27  }
28}
29
30impl PointMinkowski {
31  /// Point A - point B
32  fn point (self) -> Point3 <f64> {
33    Point3 (self.point_a - self.point_b)
34  }
35
36  fn support <A, B> (direction : NonZero3 <f64>, object_a : &A, object_b : &B)
37    -> (Self, f64)
38  where A : object::Bounded, B : object::Bounded {
39    let (point_a, _) = object_a.to_primitive().support (direction);
40    let (point_b, _) = object_b.to_primitive().support (-direction);
41    let minkowski = PointMinkowski { point_a, point_b };
42    (minkowski, minkowski.point().0.dot (*direction))
43  }
44}
45
46impl Proximity {
47  /// Return the proximity query using GJK algorithm.
48  pub fn query_gjk <A, B> (object_a : &A, object_b : &B) -> Self where
49    A : object::Bounded, B : object::Bounded
50  {
51    // query_gjk function outline:
52    // 1. main loop: gjk search for simplex containing the origin in minkowski hull
53    // 2. if contained (overlapping):
54    //      compute the penetration depth
55    //    else (not overlapping):
56    //      determine nearest features and calculate distance
57    const TOLERANCE         : f64 = 0.000_000_000_01;
58    const TOLERANCE_SQUARED : f64 = TOLERANCE * TOLERANCE;
59    // initial search direction
60    let mut search_dir = NonZero3::unchecked (vector3 (1.0, 1.0, 1.0));
61    let (support, _) = PointMinkowski::support (search_dir, object_a, object_b);
62    let mut near = support.point();
63    let mut distance_squared = near.0.magnitude_squared();
64    let mut lowest_distance_sq = distance_squared;
65    let mut simplex = SimplexMinkowski {
66      points:  [support,
67        PointMinkowski::default(), PointMinkowski::default(), PointMinkowski::default()],
68      npoints: 1
69    };
70    // new search direction towards the origin
71    // TODO: is this safe ?
72    search_dir = -NonZero3::unchecked (support.point().0);
73    let mut lowest_unique_count = 0;
74    let mut last_distance_sq = f64::MAX;
75    let noprogress_limit = 3;
76    let mut noprogress_count = 0;
77    let mut unique_count = 0;
78    loop {
79      let (support, _) = PointMinkowski::support (search_dir, object_a, object_b);
80      // vector from last nearest point to new support point
81      let vnew = support.point() - near;
82      let dot_new = vnew.dot (*search_dir);
83      if dot_new <= TOLERANCE {
84        // no intersection
85        break
86      }
87      // add new support point and check for overlap
88      simplex.push (support);
89      let mut overlap = false;
90      if simplex.npoints == 4 {
91        // check for tetrahedron containment, drop point to create face if no containment
92        let [a, b, c, d] = simplex.points.map (PointMinkowski::point);
93        // check to see if origin is contained
94        let ab = b - a;
95        let ac = c - a;
96        let ad = d - a;
97        let da = a - d;
98        let db = b - d;
99        let dc = c - d;
100        let ab_x_ac = ab.cross (ac);
101        let ab_x_ac_dot_ad = ab_x_ac.dot (ad);
102        let nd = -d.0;
103        let (dab, dac, dbc) = if ab_x_ac_dot_ad > 0.0 {
104          // winding ab x ac
105          ( db.cross (da),
106            da.cross (dc),
107            dc.cross (db) )
108        } else {
109          // winding ac x ab
110          ( da.cross (db),
111            dc.cross (da),
112            db.cross (dc) )
113        };
114        // containment checks
115        let check1 = dab.dot (nd) > 0.000000001;
116        let check2 = dac.dot (nd) > 0.000000001;
117        let check3 = dbc.dot (nd) > 0.000000001;
118        if check1 && check2 && check3 {
119          overlap = true
120        } else {
121          // otherwise intersect the base perpendicular (near) with the new faces
122          let line = geometry::Segment3::noisy (Point3::<f64>::origin(), near).into();
123          // face 1 = dab
124          if let Some (triangle) = geometry::Triangle3::new (d, a, b)
125            && geometry::intersect::line_triangle (line, triangle).is_some()
126          {
127            // drop c
128            simplex.points[2] = simplex.points[3];
129          } else {
130            // face 2 = dac
131            if let Some (triangle) = geometry::Triangle3::new (d, a, c)
132              && geometry::intersect::line_triangle (line, triangle).is_some()
133            {
134              // drop b
135              simplex.points[1] = simplex.points[3];
136            } else {
137              // face 3 = dbc
138              if let Some (triangle) = geometry::Triangle3::new (d, b, c)
139                && geometry::intersect::line_triangle (line, triangle).is_some()
140              {
141                // drop a
142                simplex.points[0] = simplex.points[3];
143              } else {
144                unreachable!("should have intersected new simplex")
145              }
146            }
147          }
148          simplex.npoints = 3;
149        }
150      }
151      if !overlap {
152        // get new search direction
153        match simplex.npoints {
154          1 => {
155            near = simplex.points[0].point();
156            distance_squared = near.0.magnitude_squared();
157            if distance_squared < TOLERANCE_SQUARED {
158              overlap = true
159            } else {
160              // new search direction assigned to inverse of this point, i.e. towards origin
161              search_dir = NonZero3::new (-near.0).unwrap()
162            }
163          }
164          2 => {
165            let t;
166            (t, near) = geometry::Segment3::unchecked (
167              simplex.points[0].point(), simplex.points[1].point()
168            ).nearest_point (Point3::origin());
169            distance_squared = near.0.magnitude_squared();
170            if distance_squared < TOLERANCE_SQUARED {
171              overlap = true
172            } else if *t == 1.0 {
173              // point 1 becomes the new simplex, new direction point 1 towards origin
174              simplex.points[0] = simplex.points[1];
175              simplex.npoints -= 1;
176              search_dir = NonZero3::unchecked (-simplex.points[0].point().0)
177            } else {
178              // new direction edge normal towards origin which is equal to -near
179              search_dir = NonZero3::unchecked (-near.0)
180            }
181          }
182          3 => {
183            let (s, t);
184            ([s, t], near) = geometry::Triangle3::unchecked (
185              simplex.points[0].point(),
186              simplex.points[1].point(),
187              simplex.points[2].point()
188            ).nearest_point (Point3::origin());
189            distance_squared = near.0.magnitude_squared();
190            if distance_squared < TOLERANCE_SQUARED {
191              overlap = true
192            } else {
193              // new search direction
194              search_dir = NonZero3::unchecked (-near.0);
195              if *s == 0.0 && *t == 0.0 {
196                // point A, search direction -A
197                simplex.npoints = 1;
198              } else if *s == 1.0 && *t == 0.0 {
199                // point B, search direction -B
200                simplex.points[0] = simplex.points[1];
201                simplex.npoints = 1;
202              } else if *s == 0.0 && *t == 1.0 {
203                // point C, search direction -C
204                simplex.points[0] = simplex.points[2];
205                simplex.npoints = 1;
206              } else if (*s + *t - 1.0).abs() < TOLERANCE {
207                // edge BC, search direction BC x BO x BC
208                simplex.points[0] = simplex.points[1];
209                simplex.points[1] = simplex.points[2];
210                simplex.npoints = 2;
211              } else if *s < TOLERANCE {
212                // edge AC, search direction AC x AO x AC
213                simplex.points[1] = simplex.points[2];
214                simplex.npoints = 2;
215              } else if *t < TOLERANCE {
216                // edge AB, search direction AB x AO x AB
217                simplex.npoints = 2;
218              } else {
219                // triangle ABC, direction face normal to origin
220                debug_assert!(*s > 0.0);
221                debug_assert!(*s < 1.0);
222                debug_assert!(*t > 0.0);
223                debug_assert!(*t < 1.0);
224                debug_assert!(*s + *t < 1.0);
225              }
226            }
227          }
228          _ => unreachable!()
229        }
230      }
231      if overlap {
232        // compute penetration
233        /// Represents distance from origin to a face in the polytope
234        #[derive(PartialEq)]
235        struct PolytopeFace {
236          /// Distance squared from origin
237          pub distance_squared : NonNegative <f64>,
238          /// Distance vector from origin
239          pub distance_vector  : Vector3 <f64>,
240          pub nearest_a        : Point3 <f64>,
241          pub nearest_b        : Point3 <f64>,
242          pub points           : [PointMinkowski; 3],
243          pub normal           : NonZero3 <f64>,
244          pub exterior         : bool
245        }
246        impl PartialOrd for PolytopeFace {
247          fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
248            self.distance_squared.partial_cmp (&other.distance_squared)
249          }
250        }
251        let mut node_list = ReverseSortedVec::<PolytopeFace>::new();
252        let add_face = |
253          node_list : &mut ReverseSortedVec <PolytopeFace>,
254          p1 : PointMinkowski, p2 : PointMinkowski, p3 : PointMinkowski
255        | {
256          let triangle = geometry::Triangle3::noisy (p1.point(), p2.point(), p3.point());
257          let ([s, t], nearest) = triangle.nearest_point (Point3::origin());
258          let nearest_a = p1.point_a +
259            (p2.point_a - p1.point_a) * *s + (p3.point_a - p1.point_a) * *t;
260          let nearest_b = p1.point_b +
261            (p2.point_b - p1.point_b) * *s + (p3.point_b - p1.point_b) * *t;
262          let normal = {
263            let mut normal = triangle.normal();
264            if normal.dot (triangle.point_a().0) < 0.0 {
265              normal = -normal
266            }
267            normal
268          };
269          let node = PolytopeFace {
270            distance_squared: nearest.0.norm_squared(),
271            distance_vector:  -nearest.0,
272            points:           [p1, p2, p3],
273            exterior:         false,
274            nearest_a,
275            nearest_b,
276            normal
277          };
278          node_list.insert (node);
279        };
280        let a  = simplex.points[0];
281        let b  = simplex.points[1];
282        let c  = simplex.points[2];
283        let d  = simplex.points[3];
284        match simplex.npoints {
285          3 => {
286            // add support points in direction of face normals
287            let e1 = simplex.points[1].point() - simplex.points[0].point();
288            let e2 = simplex.points[2].point() - simplex.points[0].point();
289            let n1 = NonZero3::noisy (e1.cross (e2));
290            let n2 = -n1;
291            // p1
292            let (p1, dot) = PointMinkowski::support (n1, object_a, object_b);
293            if dot <= TOLERANCE {
294              let half_axis = *n1 * TOLERANCE * 0.5;
295              return Proximity {
296                distance:  -TOLERANCE.sqrt(),
297                normal:    n1.normalize(),
298                midpoint:  p1.point_a + half_axis,
299                half_axis
300              }
301            }
302            add_face (&mut node_list, a, b, p1);
303            add_face (&mut node_list, a, c, p1);
304            add_face (&mut node_list, b, c, p1);
305            // p2
306            let (p2, dot) = PointMinkowski::support (n2, object_a, object_b);
307            if dot <= TOLERANCE {
308              let half_axis = *n2 * TOLERANCE * 0.5;
309              return Proximity {
310                distance:  -TOLERANCE.sqrt(),
311                normal:    n2.normalize(),
312                midpoint:  p2.point_a + half_axis,
313                half_axis
314              }
315            }
316            add_face (&mut node_list, a, b, p2);
317            add_face (&mut node_list, a, c, p2);
318            add_face (&mut node_list, b, c, p2);
319          }
320          4 => {
321            add_face (&mut node_list, a, b, c);
322            add_face (&mut node_list, a, b, d);
323            add_face (&mut node_list, a, c, d);
324            add_face (&mut node_list, b, c, d);
325          }
326          _ => unreachable!()
327        }
328        // expanding polytope search loop
329        // TODO: for improved efficiency it may be desirable to fix winding for all
330        // faces in a CW or CCW direction so that shared edges are always in opposite
331        // directions and therefore only one edge direction needs to be checked when
332        // adding to the edge array when creating a hole for the new support point
333        let mut edges = vec![];
334        let mut edge_in_list = vec![];
335        let mut last_iter_distance = f64::MIN;
336        let exterior_node_proximity = |node : PolytopeFace| {
337          let half_axis = node.distance_vector * 0.5;
338          Proximity {
339            half_axis,
340            distance:  -*node.distance_squared.sqrt(),
341            normal:    half_axis.normalize(), // TODO: verify correct ?
342            midpoint:  node.nearest_a + half_axis
343          }
344        };
345        let mut iter = 0;
346        let noprogress_limit = 3;
347        let mut noprogress_count = 0;
348        loop {
349          let mut node = node_list.pop().unwrap();
350          if approx::relative_eq!(*node.distance_squared, last_iter_distance,
351            epsilon = 0.000000001
352          ) {
353            // no progress
354            noprogress_count += 1;
355            if noprogress_count == noprogress_limit {
356              log::trace!("expanding polytope search no progress after {iter} iterations");
357              return exterior_node_proximity (node)
358            }
359          } else {
360            noprogress_count = 0;
361          }
362          last_iter_distance = *node.distance_squared;
363          // calculate support point for face normal
364          let (pa, _) = PointMinkowski::support (node.normal, object_a, object_b);
365          if node.points.contains (&pa) {
366            // support point already in face
367            node.exterior = true;
368          } else {
369            // exterior face check
370            let bpa = pa.point() - node.points[0].point();
371            // TODO: is it necessary to normalize here?
372            let normal_unit = node.normal.normalize();
373            if normal_unit.dot (bpa) < TOLERANCE {
374              node.exterior = true;
375            }
376          }
377          if node.exterior {
378            // exit if face is exterior
379            return exterior_node_proximity (node)
380          } else {
381            // face is not exterior:
382            // insert node edges into edge list
383            edges.clear();
384            edge_in_list.clear();
385            edges.extend ([
386              [node.points[0], node.points[1]],
387              [node.points[0], node.points[2]],
388              [node.points[1], node.points[2]]
389            ]);
390            edge_in_list.extend ([true, true, true]);
391            // remove all faces visible to new support point and fill holes with new
392            // faces
393            node_list.retain (|node|{
394              let p0_pa = pa.point() - node.points[0].point();
395              if TOLERANCE < node.normal.dot (p0_pa) {
396                // pa visible: add edges of this face to edge array
397                let mut add_edge = |i : usize, j : usize| {
398                  let mut found = false;
399                  // filter: skip excluded edges
400                  for (k, edge) in edges.iter().enumerate()
401                    .filter (|(k, _)| edge_in_list[*k])
402                  {
403                    if node.points[i].point() == edge[0].point()
404                      && node.points[j].point() == edge[1].point()
405                      || node.points[i].point() == edge[1].point()
406                      && node.points[j].point() == edge[0].point()
407                    {
408                      found = true;
409                      edge_in_list[k] = false;  // exclude shared edge
410                      break
411                    }
412                  }
413                  if !found {
414                    // insert
415                    edge_in_list.push (true);
416                    edges.push ([node.points[i], node.points[j]]);
417                  }
418                };
419                add_edge (0, 1); // edge0: 0->1
420                add_edge (0, 2); // edge1: 0->2
421                add_edge (1, 2); // edge2: 1->2
422                false // remove
423              } else {
424                true  // retain
425              }
426            });
427            // add new faces connected to support point and "hole" edges in edge list
428            // filter: skip excluded edges
429            for (_, edge) in edges.iter().enumerate()
430              .filter (|(i, _)| edge_in_list[*i])
431            {
432              if geometry::Triangle3::new (pa.point(), edge[0].point(), edge[1].point())
433                .is_none()
434              {
435                // skip faces where points are colinear
436                continue
437              }
438              add_face (&mut node_list, pa, edge[0], edge[1]);
439            }
440          }
441          iter += 1;
442        }
443      } // end penetration
444      // count unique points
445      if simplex.points[0].point_a == simplex.points[1].point_a
446        && simplex.points[1].point_a == simplex.points[2].point_a
447      {
448        unique_count += 1;
449      } else if simplex.points[0].point_a == simplex.points[1].point_a
450        || simplex.points[0].point_a == simplex.points[2].point_a
451        || simplex.points[1].point_a == simplex.points[2].point_a
452      {
453        unique_count += 2;
454      } else {
455        debug_assert!(
456          simplex.points[0].point_a != simplex.points[1].point_a &&
457          simplex.points[0].point_a != simplex.points[2].point_a &&
458          simplex.points[1].point_a != simplex.points[2].point_a);
459        unique_count += 3;
460      }
461      if simplex.points[0].point_b == simplex.points[1].point_b
462        && simplex.points[1].point_b == simplex.points[2].point_b
463      {
464        unique_count += 1;
465      } else if simplex.points[0].point_b == simplex.points[1].point_b
466        || simplex.points[0].point_b == simplex.points[2].point_b
467        || simplex.points[1].point_b == simplex.points[2].point_b
468      {
469        unique_count += 2;
470      } else {
471        debug_assert!(
472          simplex.points[0].point_b != simplex.points[1].point_b &&
473          simplex.points[0].point_b != simplex.points[2].point_b &&
474          simplex.points[1].point_b != simplex.points[2].point_b);
475        unique_count += 3;
476      }
477      // new lowest
478      if distance_squared < lowest_distance_sq ||
479        distance_squared == lowest_distance_sq && unique_count < lowest_unique_count
480      {
481        lowest_distance_sq = distance_squared;
482        lowest_unique_count = unique_count;
483      } else if distance_squared >= last_distance_sq {
484        if noprogress_count < noprogress_limit {
485          noprogress_count += 1;
486        } else {
487          // no progress limit reached: break
488          break
489        }
490      }
491      last_distance_sq = distance_squared;
492    }
493    // distance squared to nearest feature
494    // check duplicate vertices
495    let mut dupe_a_01 = false;
496    let mut dupe_a_02 = false;
497    let mut dupe_a_12 = false;
498    let mut dupe_b_01 = false;
499    let mut dupe_b_02 = false;
500    let mut dupe_b_12 = false;
501    if simplex.npoints >= 2 {
502      dupe_a_01 = simplex.points[0].point_a == simplex.points[1].point_a;
503      dupe_b_01 = simplex.points[0].point_b == simplex.points[1].point_b;
504    }
505    if simplex.npoints >= 3 {
506      dupe_a_02 = simplex.points[0].point_a == simplex.points[2].point_a;
507      dupe_a_12 = simplex.points[1].point_a == simplex.points[2].point_a;
508      dupe_b_02 = simplex.points[0].point_b == simplex.points[2].point_b;
509      dupe_b_12 = simplex.points[1].point_b == simplex.points[2].point_b;
510    }
511    // fix some degenerate cases
512    // simplex.points[0].point() == simplex.points[1].point()
513    debug_assert!(!(simplex.npoints == 2 && unique_count == 2),
514      "only one unique point: not possible");
515    // calculate final distance and nearest points
516    match simplex.npoints {
517      1 => {
518        // point simplex
519        let nearest_a = simplex.points[0].point_a;
520        let nearest_b = simplex.points[0].point_b;
521        let a_to_b = nearest_b - nearest_a;
522        let half_axis = a_to_b * 0.5;
523        let midpoint = nearest_a + half_axis;
524        let normal = if !a_to_b.is_approx_zero() {
525          Unit3::normalize (-a_to_b)
526        } else {
527          // points are touching: use object centers
528          Unit3::normalize (object_a.position().0 - object_b.position().0)
529        };
530        let distance = a_to_b.magnitude();
531        Proximity { distance, half_axis, midpoint, normal }
532      }
533      2 => {
534        // line simplex
535        let point0_a = simplex.points[0].point_a;
536        let point1_a = simplex.points[1].point_a;
537        let point0_b = simplex.points[0].point_b;
538        let point1_b = simplex.points[1].point_b;
539        if !dupe_a_01 && !dupe_b_01 {
540          // edge vs edge
541          let edge_a = geometry::Segment3::noisy (point0_a, point1_a);
542          let edge_b = geometry::Segment3::noisy (point0_b, point1_b);
543          Proximity::query_segment_segment (&edge_a, &edge_b)
544        } else {
545          // point vs edge
546          if dupe_a_01 {
547            // point in A vs edge in B
548            debug_assert!(!dupe_b_01);
549            debug_assert_eq!(point0_a, point1_a);
550            let point_a = point0_a;
551            let edge_b  = geometry::Segment3::noisy (point0_b, point1_b);
552            // flip direction so proximity query points from A -> B
553            Proximity::query_segment_point (&edge_b, &point_a).flip()
554          } else {
555            // point in B vs edge in A
556            debug_assert!(dupe_b_01);
557            let point_b = point0_b;
558            let edge_a  = geometry::Segment3::noisy (point0_a, point1_a);
559            Proximity::query_segment_point (&edge_a, &point_b)
560          }
561        }
562      }
563      3 => {
564        // triangle simplex
565        let is_edge = |
566          p0 : &mut Point3 <f64>, p1 : &mut Point3 <f64>, p2 : &Point3 <f64>
567        | {
568          let p01 = *p1 - *p0;
569          let p02 = *p2 - *p0;
570          let p01_cross_p02 = p01.cross (p02);
571          if p01_cross_p02.is_approx_zero() {
572            // points are colinear
573            let p12 = *p2 - *p1;
574            let dot_p01 = p01.magnitude_squared();
575            let dot_p02 = p02.magnitude_squared();
576            let dot_p12 = p12.magnitude_squared();
577            // swap so that longest edge is always p0->p1
578            if dot_p01 > dot_p02 && dot_p01 > dot_p12 {
579              // do nothing: already p0->p1
580            } else if dot_p02 > dot_p01 && dot_p02 > dot_p12 {
581              *p1 = *p2;
582            } else {
583              debug_assert!(dot_p12 > dot_p01 && dot_p12 > dot_p02);
584              *p0 = *p1;
585              *p1 = *p2;
586            }
587            true
588          } else {
589            false
590          }
591        };
592        // point vs point not possible
593        debug_assert!(!((dupe_a_01 && dupe_a_12) && (dupe_b_01 && dupe_b_12)));
594        if (!dupe_a_01 && !dupe_a_02 && !dupe_a_12) &&
595           (!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
596        {
597          // face vs face
598          let mut a0 = simplex.points[0].point_a;
599          let mut a1 = simplex.points[1].point_a;
600          let a2 = simplex.points[2].point_a;
601          let mut b0 = simplex.points[0].point_b;
602          let mut b1 = simplex.points[1].point_b;
603          let b2 = simplex.points[2].point_b;
604          let edge_a = is_edge (&mut a0, &mut a1, &a2);
605          let edge_b = is_edge (&mut b0, &mut b1, &b2);
606          if edge_a && edge_b {
607            // edge vs edge
608            let edge_a = geometry::Segment3::noisy (a0, a1);
609            let edge_b = geometry::Segment3::noisy (b0, b1);
610            Proximity::query_segment_segment (&edge_a, &edge_b)
611          } else if edge_a && !edge_b {
612            // edge vs face
613            let edge_a = geometry::Segment3::noisy (a0, a1);
614            let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
615            // flip direction so proximity query points from A -> B
616            Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
617          } else if !edge_a && edge_b {
618            // face vs edge
619            let edge_b = geometry::Segment3::noisy (b0, b1);
620            let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
621            Proximity::query_triangle_segment (&triangle_a, &edge_b)
622          } else {
623            debug_assert!(!edge_a);
624            debug_assert!(!edge_b);
625            // face vs face
626            let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
627            let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
628            Proximity::query_triangle_triangle (&triangle_a, &triangle_b)
629          }
630        } else if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
631          && (!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
632        {
633          // edge vs face
634          // need to look at distance_sq between edge and face in objects: there is
635          // probably not a general way to get the contact points from the simplex as
636          // the 3-simplex doesn't fully determine the intersection of an edge+face this
637          // code makes use of a segment/triangle distance_sq function from eberly
638          // geometrictools which is probably slower than the other (simpler) cases
639          let a0 = simplex.points[0].point_a;
640          let mut a1 = simplex.points[1].point_a;
641          let mut b0 = simplex.points[0].point_b;
642          let mut b1 = simplex.points[1].point_b;
643          let b2 = simplex.points[2].point_b;
644          if dupe_a_01 || dupe_a_12 {
645            a1 = simplex.points[2].point_a;
646          }
647          let edge_b = is_edge (&mut b0, &mut b1, &b2);
648          let edge_a = geometry::Segment3::noisy (a0, a1);
649          if edge_b {
650            // edge vs edge
651            let edge_b = geometry::Segment3::noisy (b0, b1);
652            Proximity::query_segment_segment (&edge_a, &edge_b)
653          } else {
654            // edge vs triangle
655            let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
656            // flip direction so proximity query points from A -> B
657            Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
658          }
659        } else if (dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12)
660          && (!dupe_a_01 && !dupe_a_02 && !dupe_a_12)
661        {
662          // face vs edge
663          let mut a0 = simplex.points[0].point_a;
664          let mut a1 = simplex.points[1].point_a;
665          let a2 = simplex.points[2].point_a;
666          let b0 = simplex.points[0].point_b;
667          let b1 = if dupe_b_01 || dupe_b_12 {
668            simplex.points[2].point_b
669          } else {
670            simplex.points[1].point_b
671          };
672          let edge_a = is_edge (&mut a0, &mut a1, &a2);
673          if edge_a {
674            // edge vs edge
675            let edge_a = geometry::Segment3::noisy (a0, a1);
676            let edge_b = geometry::Segment3::noisy (b0, b1);
677            Proximity::query_segment_segment (&edge_a, &edge_b)
678          } else {
679            // triangle vs edge
680            let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
681            let edge_b = geometry::Segment3::noisy (b0, b1);
682            Proximity::query_triangle_segment (&triangle_a, &edge_b)
683          }
684        } else {
685          if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
686            && !(dupe_a_01 && dupe_a_02 && dupe_a_12)  // XOR of 3 values
687          {
688            // edge vs edge
689            // B also has exactly one duplicate pair
690            debug_assert!(dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12);
691            debug_assert!(!(dupe_b_01 && dupe_b_02 && dupe_b_12));
692            debug_assert!(!(dupe_a_01 && dupe_b_01));
693            debug_assert!(!(dupe_a_02 && dupe_b_02));
694            debug_assert!(!(dupe_a_12 && dupe_b_12));
695            // before making the distance call, the base point of the triangle simplex
696            // has to be the intersection of the 'duplicate' points in each object and
697            // the edge 01 of the simplex triangle should be the edge corresponding to
698            // the edge in A, and likewise edge 02 for B
699            if (dupe_a_01 != dupe_a_02) && (dupe_b_01 != dupe_b_02) {
700              // 0 -> 0
701              if dupe_a_02 {
702                // 1 -> 1
703                // 2 -> 2
704                debug_assert!(dupe_b_01);
705                // no action needed
706              } else {
707                // 1 -> 2
708                // 2 -> 1
709                debug_assert!(dupe_a_01);
710                debug_assert!(dupe_b_02);
711                simplex.points.swap(1, 2);
712              }
713            } else if (dupe_a_01 != dupe_a_12) && (dupe_b_01 != dupe_b_12) {
714              // 0 -> 1
715              let temp = simplex.points[0];
716              simplex.points[0] = simplex.points[1];
717              if dupe_a_01 {
718                // 1 -> 2
719                // 2 -> 0
720                debug_assert!(dupe_b_12);
721                simplex.points[1] = simplex.points[2];
722                simplex.points[2] = temp;
723              } else {
724                // 1 -> 0
725                // 2 -> 2
726                debug_assert!(dupe_a_12);
727                debug_assert!(dupe_b_01);
728                simplex.points[1] = temp;
729              }
730            } else {
731              // 0 -> 2
732              debug_assert!(dupe_a_02 != dupe_a_12);
733              debug_assert!(dupe_b_02 != dupe_b_12);
734              let temp = simplex.points[0];
735              simplex.points[0] = simplex.points[2];
736              if dupe_a_02 {
737                // 1 -> 1
738                // 2 -> 0
739                debug_assert!(dupe_b_12);
740                simplex.points[2] = temp;
741              } else {
742                // 1 -> 0
743                // 2 -> 1
744                debug_assert!(dupe_a_12);
745                debug_assert!(dupe_b_02);
746                simplex.points[2] = simplex.points[1];
747                simplex.points[1] = temp;
748              }
749            }
750            let a0 = simplex.points[0].point_a;
751            let a1 = simplex.points[1].point_a;
752            let b0 = simplex.points[0].point_b;
753            // somewhat confusing, but edge B is always along simplex points 0->2
754            let b2 = simplex.points[2].point_b;
755            let edge_a = geometry::Segment3::noisy (a0, a1);
756            let edge_b = geometry::Segment3::noisy (b0, b2);
757            Proximity::query_segment_segment (&edge_a, &edge_b)
758          } else {
759            // point vs face
760            // XOR: point in one of the objects but not both
761            debug_assert!((dupe_a_01 && dupe_a_12) != (dupe_b_01 && dupe_b_12));
762            if dupe_a_01 && dupe_a_12 {
763              // point in A, face in B
764              debug_assert!(dupe_a_02); // implied
765              let point_a = simplex.points[0].point_a;
766              let b0 = simplex.points[0].point_b;
767              let b1 = simplex.points[1].point_b;
768              let b2 = simplex.points[2].point_b;
769              let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
770              // flip direction so proximity query points from A -> B
771              Proximity::query_triangle_point (&triangle_b, &point_a).flip()
772            } else {
773              // point in B, face in A
774              debug_assert!(dupe_b_01 && dupe_b_12);
775              debug_assert!(dupe_b_02); // implied
776              let point_b = simplex.points[0].point_b;
777              let a0 = simplex.points[0].point_a;
778              let a1 = simplex.points[1].point_a;
779              let a2 = simplex.points[2].point_a;
780              let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
781              Proximity::query_triangle_point (&triangle_a, &point_b)
782            }
783          }
784        }
785      }
786      _ => unreachable!()
787    }
788  }
789}
790
791#[cfg(test)]
792mod tests {
793  use approx;
794  use gl_utils::{mesh, Mesh};
795  use rand;
796  use rand_distr;
797  use rand_xorshift::XorShiftRng;
798  use crate::math::geometry::*;
799  use crate::component;
800  use super::*;
801
802  #[test]
803  fn query_gjk() {
804    use rand::SeedableRng;
805    use rand_distr::Distribution;
806
807    let tetrahedron = |p1 : [f64; 3], p2 : [f64; 3], p3 : [f64; 3], p4 : [f64; 3]|
808      -> (component::Position, component::Bound)
809    {
810      ( component::Position::origin(),
811        Hull3::from_points_with_mesh (
812          &simplex3::Tetrahedron::<f64>::noisy (
813            p1.into(), p2.into(), p3.into(), p4.into()
814          ).points()
815        ).unwrap().into()
816      )
817    };
818    let a = tetrahedron (
819      [-2.0,  2.0, -1.0],
820      [ 2.0,  2.0, -1.0],
821      [ 0.0, -2.0, -1.0],
822      [ 0.0,  0.0,  2.0]);
823    let b = tetrahedron (
824      [-2.0,  2.0,  3.5],
825      [ 2.0,  2.0,  3.5],
826      [ 0.0, -2.0,  3.5],
827      [ 0.0,  0.0,  6.5]);
828    let proximity = Proximity::query_gjk (&a, &b);
829    assert_eq!(proximity.distance, 1.5);
830    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.75].into());
831    assert_eq!(proximity.midpoint, [0.0, 0.0, 2.75].into());
832    assert_eq!(proximity.normal, -Unit3::axis_z());
833    let a = tetrahedron (
834      [-2.0,  2.0, -1.0],
835      [ 2.0,  2.0, -1.0],
836      [ 0.0, -2.0, -1.0],
837      [ 0.0,  0.0,  2.0]);
838    let b = tetrahedron (
839      [-2.0,  2.0,  1.75],
840      [ 2.0,  2.0,  1.75],
841      [ 0.0, -2.0,  1.75],
842      [ 0.0,  0.0,  4.75]);
843    let proximity = Proximity::query_gjk (&a, &b);
844    assert_eq!(proximity.distance, -0.25);
845    assert_eq!(proximity.half_axis, [0.0, 0.0, -0.125].into());
846    assert_eq!(proximity.midpoint, [0.0, 0.0, 1.875].into());
847    assert_eq!(proximity.normal, -Unit3::axis_z());
848    let a = tetrahedron (
849      [ 0.0,  0.0,  3.5],
850      [-2.0,  2.0,  0.5],
851      [ 2.0,  2.0,  0.5],
852      [ 0.0, -2.0,  0.5]);
853    let b = tetrahedron (
854      [-2.0,  2.0,  1.0],
855      [ 2.0,  2.0,  1.0],
856      [ 0.0, -2.0,  1.0],
857      [ 0.0,  0.0, -2.0]);
858    let proximity = Proximity::query_gjk (&a, &b);
859    assert_eq!(proximity.distance, -0.5);
860    approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
861    assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
862    approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
863      epsilon = f64::EPSILON * 4.0);
864    let hull = |points : &[Point3 <f64>]| -> (component::Position, component::Bound) {
865      ( component::Position::origin(),
866        Hull3::from_points_with_mesh (points).unwrap().into()
867      )
868    };
869    let a = hull (&[
870      [ 0.0,  0.0,  3.5],
871      [ 0.0,  0.0,  0.5],
872      [-2.0,  2.0,  0.5],
873      [ 2.0,  2.0,  0.5],
874      [ 0.0, -2.0,  0.5]].map (Into::into));
875    let b = hull (&[
876      [-2.0,  2.0,  1.0],
877      [ 2.0,  2.0,  1.0],
878      [ 0.0, -2.0,  1.0],
879      [ 0.0,  0.0,  1.0],
880      [ 0.0,  0.0, -2.0]].map (Into::into));
881    let proximity = Proximity::query_gjk (&a, &b);
882    assert_eq!(proximity.distance, -0.5);
883    approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
884    assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
885    approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
886      epsilon = f64::EPSILON * 4.0);
887
888    {
889      const NPOINTS : usize = 4;
890      let mut rng    = XorShiftRng::seed_from_u64 (0);
891      let cauchy     = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
892      let randf      = |rng : &mut XorShiftRng| cauchy.sample (rng);
893      let aabb = Aabb3::with_minmax (
894        [-5.0, -5.0, -5.0].into(),
895        [ 5.0,  5.0,  5.0].into()).unwrap();
896      let rand_point = |rng : &mut XorShiftRng|
897        aabb.clamp (point3 (randf (rng), randf (rng), randf (rng)));
898      let object = |hull : shape::Hull <f64>|
899        (component::Position::origin(), component::Bound::from (hull));
900      #[expect(unused_variables)]
901      let write_gltf = |
902        (hull, mesh) : &shape::Hull <f64>,
903        filename     : &str
904      | {
905        let mut m = mesh::Triangles3dBuilder::empty();
906        for triangle in mesh.triangles().values() {
907          let triangle = hull.triangle (triangle);
908          m.push_face (triangle.points());
909        }
910        Mesh::from (m.build()).write_gltf (filename);
911      };
912      for _i in 0..1000 {
913        //println!("ITER: {i}");
914        let a = {
915          let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
916            .collect::<Vec<_>>();
917          let hull = Hull3::from_points_with_mesh (&points).unwrap();
918          //write_gltf (&hull, mesh, "hull_a.gltf");
919          object (hull)
920        };
921        let b = {
922          let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
923            .collect::<Vec<_>>();
924          let hull = Hull3::from_points_with_mesh (&points).unwrap();
925          //write_gltf (&hull, mesh, "hull_b.gltf");
926          object (hull)
927        };
928        //show!(a);
929        //show!(b);
930        let _proximity = Proximity::query_gjk (&a, &b);
931      }
932    }
933  }
934}