Skip to main content

box2d_rust/distance/
gjk.rs

1// GJK distance (b2ShapeDistance) and the simplex machinery from distance.c.
2// SPDX-FileCopyrightText: 2023 Erin Catto
3// SPDX-License-Identifier: MIT
4
5use super::types::{DistanceInput, DistanceOutput, ShapeProxy, Simplex, SimplexCache};
6use crate::math_functions::{
7    add, cross, cross_sv, distance, dot, is_normalized, max_float, mul_add, mul_sub, neg,
8    normalize, sub, transform_point, Vec2, VEC2_ZERO,
9};
10
11pub(crate) fn weight2(a1: f32, w1: Vec2, a2: f32, w2: Vec2) -> Vec2 {
12    Vec2 {
13        x: a1 * w1.x + a2 * w2.x,
14        y: a1 * w1.y + a2 * w2.y,
15    }
16}
17
18fn weight3(a1: f32, w1: Vec2, a2: f32, w2: Vec2, a3: f32, w3: Vec2) -> Vec2 {
19    Vec2 {
20        x: a1 * w1.x + a2 * w2.x + a3 * w3.x,
21        y: a1 * w1.y + a2 * w2.y + a3 * w3.y,
22    }
23}
24
25pub(crate) fn find_support(proxy: &ShapeProxy, direction: Vec2) -> i32 {
26    let points = &proxy.points;
27    let count = proxy.count;
28
29    let mut best_index = 0;
30    let mut best_value = dot(points[0], direction);
31    for i in 1..count {
32        let value = dot(points[i as usize], direction);
33        if value > best_value {
34            best_index = i;
35            best_value = value;
36        }
37    }
38
39    best_index
40}
41
42fn make_simplex_from_cache(
43    cache: &SimplexCache,
44    proxy_a: &ShapeProxy,
45    proxy_b: &ShapeProxy,
46) -> Simplex {
47    debug_assert!(cache.count <= 3);
48    let mut s = Simplex {
49        count: cache.count as i32,
50        ..Default::default()
51    };
52
53    // Copy data from cache.
54    for i in 0..s.count {
55        let v = s.vertex_mut(i);
56        v.index_a = cache.index_a[i as usize] as i32;
57        v.index_b = cache.index_b[i as usize] as i32;
58        v.w_a = proxy_a.points[v.index_a as usize];
59        v.w_b = proxy_b.points[v.index_b as usize];
60        v.w = sub(v.w_a, v.w_b);
61
62        // invalid
63        v.a = -1.0;
64    }
65
66    // If the cache is empty or invalid ...
67    if s.count == 0 {
68        let v = s.vertex_mut(0);
69        v.index_a = 0;
70        v.index_b = 0;
71        v.w_a = proxy_a.points[0];
72        v.w_b = proxy_b.points[0];
73        v.w = sub(v.w_a, v.w_b);
74        v.a = 1.0;
75        s.count = 1;
76    }
77
78    s
79}
80
81fn make_simplex_cache(simplex: &Simplex) -> SimplexCache {
82    let mut cache = SimplexCache {
83        count: simplex.count as u16,
84        ..Default::default()
85    };
86    for i in 0..simplex.count {
87        cache.index_a[i as usize] = simplex.vertex(i).index_a as u8;
88        cache.index_b[i as usize] = simplex.vertex(i).index_b as u8;
89    }
90
91    cache
92}
93
94pub(crate) fn compute_witness_points(s: &Simplex) -> (Vec2, Vec2) {
95    match s.count {
96        1 => (s.v1.w_a, s.v1.w_b),
97
98        2 => (
99            weight2(s.v1.a, s.v1.w_a, s.v2.a, s.v2.w_a),
100            weight2(s.v1.a, s.v1.w_b, s.v2.a, s.v2.w_b),
101        ),
102
103        3 => {
104            let a = weight3(s.v1.a, s.v1.w_a, s.v2.a, s.v2.w_a, s.v3.a, s.v3.w_a);
105            // C: todo why are these not equal?
106            // b = weight3(s.v1.a, s.v1.w_b, s.v2.a, s.v2.w_b, s.v3.a, s.v3.w_b);
107            (a, a)
108        }
109
110        _ => {
111            debug_assert!(false);
112            (VEC2_ZERO, VEC2_ZERO)
113        }
114    }
115}
116
117// Solve a line segment using barycentric coordinates.
118//
119// p = a1 * w1 + a2 * w2
120// a1 + a2 = 1
121//
122// The vector from the origin to the closest point on the line is
123// perpendicular to the line.
124// e12 = w2 - w1
125// dot(p, e) = 0
126// a1 * dot(w1, e) + a2 * dot(w2, e) = 0
127//
128// 2-by-2 linear system
129// [1      1     ][a1] = [1]
130// [w1.e12 w2.e12][a2] = [0]
131//
132// Define
133// d12_1 =  dot(w2, e12)
134// d12_2 = -dot(w1, e12)
135// d12 = d12_1 + d12_2
136//
137// Solution
138// a1 = d12_1 / d12
139// a2 = d12_2 / d12
140//
141// returns a vector that points towards the origin
142pub(crate) fn solve_simplex2(s: &mut Simplex) -> Vec2 {
143    let w1 = s.v1.w;
144    let w2 = s.v2.w;
145    let e12 = sub(w2, w1);
146
147    // w1 region
148    let d12_2 = -dot(w1, e12);
149    if d12_2 <= 0.0 {
150        // a2 <= 0, so we clamp it to 0
151        s.v1.a = 1.0;
152        s.count = 1;
153        return neg(w1);
154    }
155
156    // w2 region
157    let d12_1 = dot(w2, e12);
158    if d12_1 <= 0.0 {
159        // a1 <= 0, so we clamp it to 0
160        s.v2.a = 1.0;
161        s.count = 1;
162        s.v1 = s.v2;
163        return neg(w2);
164    }
165
166    // Must be in e12 region.
167    let inv_d12 = 1.0 / (d12_1 + d12_2);
168    s.v1.a = d12_1 * inv_d12;
169    s.v2.a = d12_2 * inv_d12;
170    s.count = 2;
171    cross_sv(cross(add(w1, w2), e12), e12)
172}
173
174pub(crate) fn solve_simplex3(s: &mut Simplex) -> Vec2 {
175    let w1 = s.v1.w;
176    let w2 = s.v2.w;
177    let w3 = s.v3.w;
178
179    // Edge12
180    // [1      1     ][a1] = [1]
181    // [w1.e12 w2.e12][a2] = [0]
182    // a3 = 0
183    let e12 = sub(w2, w1);
184    let w1e12 = dot(w1, e12);
185    let w2e12 = dot(w2, e12);
186    let d12_1 = w2e12;
187    let d12_2 = -w1e12;
188
189    // Edge13
190    // [1      1     ][a1] = [1]
191    // [w1.e13 w3.e13][a3] = [0]
192    // a2 = 0
193    let e13 = sub(w3, w1);
194    let w1e13 = dot(w1, e13);
195    let w3e13 = dot(w3, e13);
196    let d13_1 = w3e13;
197    let d13_2 = -w1e13;
198
199    // Edge23
200    // [1      1     ][a2] = [1]
201    // [w2.e23 w3.e23][a3] = [0]
202    // a1 = 0
203    let e23 = sub(w3, w2);
204    let w2e23 = dot(w2, e23);
205    let w3e23 = dot(w3, e23);
206    let d23_1 = w3e23;
207    let d23_2 = -w2e23;
208
209    // Triangle123
210    let n123 = cross(e12, e13);
211
212    let d123_1 = n123 * cross(w2, w3);
213    let d123_2 = n123 * cross(w3, w1);
214    let d123_3 = n123 * cross(w1, w2);
215
216    // w1 region
217    if d12_2 <= 0.0 && d13_2 <= 0.0 {
218        s.v1.a = 1.0;
219        s.count = 1;
220        return neg(w1);
221    }
222
223    // e12
224    if d12_1 > 0.0 && d12_2 > 0.0 && d123_3 <= 0.0 {
225        let inv_d12 = 1.0 / (d12_1 + d12_2);
226        s.v1.a = d12_1 * inv_d12;
227        s.v2.a = d12_2 * inv_d12;
228        s.count = 2;
229        return cross_sv(cross(add(w1, w2), e12), e12);
230    }
231
232    // e13
233    if d13_1 > 0.0 && d13_2 > 0.0 && d123_2 <= 0.0 {
234        let inv_d13 = 1.0 / (d13_1 + d13_2);
235        s.v1.a = d13_1 * inv_d13;
236        s.v3.a = d13_2 * inv_d13;
237        s.count = 2;
238        s.v2 = s.v3;
239        return cross_sv(cross(add(w1, w3), e13), e13);
240    }
241
242    // w2 region
243    if d12_1 <= 0.0 && d23_2 <= 0.0 {
244        s.v2.a = 1.0;
245        s.count = 1;
246        s.v1 = s.v2;
247        return neg(w2);
248    }
249
250    // w3 region
251    if d13_1 <= 0.0 && d23_1 <= 0.0 {
252        s.v3.a = 1.0;
253        s.count = 1;
254        s.v1 = s.v3;
255        return neg(w3);
256    }
257
258    // e23
259    if d23_1 > 0.0 && d23_2 > 0.0 && d123_1 <= 0.0 {
260        let inv_d23 = 1.0 / (d23_1 + d23_2);
261        s.v2.a = d23_1 * inv_d23;
262        s.v3.a = d23_2 * inv_d23;
263        s.count = 2;
264        s.v1 = s.v3;
265        return cross_sv(cross(add(w2, w3), e23), e23);
266    }
267
268    // Must be in triangle123
269    let inv_d123 = 1.0 / (d123_1 + d123_2 + d123_3);
270    s.v1.a = d123_1 * inv_d123;
271    s.v2.a = d123_2 * inv_d123;
272    s.v3.a = d123_3 * inv_d123;
273    s.count = 3;
274
275    // No search direction
276    VEC2_ZERO
277}
278
279/// Compute the closest points between two shapes represented as point clouds.
280/// The cache is input/output: on the first call set `SimplexCache::count` to
281/// zero. (b2ShapeDistance)
282///
283/// The underlying GJK algorithm may be debugged by passing in a simplex buffer;
284/// pass `None` normally. The C version only records intermediate simplexes in
285/// debug builds; this port records them whenever a buffer is provided.
286///
287/// Uses GJK for computing the distance between convex shapes.
288/// <https://box2d.org/files/ErinCatto_GJK_GDC2010.pdf>
289pub fn shape_distance(
290    input: &DistanceInput,
291    cache: &mut SimplexCache,
292    mut simplexes: Option<&mut [Simplex]>,
293) -> DistanceOutput {
294    debug_assert!(input.proxy_a.count > 0 && input.proxy_b.count > 0);
295    debug_assert!(input.proxy_a.radius >= 0.0);
296    debug_assert!(input.proxy_b.radius >= 0.0);
297
298    let mut output = DistanceOutput::default();
299
300    let proxy_a = &input.proxy_a;
301
302    // Get proxyB in frame A to avoid further transforms in the main loop.
303    // This is still a performance gain at 8 points.
304    let mut local_proxy_b = ShapeProxy {
305        count: input.proxy_b.count,
306        radius: input.proxy_b.radius,
307        ..Default::default()
308    };
309    for i in 0..local_proxy_b.count as usize {
310        local_proxy_b.points[i] = transform_point(input.transform, input.proxy_b.points[i]);
311    }
312
313    // Initialize the simplex.
314    let mut simplex = make_simplex_from_cache(cache, proxy_a, &local_proxy_b);
315
316    let mut simplex_index = 0;
317    if let Some(buffer) = simplexes.as_mut() {
318        if simplex_index < buffer.len() {
319            buffer[simplex_index] = simplex;
320            simplex_index += 1;
321        }
322    }
323
324    let mut non_unit_normal = VEC2_ZERO;
325
326    // These store the vertices of the last simplex so that we can check for
327    // duplicates and prevent cycling.
328    let mut save_a = [0i32; 3];
329    let mut save_b = [0i32; 3];
330
331    // Main iteration loop. All computations are done in frame A.
332    let max_iterations = 20;
333    let mut iteration = 0;
334    while iteration < max_iterations {
335        // Copy simplex so we can identify duplicates.
336        let save_count = simplex.count;
337        for i in 0..save_count {
338            save_a[i as usize] = simplex.vertex(i).index_a;
339            save_b[i as usize] = simplex.vertex(i).index_b;
340        }
341
342        let d = match simplex.count {
343            1 => neg(simplex.v1.w),
344            2 => solve_simplex2(&mut simplex),
345            3 => solve_simplex3(&mut simplex),
346            _ => {
347                debug_assert!(false);
348                VEC2_ZERO
349            }
350        };
351
352        // If we have 3 points, then the origin is in the corresponding triangle.
353        if simplex.count == 3 {
354            // Overlap
355            let (local_point_a, local_point_b) = compute_witness_points(&simplex);
356            output.point_a = local_point_a;
357            output.point_b = local_point_b;
358            return output;
359        }
360
361        if let Some(buffer) = simplexes.as_mut() {
362            if simplex_index < buffer.len() {
363                buffer[simplex_index] = simplex;
364                simplex_index += 1;
365            }
366        }
367
368        // Ensure the search direction is numerically fit.
369        if dot(d, d) < f32::EPSILON * f32::EPSILON {
370            // This is unlikely but could lead to bad cycling.
371            // The branch predictor seems to make this check have low cost.
372
373            // The origin is probably contained by a line segment
374            // or triangle. Thus the shapes are overlapped.
375
376            // Must return overlap due to invalid normal.
377            let (local_point_a, local_point_b) = compute_witness_points(&simplex);
378            output.point_a = local_point_a;
379            output.point_b = local_point_b;
380            return output;
381        }
382
383        // Save the normal
384        non_unit_normal = d;
385
386        // Compute a tentative new simplex vertex using support points.
387        // support = support(a, d) - support(b, -d)
388        let index_a = find_support(proxy_a, d);
389        let index_b = find_support(&local_proxy_b, neg(d));
390        let vertex = simplex.vertex_mut(simplex.count);
391        vertex.index_a = index_a;
392        vertex.w_a = proxy_a.points[index_a as usize];
393        vertex.index_b = index_b;
394        vertex.w_b = local_proxy_b.points[index_b as usize];
395        vertex.w = sub(vertex.w_a, vertex.w_b);
396
397        // Iteration count is equated to the number of support point calls.
398        iteration += 1;
399
400        // Check for duplicate support points. This is the main termination criteria.
401        let mut duplicate = false;
402        for i in 0..save_count {
403            if index_a == save_a[i as usize] && index_b == save_b[i as usize] {
404                duplicate = true;
405                break;
406            }
407        }
408
409        // If we found a duplicate support point we must exit to avoid cycling.
410        if duplicate {
411            break;
412        }
413
414        // New vertex is valid and needed.
415        simplex.count += 1;
416    }
417
418    if let Some(buffer) = simplexes.as_mut() {
419        if simplex_index < buffer.len() {
420            buffer[simplex_index] = simplex;
421            simplex_index += 1;
422        }
423    }
424
425    // Prepare output in frame A
426    let normal = normalize(non_unit_normal);
427    debug_assert!(is_normalized(normal));
428
429    let (local_point_a, local_point_b) = compute_witness_points(&simplex);
430    output.normal = normal;
431    output.distance = distance(local_point_a, local_point_b);
432    output.point_a = local_point_a;
433    output.point_b = local_point_b;
434    output.iterations = iteration;
435    output.simplex_count = simplex_index as i32;
436
437    // Cache the simplex
438    *cache = make_simplex_cache(&simplex);
439
440    // Apply radii if requested
441    if input.use_radii {
442        let radius_a = input.proxy_a.radius;
443        let radius_b = input.proxy_b.radius;
444        output.distance = max_float(0.0, output.distance - radius_a - radius_b);
445
446        // Keep closest points on perimeter even if overlapped, this way the
447        // points move smoothly.
448        output.point_a = mul_add(output.point_a, radius_a, normal);
449        output.point_b = mul_sub(output.point_b, radius_b, normal);
450    }
451
452    output
453}