parry2d/query/contact/
contact_cuboid_cuboid.rs1use crate::math::{Isometry, Real};
2use crate::query::{sat, Contact, PointQuery};
3use crate::shape::{Cuboid, SupportMap};
4use approx::AbsDiffEq;
5use na::Unit;
6
7#[inline]
9pub fn contact_cuboid_cuboid(
10 pos12: &Isometry<Real>,
11 cuboid1: &Cuboid,
12 cuboid2: &Cuboid,
13 prediction: Real,
14) -> Option<Contact> {
15 let pos21 = pos12.inverse();
16
17 let sep1 = sat::cuboid_cuboid_find_local_separating_normal_oneway(cuboid1, cuboid2, pos12);
18 if sep1.0 > prediction {
19 return None;
20 }
21
22 let sep2 = sat::cuboid_cuboid_find_local_separating_normal_oneway(cuboid2, cuboid1, &pos21);
23 if sep2.0 > prediction {
24 return None;
25 }
26
27 #[cfg(feature = "dim2")]
28 let sep3 = (-Real::MAX, crate::math::Vector::<Real>::y()); #[cfg(feature = "dim3")]
30 let sep3 = sat::cuboid_cuboid_find_local_separating_edge_twoway(cuboid1, cuboid2, pos12);
31 if sep3.0 > prediction {
32 return None;
33 }
34
35 if sep1.0 >= sep2.0 && sep1.0 >= sep3.0 {
37 let pt2_1 = cuboid2.support_point(pos12, &-sep1.1);
41 let proj1 = cuboid1.project_local_point(&pt2_1, false);
42
43 let separation = (pt2_1 - proj1.point).dot(&sep1.1);
44 let normalized_dir = Unit::try_new_and_get(pt2_1 - proj1.point, Real::default_epsilon());
45 let normal1;
46 let dist;
47
48 if separation < 0.0 || normalized_dir.is_none() {
51 normal1 = Unit::new_unchecked(sep1.1);
53 dist = separation;
54 } else {
55 let Some((dir, norm)) = normalized_dir else {
56 unreachable!()
57 };
58 normal1 = dir;
60 dist = norm;
61 }
62
63 if dist > prediction {
64 return None;
65 }
66
67 return Some(Contact::new(
68 proj1.point,
69 pos12.inverse_transform_point(&pt2_1),
70 normal1,
71 pos12.inverse_transform_unit_vector(&-normal1),
72 dist,
73 ));
74 }
75
76 if sep2.0 >= sep1.0 && sep2.0 >= sep3.0 {
78 let pt1_2 = cuboid1.support_point(&pos21, &-sep2.1);
82 let proj2 = cuboid2.project_local_point(&pt1_2, false);
83
84 let separation = (pt1_2 - proj2.point).dot(&sep2.1);
85 let normalized_dir = Unit::try_new_and_get(pt1_2 - proj2.point, Real::default_epsilon());
86 let normal2;
87 let dist;
88
89 if separation < 0.0 || normalized_dir.is_none() {
92 normal2 = Unit::new_unchecked(sep2.1);
94 dist = separation;
95 } else {
96 let Some((dir, norm)) = normalized_dir else {
98 unreachable!()
99 };
100 normal2 = dir;
101 dist = norm;
102 }
103
104 if dist > prediction {
105 return None;
106 }
107
108 return Some(Contact::new(
109 pos12.transform_point(&pt1_2),
110 proj2.point,
111 pos12 * -normal2,
112 normal2,
113 dist,
114 ));
115 }
116
117 #[cfg(feature = "dim3")]
119 if sep3.0 >= sep2.0 && sep3.0 >= sep1.0 {
120 use crate::query::{details, ClosestPoints};
121 let edge1 = cuboid1.local_support_edge_segment(sep3.1);
124 let edge2 = cuboid2.local_support_edge_segment(pos21 * -sep3.1);
125
126 match details::closest_points_segment_segment(pos12, &edge1, &edge2, prediction) {
127 ClosestPoints::Disjoint => return None,
128 ClosestPoints::WithinMargin(a, b) => {
129 let normal1 = Unit::new_unchecked(sep3.1);
130 let normal2 = pos12.inverse_transform_unit_vector(&-normal1);
131 return Some(Contact::new(a, b, normal1, normal2, sep3.0));
132 }
133 ClosestPoints::Intersecting => unreachable!(),
134 }
135 }
136
137 unreachable!()
138}