Skip to main content

parry3d/query/sweep_toi/
proxy_distance.rs

1//! GJK distance between two point-cloud proxies with an index-based simplex cache.
2//!
3//! The cache warm-starts successive distance queries on the same pair, which is the
4//! backbone of the conservative-advancement time-of-impact loop.
5
6use super::toi_proxy::ToiProxy;
7use crate::math::{Pose, Real, Vector};
8
9/// Warm-starting simplex cache for [`proxy_distance`].
10#[derive(Copy, Clone, Debug, Default)]
11pub struct SimplexCache {
12    /// Simplex size measure used to detect a stale cache (3D only).
13    #[cfg(feature = "dim3")]
14    pub metric: Real,
15    /// Number of cached simplex vertices (0 to 3).
16    pub count: u8,
17    /// Cached support indices on the first proxy.
18    pub index_a: [u32; 3],
19    /// Cached support indices on the second proxy.
20    pub index_b: [u32; 3],
21}
22
23/// Result of [`proxy_distance`]. All geometric quantities are expressed in the local frame of
24/// the first proxy.
25#[derive(Copy, Clone, Debug, Default)]
26pub struct ProxyDistanceOutput {
27    /// Closest point on the first proxy (frame A).
28    pub point_a: Vector,
29    /// Closest point on the second proxy (frame A).
30    pub point_b: Vector,
31    /// Separation direction pointing from A to B (frame A). Zero if overlapped.
32    pub normal: Vector,
33    /// Distance between the two proxies (0 if overlapped).
34    pub distance: Real,
35    /// Number of GJK iterations used.
36    pub iterations: u32,
37}
38
39#[derive(Copy, Clone, Default)]
40struct SimplexVertex {
41    wa: Vector,
42    wb: Vector,
43    w: Vector,
44    a: Real,
45    index_a: u32,
46    index_b: u32,
47}
48
49fn cache_is_valid(cache: &SimplexCache, proxy_a: &ToiProxy, proxy_b: &ToiProxy) -> bool {
50    let (na, nb) = (proxy_a.points().len() as u32, proxy_b.points().len() as u32);
51    cache.count <= 3
52        && cache.index_a[..cache.count as usize]
53            .iter()
54            .all(|i| *i < na)
55        && cache.index_b[..cache.count as usize]
56            .iter()
57            .all(|i| *i < nb)
58}
59
60/// Computes the distance between two point-cloud proxies, warm-started by `cache`.
61///
62/// `pos12` is the pose of the second proxy relative to the first; the query runs entirely in
63/// the first proxy’s local frame. When `use_radii` is `false` the proxy radii are ignored
64/// (core-shape distance), which is what the time-of-impact loop uses.
65#[cfg(feature = "dim2")]
66pub fn proxy_distance(
67    pos12: &Pose,
68    proxy_a: &ToiProxy,
69    proxy_b: &ToiProxy,
70    use_radii: bool,
71    cache: &mut SimplexCache,
72) -> ProxyDistanceOutput {
73    let points_a = proxy_a.points();
74    let points_b = proxy_b.points();
75    let point_b_in_a = |i: u32| pos12.transform_point(points_b[i as usize]);
76
77    let mut output = ProxyDistanceOutput::default();
78
79    // Initialize the simplex from the cache.
80    let mut simplex = [SimplexVertex::default(); 3];
81    let mut count = if cache_is_valid(cache, proxy_a, proxy_b) {
82        cache.count as usize
83    } else {
84        0
85    };
86    for (i, v) in simplex.iter_mut().enumerate().take(count) {
87        v.index_a = cache.index_a[i];
88        v.index_b = cache.index_b[i];
89        v.wa = points_a[v.index_a as usize];
90        v.wb = point_b_in_a(v.index_b);
91        v.w = v.wa - v.wb;
92        // Invalid coefficient; set by the simplex solvers.
93        v.a = -1.0;
94    }
95
96    if count == 0 {
97        let v = &mut simplex[0];
98        v.index_a = 0;
99        v.index_b = 0;
100        v.wa = points_a[0];
101        v.wb = point_b_in_a(0);
102        v.w = v.wa - v.wb;
103        v.a = 1.0;
104        count = 1;
105    }
106
107    let mut non_unit_normal = Vector::ZERO;
108    let mut save_a = [0u32; 3];
109    let mut save_b = [0u32; 3];
110
111    // Main iteration loop. All computations are done in frame A.
112    const MAX_ITERATIONS: u32 = 20;
113    let mut iteration = 0;
114    while iteration < MAX_ITERATIONS {
115        // Copy simplex indices so we can identify duplicates.
116        let save_count = count;
117        for i in 0..save_count {
118            save_a[i] = simplex[i].index_a;
119            save_b[i] = simplex[i].index_b;
120        }
121
122        let d = match count {
123            1 => -simplex[0].w,
124            2 => solve_simplex2(&mut simplex, &mut count),
125            3 => solve_simplex3(&mut simplex, &mut count),
126            _ => unreachable!(),
127        };
128
129        // If we have 3 points, then the origin is in the corresponding triangle.
130        if count == 3 {
131            let (pa, pb) = witness_points(&simplex, count);
132            output.point_a = pa;
133            output.point_b = pb;
134            output.iterations = iteration;
135            return output;
136        }
137
138        // Ensure the search direction is numerically fit; a degenerate direction means the
139        // origin is contained by a segment, i.e. the shapes are overlapped.
140        if d.dot(d) < Real::EPSILON * Real::EPSILON {
141            let (pa, pb) = witness_points(&simplex, count);
142            output.point_a = pa;
143            output.point_b = pb;
144            output.iterations = iteration;
145            return output;
146        }
147
148        non_unit_normal = d;
149
150        // Compute a tentative new simplex vertex using support points:
151        // support = support(a, d) - support(b, -d).
152        let index_a = proxy_a.support(d);
153        let index_b = proxy_b.support(pos12.rotation.inverse_transform_vector(-d));
154        let vertex = &mut simplex[count];
155        vertex.index_a = index_a;
156        vertex.wa = points_a[index_a as usize];
157        vertex.index_b = index_b;
158        vertex.wb = point_b_in_a(index_b);
159        vertex.w = vertex.wa - vertex.wb;
160
161        // Iteration count is equated to the number of support point calls.
162        iteration += 1;
163
164        // Check for duplicate support points. This is the main termination criteria.
165        let duplicate = (0..save_count).any(|i| index_a == save_a[i] && index_b == save_b[i]);
166        if duplicate {
167            break;
168        }
169
170        // New vertex is valid and needed.
171        count += 1;
172    }
173
174    let normal = non_unit_normal.normalize_or_zero();
175    let (pa, pb) = witness_points(&simplex, count);
176    output.normal = normal;
177    output.distance = pa.distance(pb);
178    output.point_a = pa;
179    output.point_b = pb;
180    output.iterations = iteration;
181
182    // Cache the simplex.
183    cache.count = count as u8;
184    for (i, v) in simplex.iter().enumerate().take(count) {
185        cache.index_a[i] = v.index_a;
186        cache.index_b[i] = v.index_b;
187    }
188
189    // Apply radii if requested.
190    if use_radii {
191        let radius_a = proxy_a.radius;
192        let radius_b = proxy_b.radius;
193        output.distance = (output.distance - radius_a - radius_b).max(0.0);
194
195        // Keep closest points on perimeter even if overlapped, this way the points move
196        // smoothly.
197        output.point_a += radius_a * normal;
198        output.point_b -= radius_b * normal;
199    }
200
201    output
202}
203
204#[cfg(feature = "dim2")]
205fn witness_points(simplex: &[SimplexVertex; 3], count: usize) -> (Vector, Vector) {
206    match count {
207        1 => (simplex[0].wa, simplex[0].wb),
208        2 => (
209            simplex[0].a * simplex[0].wa + simplex[1].a * simplex[1].wa,
210            simplex[0].a * simplex[0].wb + simplex[1].a * simplex[1].wb,
211        ),
212        3 => {
213            let pa = simplex[0].a * simplex[0].wa
214                + simplex[1].a * simplex[1].wa
215                + simplex[2].a * simplex[2].wa;
216            (pa, pa)
217        }
218        _ => unreachable!(),
219    }
220}
221
222// Returns a vector pointing towards the origin, reducing the simplex if needed.
223#[cfg(feature = "dim2")]
224fn solve_simplex2(simplex: &mut [SimplexVertex; 3], count: &mut usize) -> Vector {
225    let w1 = simplex[0].w;
226    let w2 = simplex[1].w;
227    let e12 = w2 - w1;
228
229    // w1 region
230    let d12_2 = -w1.dot(e12);
231    if d12_2 <= 0.0 {
232        // a2 <= 0, so we clamp it to 0
233        simplex[0].a = 1.0;
234        *count = 1;
235        return -w1;
236    }
237
238    // w2 region
239    let d12_1 = w2.dot(e12);
240    if d12_1 <= 0.0 {
241        // a1 <= 0, so we clamp it to 0
242        simplex[1].a = 1.0;
243        *count = 1;
244        simplex[0] = simplex[1];
245        return -w2;
246    }
247
248    // Must be in e12 region.
249    let inv_d12 = 1.0 / (d12_1 + d12_2);
250    simplex[0].a = d12_1 * inv_d12;
251    simplex[1].a = d12_2 * inv_d12;
252    *count = 2;
253    // cross(cross(w1 + w2, e12), e12)
254    let s = (w1 + w2).perp_dot(e12);
255    Vector::new(-s * e12.y, s * e12.x)
256}
257
258#[cfg(feature = "dim2")]
259fn solve_simplex3(simplex: &mut [SimplexVertex; 3], count: &mut usize) -> Vector {
260    let w1 = simplex[0].w;
261    let w2 = simplex[1].w;
262    let w3 = simplex[2].w;
263
264    // Edge12: a3 = 0
265    let e12 = w2 - w1;
266    let w1e12 = w1.dot(e12);
267    let w2e12 = w2.dot(e12);
268    let d12_1 = w2e12;
269    let d12_2 = -w1e12;
270
271    // Edge13: a2 = 0
272    let e13 = w3 - w1;
273    let w1e13 = w1.dot(e13);
274    let w3e13 = w3.dot(e13);
275    let d13_1 = w3e13;
276    let d13_2 = -w1e13;
277
278    // Edge23: a1 = 0
279    let e23 = w3 - w2;
280    let w2e23 = w2.dot(e23);
281    let w3e23 = w3.dot(e23);
282    let d23_1 = w3e23;
283    let d23_2 = -w2e23;
284
285    // Triangle123
286    let n123 = e12.perp_dot(e13);
287    let d123_1 = n123 * w2.perp_dot(w3);
288    let d123_2 = n123 * w3.perp_dot(w1);
289    let d123_3 = n123 * w1.perp_dot(w2);
290
291    // w1 region
292    if d12_2 <= 0.0 && d13_2 <= 0.0 {
293        simplex[0].a = 1.0;
294        *count = 1;
295        return -w1;
296    }
297
298    // e12
299    if d12_1 > 0.0 && d12_2 > 0.0 && d123_3 <= 0.0 {
300        let inv_d12 = 1.0 / (d12_1 + d12_2);
301        simplex[0].a = d12_1 * inv_d12;
302        simplex[1].a = d12_2 * inv_d12;
303        *count = 2;
304        let s = (w1 + w2).perp_dot(e12);
305        return Vector::new(-s * e12.y, s * e12.x);
306    }
307
308    // e13
309    if d13_1 > 0.0 && d13_2 > 0.0 && d123_2 <= 0.0 {
310        let inv_d13 = 1.0 / (d13_1 + d13_2);
311        simplex[0].a = d13_1 * inv_d13;
312        simplex[2].a = d13_2 * inv_d13;
313        *count = 2;
314        simplex[1] = simplex[2];
315        let s = (w1 + w3).perp_dot(e13);
316        return Vector::new(-s * e13.y, s * e13.x);
317    }
318
319    // w2 region
320    if d12_1 <= 0.0 && d23_2 <= 0.0 {
321        simplex[1].a = 1.0;
322        *count = 1;
323        simplex[0] = simplex[1];
324        return -w2;
325    }
326
327    // w3 region
328    if d13_1 <= 0.0 && d23_1 <= 0.0 {
329        simplex[2].a = 1.0;
330        *count = 1;
331        simplex[0] = simplex[2];
332        return -w3;
333    }
334
335    // e23
336    if d23_1 > 0.0 && d23_2 > 0.0 && d123_1 <= 0.0 {
337        let inv_d23 = 1.0 / (d23_1 + d23_2);
338        simplex[1].a = d23_1 * inv_d23;
339        simplex[2].a = d23_2 * inv_d23;
340        *count = 2;
341        simplex[0] = simplex[2];
342        let s = (w2 + w3).perp_dot(e23);
343        return Vector::new(-s * e23.y, s * e23.x);
344    }
345
346    // Must be in triangle123
347    let inv_d123 = 1.0 / (d123_1 + d123_2 + d123_3);
348    simplex[0].a = d123_1 * inv_d123;
349    simplex[1].a = d123_2 * inv_d123;
350    simplex[2].a = d123_3 * inv_d123;
351    *count = 3;
352
353    // No search direction
354    Vector::ZERO
355}
356
357// ==================== 3D ====================
358
359#[cfg(feature = "dim3")]
360const MAX_GJK_ITERATIONS: u32 = 32;
361
362#[cfg(feature = "dim3")]
363fn barycentric_coords_edge(a: Vector, b: Vector) -> [Real; 3] {
364    let ab = b - a;
365    // Last element is divisor
366    [b.dot(ab), -a.dot(ab), ab.dot(ab)]
367}
368
369#[cfg(feature = "dim3")]
370fn barycentric_coords_tri(a: Vector, b: Vector, c: Vector) -> [Real; 4] {
371    let ab = b - a;
372    let ac = c - a;
373
374    let b_x_c = b.cross(c);
375    let c_x_a = c.cross(a);
376    let a_x_b = a.cross(b);
377
378    let ab_x_ac = ab.cross(ac);
379
380    // Last element is divisor
381    [
382        b_x_c.dot(ab_x_ac),
383        c_x_a.dot(ab_x_ac),
384        a_x_b.dot(ab_x_ac),
385        ab_x_ac.dot(ab_x_ac),
386    ]
387}
388
389#[cfg(feature = "dim3")]
390fn scalar_triple_product(a: Vector, b: Vector, c: Vector) -> Real {
391    a.cross(b).dot(c)
392}
393
394#[cfg(feature = "dim3")]
395fn barycentric_coords_tet(a: Vector, b: Vector, c: Vector, d: Vector) -> [Real; 5] {
396    let ab = b - a;
397    let ac = c - a;
398    let ad = d - a;
399
400    // Last element is divisor (forced to be positive)
401    let divisor = scalar_triple_product(ab, ac, ad);
402    let sign = if divisor < 0.0 { -1.0 } else { 1.0 };
403
404    [
405        sign * scalar_triple_product(b, c, d),
406        sign * scalar_triple_product(a, d, c),
407        sign * scalar_triple_product(a, b, d),
408        sign * scalar_triple_product(a, c, b),
409        sign * divisor,
410    ]
411}
412
413#[cfg(feature = "dim3")]
414fn simplex_metric(simplex: &[SimplexVertex; 4], count: usize) -> Real {
415    match count {
416        1 => 0.0,
417        2 => simplex[0].w.distance(simplex[1].w),
418        3 => {
419            let a = simplex[0].w;
420            let b = simplex[1].w;
421            let c = simplex[2].w;
422            (b - a).cross(c - a).length() / 2.0
423        }
424        4 => {
425            let a = simplex[0].w;
426            let b = simplex[1].w;
427            let c = simplex[2].w;
428            let d = simplex[3].w;
429            scalar_triple_product(b - a, c - a, d - a) / 6.0
430        }
431        _ => unreachable!(),
432    }
433}
434
435#[cfg(feature = "dim3")]
436fn witness_points3(simplex: &[SimplexVertex; 4], count: usize) -> (Vector, Vector) {
437    let vs = simplex;
438    match count {
439        1 => (vs[0].wa, vs[0].wb),
440        2 => (
441            vs[0].a * vs[0].wa + vs[1].a * vs[1].wa,
442            vs[0].a * vs[0].wb + vs[1].a * vs[1].wb,
443        ),
444        3 => (
445            vs[0].a * vs[0].wa + vs[1].a * vs[1].wa + vs[2].a * vs[2].wa,
446            vs[0].a * vs[0].wb + vs[1].a * vs[1].wb + vs[2].a * vs[2].wb,
447        ),
448        4 => {
449            // Force identical points and *zero* distance
450            let sum =
451                vs[0].a * vs[0].wa + vs[1].a * vs[1].wa + vs[2].a * vs[2].wa + vs[3].a * vs[3].wa;
452            (sum, sum)
453        }
454        _ => unreachable!(),
455    }
456}
457
458/// Solves the 2-simplex. Returns `false` when the barycentric divisor degenerates.
459#[cfg(feature = "dim3")]
460fn solve_simplex2_3d(simplex: &mut [SimplexVertex; 4], count: &mut usize) -> bool {
461    let a = simplex[0].w;
462    let b = simplex[1].w;
463    let ab = b - a;
464
465    let divisor = ab.dot(ab);
466    let u = b.dot(ab);
467    let v = -a.dot(ab);
468
469    // V(A)
470    if v <= 0.0 {
471        *count = 1;
472        simplex[0].a = 1.0;
473        return true;
474    }
475
476    // V(B)
477    if u <= 0.0 {
478        *count = 1;
479        simplex[0] = simplex[1];
480        simplex[0].a = 1.0;
481        return true;
482    }
483
484    // Edge region
485    if divisor <= 0.0 {
486        return false;
487    }
488
489    let denominator = 1.0 / divisor;
490    simplex[0].a = denominator * u;
491    simplex[1].a = denominator * v;
492    true
493}
494
495#[cfg(feature = "dim3")]
496fn solve_simplex3_3d(simplex: &mut [SimplexVertex; 4], count: &mut usize) -> bool {
497    let v1 = simplex[0];
498    let v2 = simplex[1];
499    let v3 = simplex[2];
500
501    let w_ab = barycentric_coords_edge(v1.w, v2.w);
502    let w_bc = barycentric_coords_edge(v2.w, v3.w);
503    let w_ca = barycentric_coords_edge(v3.w, v1.w);
504
505    // VR(A)
506    if w_ab[1] <= 0.0 && w_ca[0] <= 0.0 {
507        *count = 1;
508        simplex[0] = v1;
509        simplex[0].a = 1.0;
510        return true;
511    }
512
513    // VR(B)
514    if w_bc[1] <= 0.0 && w_ab[0] <= 0.0 {
515        *count = 1;
516        simplex[0] = v2;
517        simplex[0].a = 1.0;
518        return true;
519    }
520
521    // VR(C)
522    if w_ca[1] <= 0.0 && w_bc[0] <= 0.0 {
523        *count = 1;
524        simplex[0] = v3;
525        simplex[0].a = 1.0;
526        return true;
527    }
528
529    let w_abc = barycentric_coords_tri(v1.w, v2.w, v3.w);
530
531    // VR(AB)
532    if w_abc[2] <= 0.0 && w_ab[0] > 0.0 && w_ab[1] > 0.0 {
533        *count = 2;
534        simplex[0] = v1;
535        simplex[1] = v2;
536        let divisor = w_ab[2];
537        if divisor <= 0.0 {
538            return false;
539        }
540        simplex[0].a = w_ab[0] / divisor;
541        simplex[1].a = w_ab[1] / divisor;
542        return true;
543    }
544
545    // VR(BC)
546    if w_abc[0] <= 0.0 && w_bc[0] > 0.0 && w_bc[1] > 0.0 {
547        *count = 2;
548        simplex[0] = v2;
549        simplex[1] = v3;
550        let divisor = w_bc[2];
551        if divisor <= 0.0 {
552            return false;
553        }
554        simplex[0].a = w_bc[0] / divisor;
555        simplex[1].a = w_bc[1] / divisor;
556        return true;
557    }
558
559    // VR(CA)
560    if w_abc[1] <= 0.0 && w_ca[0] > 0.0 && w_ca[1] > 0.0 {
561        *count = 2;
562        simplex[0] = v3;
563        simplex[1] = v1;
564        let divisor = w_ca[2];
565        if divisor <= 0.0 {
566            return false;
567        }
568        simplex[0].a = w_ca[0] / divisor;
569        simplex[1].a = w_ca[1] / divisor;
570        return true;
571    }
572
573    // Face region
574    let divisor = w_abc[3];
575    if divisor <= 0.0 {
576        return false;
577    }
578
579    // VR(ABC)
580    simplex[0].a = w_abc[0] / divisor;
581    simplex[1].a = w_abc[1] / divisor;
582    simplex[2].a = w_abc[2] / divisor;
583    true
584}
585
586#[cfg(feature = "dim3")]
587fn solve_simplex4_3d(simplex: &mut [SimplexVertex; 4], count: &mut usize) -> bool {
588    let vertex_a = simplex[0];
589    let vertex_b = simplex[1];
590    let vertex_c = simplex[2];
591    let vertex_d = simplex[3];
592
593    let w_ab = barycentric_coords_edge(vertex_a.w, vertex_b.w);
594    let w_ac = barycentric_coords_edge(vertex_a.w, vertex_c.w);
595    let w_ad = barycentric_coords_edge(vertex_a.w, vertex_d.w);
596    let w_bc = barycentric_coords_edge(vertex_b.w, vertex_c.w);
597    let w_cd = barycentric_coords_edge(vertex_c.w, vertex_d.w);
598    let w_db = barycentric_coords_edge(vertex_d.w, vertex_b.w);
599
600    // VR(A)
601    if w_ab[1] <= 0.0 && w_ac[1] <= 0.0 && w_ad[1] <= 0.0 {
602        *count = 1;
603        simplex[0] = vertex_a;
604        simplex[0].a = 1.0;
605        return true;
606    }
607
608    // VR(B)
609    if w_ab[0] <= 0.0 && w_db[0] <= 0.0 && w_bc[1] <= 0.0 {
610        *count = 1;
611        simplex[0] = vertex_b;
612        simplex[0].a = 1.0;
613        return true;
614    }
615
616    // VR(C)
617    if w_ac[0] <= 0.0 && w_bc[0] <= 0.0 && w_cd[1] <= 0.0 {
618        *count = 1;
619        simplex[0] = vertex_c;
620        simplex[0].a = 1.0;
621        return true;
622    }
623
624    // VR(D)
625    if w_ad[0] <= 0.0 && w_cd[0] <= 0.0 && w_db[1] <= 0.0 {
626        *count = 1;
627        simplex[0] = vertex_d;
628        simplex[0].a = 1.0;
629        return true;
630    }
631
632    let w_acb = barycentric_coords_tri(vertex_a.w, vertex_c.w, vertex_b.w);
633    let w_abd = barycentric_coords_tri(vertex_a.w, vertex_b.w, vertex_d.w);
634    let w_adc = barycentric_coords_tri(vertex_a.w, vertex_d.w, vertex_c.w);
635    let w_bcd = barycentric_coords_tri(vertex_b.w, vertex_c.w, vertex_d.w);
636
637    // VR(AB)
638    if w_abd[2] <= 0.0 && w_acb[1] <= 0.0 && w_ab[0] > 0.0 && w_ab[1] > 0.0 {
639        *count = 2;
640        simplex[0] = vertex_a;
641        simplex[1] = vertex_b;
642        let divisor = w_ab[2];
643        if divisor <= 0.0 {
644            return false;
645        }
646        simplex[0].a = w_ab[0] / divisor;
647        simplex[1].a = w_ab[1] / divisor;
648        return true;
649    }
650
651    // VR(AC)
652    if w_acb[2] <= 0.0 && w_adc[1] <= 0.0 && w_ac[0] > 0.0 && w_ac[1] > 0.0 {
653        *count = 2;
654        simplex[0] = vertex_a;
655        simplex[1] = vertex_c;
656        let divisor = w_ac[2];
657        if divisor <= 0.0 {
658            return false;
659        }
660        simplex[0].a = w_ac[0] / divisor;
661        simplex[1].a = w_ac[1] / divisor;
662        return true;
663    }
664
665    // VR(AD)
666    if w_adc[2] <= 0.0 && w_abd[1] <= 0.0 && w_ad[0] > 0.0 && w_ad[1] > 0.0 {
667        *count = 2;
668        simplex[0] = vertex_a;
669        simplex[1] = vertex_d;
670        let divisor = w_ad[2];
671        if divisor <= 0.0 {
672            return false;
673        }
674        simplex[0].a = w_ad[0] / divisor;
675        simplex[1].a = w_ad[1] / divisor;
676        return true;
677    }
678
679    // VR(BC)
680    if w_acb[0] <= 0.0 && w_bcd[2] <= 0.0 && w_bc[0] > 0.0 && w_bc[1] > 0.0 {
681        *count = 2;
682        simplex[0] = vertex_b;
683        simplex[1] = vertex_c;
684        let divisor = w_bc[2];
685        if divisor <= 0.0 {
686            return false;
687        }
688        simplex[0].a = w_bc[0] / divisor;
689        simplex[1].a = w_bc[1] / divisor;
690        return true;
691    }
692
693    // VR(CD)
694    if w_adc[0] <= 0.0 && w_bcd[0] <= 0.0 && w_cd[0] > 0.0 && w_cd[1] > 0.0 {
695        *count = 2;
696        simplex[0] = vertex_c;
697        simplex[1] = vertex_d;
698        let divisor = w_cd[2];
699        if divisor <= 0.0 {
700            return false;
701        }
702        simplex[0].a = w_cd[0] / divisor;
703        simplex[1].a = w_cd[1] / divisor;
704        return true;
705    }
706
707    // VR(DB)
708    if w_abd[0] <= 0.0 && w_bcd[1] <= 0.0 && w_db[0] > 0.0 && w_db[1] > 0.0 {
709        *count = 2;
710        simplex[0] = vertex_d;
711        simplex[1] = vertex_b;
712        let divisor = w_db[2];
713        if divisor <= 0.0 {
714            return false;
715        }
716        simplex[0].a = w_db[0] / divisor;
717        simplex[1].a = w_db[1] / divisor;
718        return true;
719    }
720
721    let w_abcd = barycentric_coords_tet(vertex_a.w, vertex_b.w, vertex_c.w, vertex_d.w);
722
723    // VR(ACB)
724    if w_abcd[3] < 0.0 && w_acb[0] > 0.0 && w_acb[1] > 0.0 && w_acb[2] > 0.0 {
725        *count = 3;
726        simplex[0] = vertex_a;
727        simplex[1] = vertex_c;
728        simplex[2] = vertex_b;
729        let divisor = w_acb[3];
730        if divisor <= 0.0 {
731            return false;
732        }
733        simplex[0].a = w_acb[0] / divisor;
734        simplex[1].a = w_acb[1] / divisor;
735        simplex[2].a = w_acb[2] / divisor;
736        return true;
737    }
738
739    // VR(ABD)
740    if w_abcd[2] < 0.0 && w_abd[0] > 0.0 && w_abd[1] > 0.0 && w_abd[2] > 0.0 {
741        *count = 3;
742        simplex[0] = vertex_a;
743        simplex[1] = vertex_b;
744        simplex[2] = vertex_d;
745        let divisor = w_abd[3];
746        if divisor <= 0.0 {
747            return false;
748        }
749        simplex[0].a = w_abd[0] / divisor;
750        simplex[1].a = w_abd[1] / divisor;
751        simplex[2].a = w_abd[2] / divisor;
752        return true;
753    }
754
755    // VR(ADC)
756    if w_abcd[1] < 0.0 && w_adc[0] > 0.0 && w_adc[1] > 0.0 && w_adc[2] > 0.0 {
757        *count = 3;
758        simplex[0] = vertex_a;
759        simplex[1] = vertex_d;
760        simplex[2] = vertex_c;
761        let divisor = w_adc[3];
762        if divisor <= 0.0 {
763            return false;
764        }
765        simplex[0].a = w_adc[0] / divisor;
766        simplex[1].a = w_adc[1] / divisor;
767        simplex[2].a = w_adc[2] / divisor;
768        return true;
769    }
770
771    // VR(BCD)
772    if w_abcd[0] < 0.0 && w_bcd[0] > 0.0 && w_bcd[1] > 0.0 && w_bcd[2] > 0.0 {
773        *count = 3;
774        simplex[0] = vertex_b;
775        simplex[1] = vertex_c;
776        simplex[2] = vertex_d;
777        let divisor = w_bcd[3];
778        if divisor <= 0.0 {
779            return false;
780        }
781        simplex[0].a = w_bcd[0] / divisor;
782        simplex[1].a = w_bcd[1] / divisor;
783        simplex[2].a = w_bcd[2] / divisor;
784        return true;
785    }
786
787    // *** Inside tetrahedron ***
788    let divisor = w_abcd[4];
789    if divisor <= 0.0 {
790        return false;
791    }
792
793    // VR(ABCD)
794    simplex[0].a = w_abcd[0] / divisor;
795    simplex[1].a = w_abcd[1] / divisor;
796    simplex[2].a = w_abcd[2] / divisor;
797    simplex[3].a = w_abcd[3] / divisor;
798    true
799}
800
801/// Computes the distance between two point-cloud proxies, warm-started by `cache`.
802///
803/// `pos12` is the pose of the second proxy relative to the first; the query runs entirely in
804/// the first proxy’s local frame. When `use_radii` is `false` the proxy radii are ignored
805/// (core-shape distance), which is what the time-of-impact loop uses.
806#[cfg(feature = "dim3")]
807pub fn proxy_distance(
808    pos12: &Pose,
809    proxy_a: &ToiProxy,
810    proxy_b: &ToiProxy,
811    use_radii: bool,
812    cache: &mut SimplexCache,
813) -> ProxyDistanceOutput {
814    let points_a = proxy_a.points();
815    let points_b = proxy_b.points();
816    let point_b_in_a = |i: u32| pos12.transform_point(points_b[i as usize]);
817
818    let mut output = ProxyDistanceOutput::default();
819
820    // Compute initial simplex from cache. Note that in 3D the CSO points are w = wB - wA
821    // (the opposite of the 2D convention).
822    let mut simplex = [SimplexVertex::default(); 4];
823    let mut count = if cache_is_valid(cache, proxy_a, proxy_b) {
824        cache.count as usize
825    } else {
826        0
827    };
828    for (i, v) in simplex.iter_mut().enumerate().take(count) {
829        v.index_a = cache.index_a[i];
830        v.index_b = cache.index_b[i];
831        v.wa = points_a[v.index_a as usize];
832        v.wb = point_b_in_a(v.index_b);
833        v.w = v.wb - v.wa;
834        v.a = 0.0;
835    }
836
837    // Compute the new simplex metric; if it is substantially different than the old metric
838    // flush the simplex.
839    if count > 0 {
840        let metric1 = cache.metric;
841        let metric2 = simplex_metric(&simplex, count);
842        if 2.0 * metric1 < metric2 || metric2 < 0.5 * metric1 || metric2 < Real::EPSILON {
843            count = 0;
844        }
845    }
846
847    // If the cache is invalid or empty.
848    if count == 0 {
849        let v = &mut simplex[0];
850        v.index_a = 0;
851        v.index_b = 0;
852        v.wa = points_a[0];
853        v.wb = point_b_in_a(0);
854        v.w = v.wb - v.wa;
855        v.a = 0.0;
856        count = 1;
857    }
858
859    let mut backup = simplex;
860    let mut backup_count = 0usize;
861
862    // Keep track of squared distance.
863    let mut distance_sq = Real::MAX;
864    let mut normal = Vector::ZERO;
865
866    // Run GJK.
867    let mut iteration = 0;
868    while iteration < MAX_GJK_ITERATIONS {
869        // Solve simplex.
870        let solved = match count {
871            1 => {
872                simplex[0].a = 1.0;
873                true
874            }
875            2 => solve_simplex2_3d(&mut simplex, &mut count),
876            3 => solve_simplex3_3d(&mut simplex, &mut count),
877            4 => solve_simplex4_3d(&mut simplex, &mut count),
878            _ => unreachable!(),
879        };
880
881        if !solved {
882            // No progress - reconstruct last simplex.
883            if backup_count == 0 {
884                break;
885            }
886            simplex = backup;
887            count = backup_count;
888            break;
889        }
890
891        if count == 4 {
892            // Overlap
893            let (pa, pb) = witness_points3(&simplex, count);
894            output.point_a = pa;
895            output.point_b = pb;
896            output.iterations = iteration;
897            return output;
898        }
899
900        // Assure distance progression.
901        let old_distance_sq = distance_sq;
902
903        // Compute closest point.
904        let closest_point = match count {
905            1 => simplex[0].w,
906            2 => simplex[0].a * simplex[0].w + simplex[1].a * simplex[1].w,
907            3 => {
908                simplex[0].a * simplex[0].w
909                    + simplex[1].a * simplex[1].w
910                    + simplex[2].a * simplex[2].w
911            }
912            _ => unreachable!(),
913        };
914
915        distance_sq = closest_point.dot(closest_point);
916
917        if distance_sq >= old_distance_sq {
918            // No progress - reconstruct last simplex.
919            if backup_count == 0 {
920                break;
921            }
922            simplex = backup;
923            count = backup_count;
924            break;
925        }
926
927        // Build new tentative support point.
928        let search_direction = match count {
929            1 => -simplex[0].w,
930            2 => {
931                // v = (AB x AO) x AB
932                let a = simplex[0].w;
933                let b = simplex[1].w;
934                let ab = b - a;
935                ab.cross(-a).cross(ab)
936            }
937            3 => {
938                // v = AB x AC or v = AC x AB
939                let a = simplex[0].w;
940                let b = simplex[1].w;
941                let c = simplex[2].w;
942                let n = (b - a).cross(c - a);
943                if n.dot(a) < 0.0 {
944                    n
945                } else {
946                    -n
947                }
948            }
949            _ => unreachable!(),
950        };
951
952        if search_direction.length_squared() < 1000.0 * Real::MIN_POSITIVE {
953            // The origin is probably contained by a line segment or triangle.
954            // Thus the shapes are overlapped.
955            let (pa, pb) = witness_points3(&simplex, count);
956            output.point_a = pa;
957            output.point_b = pb;
958            output.iterations = iteration;
959            return output;
960        }
961
962        normal = -search_direction;
963
964        // Get new support points.
965        let index_a = proxy_a.support(-search_direction);
966        let support_a = points_a[index_a as usize];
967        let index_b = proxy_b.support(pos12.rotation.inverse() * search_direction);
968        let support_b = point_b_in_a(index_b);
969
970        // Save current simplex and add new vertex - this can fail if we detect cycling.
971        backup = simplex;
972        backup_count = count;
973
974        // Check for duplicate support points. This is the main termination criteria.
975        let duplicate =
976            (0..count).any(|i| simplex[i].index_a == index_a && simplex[i].index_b == index_b);
977        if duplicate {
978            break;
979        }
980
981        simplex[count].index_a = index_a;
982        simplex[count].index_b = index_b;
983        simplex[count].wa = support_a;
984        simplex[count].wb = support_b;
985        simplex[count].w = support_b - support_a;
986        count += 1;
987
988        iteration += 1;
989    }
990
991    let normal = normal.normalize_or_zero();
992    if normal == Vector::ZERO {
993        // Treat as overlap.
994        output.iterations = iteration;
995        return output;
996    }
997
998    // Build witness points and save cache.
999    let (pa, pb) = witness_points3(&simplex, count);
1000    cache.metric = simplex_metric(&simplex, count);
1001    cache.count = count.min(3) as u8;
1002    for (i, v) in simplex.iter().enumerate().take(count.min(3)) {
1003        cache.index_a[i] = v.index_a;
1004        cache.index_b[i] = v.index_b;
1005    }
1006
1007    // Results stay in frame A.
1008    output.point_a = pa;
1009    output.point_b = pb;
1010    output.distance = pa.distance(pb);
1011    output.normal = normal;
1012    output.iterations = iteration;
1013
1014    // Apply radii if requested.
1015    if use_radii {
1016        let ra = proxy_a.radius;
1017        let rb = proxy_b.radius;
1018        output.distance = (output.distance - ra - rb).max(0.0);
1019
1020        // Keep closest points on perimeter even if overlapped, this way the points move
1021        // smoothly.
1022        output.point_a += ra * normal;
1023        output.point_b -= rb * normal;
1024    }
1025
1026    output
1027}