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#[derive(Clone, Copy, Debug, Default, PartialEq)]
16struct PointMinkowski {
17 pub point_a : Point3 <f64>,
18 pub point_b : Point3 <f64>
19 }
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 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 pub fn query_gjk <A, B> (object_a : &A, object_b : &B) -> Self where
49 A : object::Bounded, B : object::Bounded
50 {
51 const TOLERANCE : f64 = 0.000_000_000_01;
58 const TOLERANCE_SQUARED : f64 = TOLERANCE * TOLERANCE;
59 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 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 let vnew = support.point() - near;
82 let dot_new = vnew.dot (*search_dir);
83 if dot_new <= TOLERANCE {
84 break
86 }
87 simplex.push (support);
89 let mut overlap = false;
90 if simplex.npoints == 4 {
91 let [a, b, c, d] = simplex.points.map (PointMinkowski::point);
93 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;
104 let dac;
105 #[expect(clippy::useless_let_if_seq)]
106 let dbc;
107 if ab_x_ac_dot_ad > 0.0 {
108 dab = db.cross (da);
110 dac = da.cross (dc);
111 dbc = dc.cross (db);
112 } else {
113 dab = da.cross (db);
115 dac = dc.cross (da);
116 dbc = db.cross (dc);
117 }
118 let check1 = dab.dot (nd) > 0.000000001;
120 let check2 = dac.dot (nd) > 0.000000001;
121 let check3 = dbc.dot (nd) > 0.000000001;
122 if check1 && check2 && check3 {
123 overlap = true
124 } else {
125 let line = geometry::Segment3::noisy (Point3::<f64>::origin(), near).into();
127 if let Some (triangle) = geometry::Triangle3::new (d, a, b)
129 && geometry::intersect::line_triangle (line, triangle).is_some()
130 {
131 simplex.points[2] = simplex.points[3];
133 } else {
134 if let Some (triangle) = geometry::Triangle3::new (d, a, c)
136 && geometry::intersect::line_triangle (line, triangle).is_some()
137 {
138 simplex.points[1] = simplex.points[3];
140 } else {
141 if let Some (triangle) = geometry::Triangle3::new (d, b, c)
143 && geometry::intersect::line_triangle (line, triangle).is_some()
144 {
145 simplex.points[0] = simplex.points[3];
147 } else {
148 unreachable!("should have intersected new simplex")
149 }
150 }
151 }
152 simplex.npoints = 3;
153 }
154 }
155 if !overlap {
156 match simplex.npoints {
158 1 => {
159 near = simplex.points[0].point();
160 distance_squared = near.0.magnitude_squared();
161 if distance_squared < TOLERANCE_SQUARED {
162 overlap = true
163 } else {
164 search_dir = NonZero3::new (-near.0).unwrap()
166 }
167 }
168 2 => {
169 let t;
170 (t, near) = geometry::Segment3::unchecked (
171 simplex.points[0].point(), simplex.points[1].point()
172 ).nearest_point (Point3::origin());
173 distance_squared = near.0.magnitude_squared();
174 if distance_squared < TOLERANCE_SQUARED {
175 overlap = true
176 } else if *t == 1.0 {
177 simplex.points[0] = simplex.points[1];
179 simplex.npoints -= 1;
180 search_dir = NonZero3::unchecked (-simplex.points[0].point().0)
181 } else {
182 search_dir = NonZero3::unchecked (-near.0)
184 }
185 }
186 3 => {
187 let (s, t);
188 ([s, t], near) = geometry::Triangle3::unchecked (
189 simplex.points[0].point(),
190 simplex.points[1].point(),
191 simplex.points[2].point()
192 ).nearest_point (Point3::origin());
193 distance_squared = near.0.magnitude_squared();
194 if distance_squared < TOLERANCE_SQUARED {
195 overlap = true
196 } else {
197 search_dir = NonZero3::unchecked (-near.0);
199 if *s == 0.0 && *t == 0.0 {
200 simplex.npoints = 1;
202 } else if *s == 1.0 && *t == 0.0 {
203 simplex.points[0] = simplex.points[1];
205 simplex.npoints = 1;
206 } else if *s == 0.0 && *t == 1.0 {
207 simplex.points[0] = simplex.points[2];
209 simplex.npoints = 1;
210 } else if (*s + *t - 1.0).abs() < TOLERANCE {
211 simplex.points[0] = simplex.points[1];
213 simplex.points[1] = simplex.points[2];
214 simplex.npoints = 2;
215 } else if *s < TOLERANCE {
216 simplex.points[1] = simplex.points[2];
218 simplex.npoints = 2;
219 } else if *t < TOLERANCE {
220 simplex.npoints = 2;
222 } else {
223 debug_assert!(*s > 0.0);
225 debug_assert!(*s < 1.0);
226 debug_assert!(*t > 0.0);
227 debug_assert!(*t < 1.0);
228 debug_assert!(*s + *t < 1.0);
229 }
230 }
231 }
232 _ => unreachable!()
233 }
234 }
235 if overlap {
236 #[derive(PartialEq)]
239 struct PolytopeFace {
240 pub distance_squared : NonNegative <f64>,
242 pub distance_vector : Vector3 <f64>,
244 pub nearest_a : Point3 <f64>,
245 pub nearest_b : Point3 <f64>,
246 pub points : [PointMinkowski; 3],
247 pub normal : NonZero3 <f64>,
248 pub exterior : bool
249 }
250 impl PartialOrd for PolytopeFace {
251 fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
252 self.distance_squared.partial_cmp (&other.distance_squared)
253 }
254 }
255 let mut node_list = ReverseSortedVec::<PolytopeFace>::new();
256 let add_face = |
257 node_list : &mut ReverseSortedVec <PolytopeFace>,
258 p1 : PointMinkowski, p2 : PointMinkowski, p3 : PointMinkowski
259 | {
260 let triangle = geometry::Triangle3::noisy (p1.point(), p2.point(), p3.point());
261 let ([s, t], nearest) = triangle.nearest_point (Point3::origin());
262 let nearest_a = p1.point_a +
263 (p2.point_a - p1.point_a) * *s + (p3.point_a - p1.point_a) * *t;
264 let nearest_b = p1.point_b +
265 (p2.point_b - p1.point_b) * *s + (p3.point_b - p1.point_b) * *t;
266 let normal = {
267 let mut normal = triangle.normal();
268 if normal.dot (triangle.point_a().0) < 0.0 {
269 normal = -normal
270 }
271 normal
272 };
273 let node = PolytopeFace {
274 distance_squared: nearest.0.norm_squared(),
275 distance_vector: -nearest.0,
276 points: [p1, p2, p3],
277 exterior: false,
278 nearest_a,
279 nearest_b,
280 normal
281 };
282 node_list.insert (node);
283 };
284 let a = simplex.points[0];
285 let b = simplex.points[1];
286 let c = simplex.points[2];
287 let d = simplex.points[3];
288 match simplex.npoints {
289 3 => {
290 let e1 = simplex.points[1].point() - simplex.points[0].point();
292 let e2 = simplex.points[2].point() - simplex.points[0].point();
293 let n1 = NonZero3::noisy (e1.cross (e2));
294 let n2 = -n1;
295 let (p1, dot) = PointMinkowski::support (n1, object_a, object_b);
297 if dot <= TOLERANCE {
298 let half_axis = *n1 * TOLERANCE * 0.5;
299 return Proximity {
300 distance: -TOLERANCE.sqrt(),
301 normal: n1.normalize(),
302 midpoint: p1.point_a + half_axis,
303 half_axis
304 }
305 }
306 add_face (&mut node_list, a, b, p1);
307 add_face (&mut node_list, a, c, p1);
308 add_face (&mut node_list, b, c, p1);
309 let (p2, dot) = PointMinkowski::support (n2, object_a, object_b);
311 if dot <= TOLERANCE {
312 let half_axis = *n2 * TOLERANCE * 0.5;
313 return Proximity {
314 distance: -TOLERANCE.sqrt(),
315 normal: n2.normalize(),
316 midpoint: p2.point_a + half_axis,
317 half_axis
318 }
319 }
320 add_face (&mut node_list, a, b, p2);
321 add_face (&mut node_list, a, c, p2);
322 add_face (&mut node_list, b, c, p2);
323 }
324 4 => {
325 add_face (&mut node_list, a, b, c);
326 add_face (&mut node_list, a, b, d);
327 add_face (&mut node_list, a, c, d);
328 add_face (&mut node_list, b, c, d);
329 }
330 _ => unreachable!()
331 }
332 let mut edges = vec![];
338 let mut edge_in_list = vec![];
339 let mut last_iter_distance = f64::MIN;
340 let exterior_node_proximity = |node : PolytopeFace| {
341 let half_axis = node.distance_vector * 0.5;
342 Proximity {
343 half_axis,
344 distance: -*node.distance_squared.sqrt(),
345 normal: half_axis.normalize(), midpoint: node.nearest_a + half_axis
347 }
348 };
349 let mut iter = 0;
350 let noprogress_limit = 3;
351 let mut noprogress_count = 0;
352 loop {
353 let mut node = node_list.pop().unwrap();
354 if approx::relative_eq!(*node.distance_squared, last_iter_distance,
355 epsilon = 0.000000001
356 ) {
357 noprogress_count += 1;
359 if noprogress_count == noprogress_limit {
360 log::trace!("expanding polytope search no progress after {iter} iterations");
361 return exterior_node_proximity (node)
362 }
363 } else {
364 noprogress_count = 0;
365 }
366 last_iter_distance = *node.distance_squared;
367 let (pa, _) = PointMinkowski::support (node.normal, object_a, object_b);
369 if node.points.contains (&pa) {
370 node.exterior = true;
372 } else {
373 let bpa = pa.point() - node.points[0].point();
375 let normal_unit = node.normal.normalize();
377 if normal_unit.dot (bpa) < TOLERANCE {
378 node.exterior = true;
379 }
380 }
381 if node.exterior {
382 return exterior_node_proximity (node)
384 } else {
385 edges.clear();
388 edge_in_list.clear();
389 edges.extend ([
390 [node.points[0], node.points[1]],
391 [node.points[0], node.points[2]],
392 [node.points[1], node.points[2]]
393 ]);
394 edge_in_list.extend ([true, true, true]);
395 node_list.retain (|node|{
398 let p0_pa = pa.point() - node.points[0].point();
399 if TOLERANCE < node.normal.dot (p0_pa) {
400 let mut add_edge = |i : usize, j : usize| {
402 let mut found = false;
403 for (k, edge) in edges.iter().enumerate()
405 .filter (|(k, _)| edge_in_list[*k])
406 {
407 if node.points[i].point() == edge[0].point()
408 && node.points[j].point() == edge[1].point()
409 || node.points[i].point() == edge[1].point()
410 && node.points[j].point() == edge[0].point()
411 {
412 found = true;
413 edge_in_list[k] = false; break
415 }
416 }
417 if !found {
418 edge_in_list.push (true);
420 edges.push ([node.points[i], node.points[j]]);
421 }
422 };
423 add_edge (0, 1); add_edge (0, 2); add_edge (1, 2); false } else {
428 true }
430 });
431 for (_, edge) in edges.iter().enumerate()
434 .filter (|(i, _)| edge_in_list[*i])
435 {
436 if geometry::Triangle3::new (pa.point(), edge[0].point(), edge[1].point())
437 .is_none()
438 {
439 continue
441 }
442 add_face (&mut node_list, pa, edge[0], edge[1]);
443 }
444 }
445 iter += 1;
446 }
447 } if simplex.points[0].point_a == simplex.points[1].point_a
450 && simplex.points[1].point_a == simplex.points[2].point_a
451 {
452 unique_count += 1;
453 } else if simplex.points[0].point_a == simplex.points[1].point_a
454 || simplex.points[0].point_a == simplex.points[2].point_a
455 || simplex.points[1].point_a == simplex.points[2].point_a
456 {
457 unique_count += 2;
458 } else {
459 debug_assert!(
460 simplex.points[0].point_a != simplex.points[1].point_a &&
461 simplex.points[0].point_a != simplex.points[2].point_a &&
462 simplex.points[1].point_a != simplex.points[2].point_a);
463 unique_count += 3;
464 }
465 if simplex.points[0].point_b == simplex.points[1].point_b
466 && simplex.points[1].point_b == simplex.points[2].point_b
467 {
468 unique_count += 1;
469 } else if simplex.points[0].point_b == simplex.points[1].point_b
470 || simplex.points[0].point_b == simplex.points[2].point_b
471 || simplex.points[1].point_b == simplex.points[2].point_b
472 {
473 unique_count += 2;
474 } else {
475 debug_assert!(
476 simplex.points[0].point_b != simplex.points[1].point_b &&
477 simplex.points[0].point_b != simplex.points[2].point_b &&
478 simplex.points[1].point_b != simplex.points[2].point_b);
479 unique_count += 3;
480 }
481 if distance_squared < lowest_distance_sq ||
483 distance_squared == lowest_distance_sq && unique_count < lowest_unique_count
484 {
485 lowest_distance_sq = distance_squared;
486 lowest_unique_count = unique_count;
487 } else if distance_squared >= last_distance_sq {
488 if noprogress_count < noprogress_limit {
489 noprogress_count += 1;
490 } else {
491 break
493 }
494 }
495 last_distance_sq = distance_squared;
496 }
497 let mut dupe_a_01 = false;
500 let mut dupe_a_02 = false;
501 let mut dupe_a_12 = false;
502 let mut dupe_b_01 = false;
503 let mut dupe_b_02 = false;
504 let mut dupe_b_12 = false;
505 if simplex.npoints >= 2 {
506 dupe_a_01 = simplex.points[0].point_a == simplex.points[1].point_a;
507 dupe_b_01 = simplex.points[0].point_b == simplex.points[1].point_b;
508 }
509 if simplex.npoints >= 3 {
510 dupe_a_02 = simplex.points[0].point_a == simplex.points[2].point_a;
511 dupe_a_12 = simplex.points[1].point_a == simplex.points[2].point_a;
512 dupe_b_02 = simplex.points[0].point_b == simplex.points[2].point_b;
513 dupe_b_12 = simplex.points[1].point_b == simplex.points[2].point_b;
514 }
515 debug_assert!(!(simplex.npoints == 2 && unique_count == 2),
518 "only one unique point: not possible");
519 match simplex.npoints {
521 1 => {
522 let nearest_a = simplex.points[0].point_a;
524 let nearest_b = simplex.points[0].point_b;
525 let a_to_b = nearest_b - nearest_a;
526 let half_axis = a_to_b * 0.5;
527 let midpoint = nearest_a + half_axis;
528 let normal = if !a_to_b.is_approx_zero() {
529 Unit3::normalize (-a_to_b)
530 } else {
531 Unit3::normalize (object_a.position().0 - object_b.position().0)
533 };
534 let distance = a_to_b.magnitude();
535 Proximity { distance, half_axis, midpoint, normal }
536 }
537 2 => {
538 let point0_a = simplex.points[0].point_a;
540 let point1_a = simplex.points[1].point_a;
541 let point0_b = simplex.points[0].point_b;
542 let point1_b = simplex.points[1].point_b;
543 if !dupe_a_01 && !dupe_b_01 {
544 let edge_a = geometry::Segment3::noisy (point0_a, point1_a);
546 let edge_b = geometry::Segment3::noisy (point0_b, point1_b);
547 Proximity::query_segment_segment (&edge_a, &edge_b)
548 } else {
549 if dupe_a_01 {
551 debug_assert!(!dupe_b_01);
553 debug_assert_eq!(point0_a, point1_a);
554 let point_a = point0_a;
555 let edge_b = geometry::Segment3::noisy (point0_b, point1_b);
556 Proximity::query_segment_point (&edge_b, &point_a).flip()
558 } else {
559 debug_assert!(dupe_b_01);
561 let point_b = point0_b;
562 let edge_a = geometry::Segment3::noisy (point0_a, point1_a);
563 Proximity::query_segment_point (&edge_a, &point_b)
564 }
565 }
566 }
567 3 => {
568 let is_edge = |
570 p0 : &mut Point3 <f64>, p1 : &mut Point3 <f64>, p2 : &Point3 <f64>
571 | {
572 let p01 = *p1 - *p0;
573 let p02 = *p2 - *p0;
574 let p01_cross_p02 = p01.cross (p02);
575 if p01_cross_p02.is_approx_zero() {
576 let p12 = *p2 - *p1;
578 let dot_p01 = p01.magnitude_squared();
579 let dot_p02 = p02.magnitude_squared();
580 let dot_p12 = p12.magnitude_squared();
581 if dot_p01 > dot_p02 && dot_p01 > dot_p12 {
583 } else if dot_p02 > dot_p01 && dot_p02 > dot_p12 {
585 *p1 = *p2;
586 } else {
587 debug_assert!(dot_p12 > dot_p01 && dot_p12 > dot_p02);
588 *p0 = *p1;
589 *p1 = *p2;
590 }
591 true
592 } else {
593 false
594 }
595 };
596 debug_assert!(!((dupe_a_01 && dupe_a_12) && (dupe_b_01 && dupe_b_12)));
598 if (!dupe_a_01 && !dupe_a_02 && !dupe_a_12) &&
599 (!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
600 {
601 let mut a0 = simplex.points[0].point_a;
603 let mut a1 = simplex.points[1].point_a;
604 let a2 = simplex.points[2].point_a;
605 let mut b0 = simplex.points[0].point_b;
606 let mut b1 = simplex.points[1].point_b;
607 let b2 = simplex.points[2].point_b;
608 let edge_a = is_edge (&mut a0, &mut a1, &a2);
609 let edge_b = is_edge (&mut b0, &mut b1, &b2);
610 if edge_a && edge_b {
611 let edge_a = geometry::Segment3::noisy (a0, a1);
613 let edge_b = geometry::Segment3::noisy (b0, b1);
614 Proximity::query_segment_segment (&edge_a, &edge_b)
615 } else if edge_a && !edge_b {
616 let edge_a = geometry::Segment3::noisy (a0, a1);
618 let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
619 Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
621 } else if !edge_a && edge_b {
622 let edge_b = geometry::Segment3::noisy (b0, b1);
624 let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
625 Proximity::query_triangle_segment (&triangle_a, &edge_b)
626 } else {
627 debug_assert!(!edge_a);
628 debug_assert!(!edge_b);
629 let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
631 let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
632 Proximity::query_triangle_triangle (&triangle_a, &triangle_b)
633 }
634 } else if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
635 && (!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
636 {
637 let a0 = simplex.points[0].point_a;
644 let mut a1 = simplex.points[1].point_a;
645 let mut b0 = simplex.points[0].point_b;
646 let mut b1 = simplex.points[1].point_b;
647 let b2 = simplex.points[2].point_b;
648 if dupe_a_01 || dupe_a_12 {
649 a1 = simplex.points[2].point_a;
650 }
651 let edge_b = is_edge (&mut b0, &mut b1, &b2);
652 let edge_a = geometry::Segment3::noisy (a0, a1);
653 if edge_b {
654 let edge_b = geometry::Segment3::noisy (b0, b1);
656 Proximity::query_segment_segment (&edge_a, &edge_b)
657 } else {
658 let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
660 Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
662 }
663 } else if (dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12)
664 && (!dupe_a_01 && !dupe_a_02 && !dupe_a_12)
665 {
666 let mut a0 = simplex.points[0].point_a;
668 let mut a1 = simplex.points[1].point_a;
669 let a2 = simplex.points[2].point_a;
670 let b0 = simplex.points[0].point_b;
671 let b1 = if dupe_b_01 || dupe_b_12 {
672 simplex.points[2].point_b
673 } else {
674 simplex.points[1].point_b
675 };
676 let edge_a = is_edge (&mut a0, &mut a1, &a2);
677 if edge_a {
678 let edge_a = geometry::Segment3::noisy (a0, a1);
680 let edge_b = geometry::Segment3::noisy (b0, b1);
681 Proximity::query_segment_segment (&edge_a, &edge_b)
682 } else {
683 let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
685 let edge_b = geometry::Segment3::noisy (b0, b1);
686 Proximity::query_triangle_segment (&triangle_a, &edge_b)
687 }
688 } else {
689 if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
690 && !(dupe_a_01 && dupe_a_02 && dupe_a_12) {
692 debug_assert!(dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12);
695 debug_assert!(!(dupe_b_01 && dupe_b_02 && dupe_b_12));
696 debug_assert!(!(dupe_a_01 && dupe_b_01));
697 debug_assert!(!(dupe_a_02 && dupe_b_02));
698 debug_assert!(!(dupe_a_12 && dupe_b_12));
699 if (dupe_a_01 != dupe_a_02) && (dupe_b_01 != dupe_b_02) {
704 if dupe_a_02 {
706 debug_assert!(dupe_b_01);
709 } else {
711 debug_assert!(dupe_a_01);
714 debug_assert!(dupe_b_02);
715 simplex.points.swap(1, 2);
716 }
717 } else if (dupe_a_01 != dupe_a_12) && (dupe_b_01 != dupe_b_12) {
718 let temp = simplex.points[0];
720 simplex.points[0] = simplex.points[1];
721 if dupe_a_01 {
722 debug_assert!(dupe_b_12);
725 simplex.points[1] = simplex.points[2];
726 simplex.points[2] = temp;
727 } else {
728 debug_assert!(dupe_a_12);
731 debug_assert!(dupe_b_01);
732 simplex.points[1] = temp;
733 }
734 } else {
735 debug_assert!(dupe_a_02 != dupe_a_12);
737 debug_assert!(dupe_b_02 != dupe_b_12);
738 let temp = simplex.points[0];
739 simplex.points[0] = simplex.points[2];
740 if dupe_a_02 {
741 debug_assert!(dupe_b_12);
744 simplex.points[2] = temp;
745 } else {
746 debug_assert!(dupe_a_12);
749 debug_assert!(dupe_b_02);
750 simplex.points[2] = simplex.points[1];
751 simplex.points[1] = temp;
752 }
753 }
754 let a0 = simplex.points[0].point_a;
755 let a1 = simplex.points[1].point_a;
756 let b0 = simplex.points[0].point_b;
757 let b2 = simplex.points[2].point_b;
759 let edge_a = geometry::Segment3::noisy (a0, a1);
760 let edge_b = geometry::Segment3::noisy (b0, b2);
761 Proximity::query_segment_segment (&edge_a, &edge_b)
762 } else {
763 debug_assert!((dupe_a_01 && dupe_a_12) != (dupe_b_01 && dupe_b_12));
766 if dupe_a_01 && dupe_a_12 {
767 debug_assert!(dupe_a_02); let point_a = simplex.points[0].point_a;
770 let b0 = simplex.points[0].point_b;
771 let b1 = simplex.points[1].point_b;
772 let b2 = simplex.points[2].point_b;
773 let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
774 Proximity::query_triangle_point (&triangle_b, &point_a).flip()
776 } else {
777 debug_assert!(dupe_b_01 && dupe_b_12);
779 debug_assert!(dupe_b_02); let point_b = simplex.points[0].point_b;
781 let a0 = simplex.points[0].point_a;
782 let a1 = simplex.points[1].point_a;
783 let a2 = simplex.points[2].point_a;
784 let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
785 Proximity::query_triangle_point (&triangle_a, &point_b)
786 }
787 }
788 }
789 }
790 _ => unreachable!()
791 }
792 }
793}
794
795#[cfg(test)]
796mod tests {
797 use approx;
798 use gl_utils::{mesh, Mesh};
799 use rand;
800 use rand_distr;
801 use rand_xorshift::XorShiftRng;
802 use crate::math::geometry::*;
803 use crate::component;
804 use super::*;
805
806 #[test]
807 fn query_gjk() {
808 use rand::SeedableRng;
809 use rand_distr::Distribution;
810
811 let tetrahedron = |p1 : [f64; 3], p2 : [f64; 3], p3 : [f64; 3], p4 : [f64; 3]|
812 -> (component::Position, component::Bound)
813 {
814 ( component::Position::origin(),
815 Hull3::from_points_with_mesh (
816 &simplex3::Tetrahedron::<f64>::noisy (
817 p1.into(), p2.into(), p3.into(), p4.into()
818 ).points()
819 ).unwrap().into()
820 )
821 };
822 let a = tetrahedron (
823 [-2.0, 2.0, -1.0],
824 [ 2.0, 2.0, -1.0],
825 [ 0.0, -2.0, -1.0],
826 [ 0.0, 0.0, 2.0]);
827 let b = tetrahedron (
828 [-2.0, 2.0, 3.5],
829 [ 2.0, 2.0, 3.5],
830 [ 0.0, -2.0, 3.5],
831 [ 0.0, 0.0, 6.5]);
832 let proximity = Proximity::query_gjk (&a, &b);
833 assert_eq!(proximity.distance, 1.5);
834 assert_eq!(proximity.half_axis, [0.0, 0.0, 0.75].into());
835 assert_eq!(proximity.midpoint, [0.0, 0.0, 2.75].into());
836 assert_eq!(proximity.normal, -Unit3::axis_z());
837 let a = tetrahedron (
838 [-2.0, 2.0, -1.0],
839 [ 2.0, 2.0, -1.0],
840 [ 0.0, -2.0, -1.0],
841 [ 0.0, 0.0, 2.0]);
842 let b = tetrahedron (
843 [-2.0, 2.0, 1.75],
844 [ 2.0, 2.0, 1.75],
845 [ 0.0, -2.0, 1.75],
846 [ 0.0, 0.0, 4.75]);
847 let proximity = Proximity::query_gjk (&a, &b);
848 assert_eq!(proximity.distance, -0.25);
849 assert_eq!(proximity.half_axis, [0.0, 0.0, -0.125].into());
850 assert_eq!(proximity.midpoint, [0.0, 0.0, 1.875].into());
851 assert_eq!(proximity.normal, -Unit3::axis_z());
852 let a = tetrahedron (
853 [ 0.0, 0.0, 3.5],
854 [-2.0, 2.0, 0.5],
855 [ 2.0, 2.0, 0.5],
856 [ 0.0, -2.0, 0.5]);
857 let b = tetrahedron (
858 [-2.0, 2.0, 1.0],
859 [ 2.0, 2.0, 1.0],
860 [ 0.0, -2.0, 1.0],
861 [ 0.0, 0.0, -2.0]);
862 let proximity = Proximity::query_gjk (&a, &b);
863 assert_eq!(proximity.distance, -0.5);
864 approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
865 assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
866 approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
867 epsilon = f64::EPSILON * 4.0);
868 let hull = |points : &[Point3 <f64>]| -> (component::Position, component::Bound) {
869 ( component::Position::origin(),
870 Hull3::from_points_with_mesh (points).unwrap().into()
871 )
872 };
873 let a = hull (&[
874 [ 0.0, 0.0, 3.5],
875 [ 0.0, 0.0, 0.5],
876 [-2.0, 2.0, 0.5],
877 [ 2.0, 2.0, 0.5],
878 [ 0.0, -2.0, 0.5]].map (Into::into));
879 let b = hull (&[
880 [-2.0, 2.0, 1.0],
881 [ 2.0, 2.0, 1.0],
882 [ 0.0, -2.0, 1.0],
883 [ 0.0, 0.0, 1.0],
884 [ 0.0, 0.0, -2.0]].map (Into::into));
885 let proximity = Proximity::query_gjk (&a, &b);
886 assert_eq!(proximity.distance, -0.5);
887 approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
888 assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
889 approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
890 epsilon = f64::EPSILON * 4.0);
891
892 {
893 const NPOINTS : usize = 4;
894 let mut rng = XorShiftRng::seed_from_u64 (0);
895 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
896 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
897 let aabb = Aabb3::with_minmax (
898 [-5.0, -5.0, -5.0].into(),
899 [ 5.0, 5.0, 5.0].into()).unwrap();
900 let rand_point = |rng : &mut XorShiftRng|
901 aabb.clamp (point3 (randf (rng), randf (rng), randf (rng)));
902 let object = |hull : shape::Hull <f64>|
903 (component::Position::origin(), component::Bound::from (hull));
904 #[expect(unused_variables)]
905 let write_gltf = |
906 (hull, mesh) : &shape::Hull <f64>,
907 filename : &str
908 | {
909 let mut m = mesh::Triangles3dBuilder::empty();
910 for triangle in mesh.triangles().values() {
911 let triangle = hull.triangle (triangle);
912 m.push_face (triangle.points());
913 }
914 Mesh::from (m.build()).write_gltf (filename);
915 };
916 for _i in 0..1000 {
917 let a = {
919 let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
920 .collect::<Vec<_>>();
921 let hull = Hull3::from_points_with_mesh (&points).unwrap();
922 object (hull)
924 };
925 let b = {
926 let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
927 .collect::<Vec<_>>();
928 let hull = Hull3::from_points_with_mesh (&points).unwrap();
929 object (hull)
931 };
932 let _proximity = Proximity::query_gjk (&a, &b);
935 }
936 }
937 }
938}