parry3d/query/contact_manifolds/
contact_manifold.rs1use crate::math::{Isometry, Point, Real, Vector};
2use crate::shape::PackedFeatureId;
3#[cfg(feature = "dim3")]
4use alloc::vec::Vec;
5
6#[derive(Copy, Clone, Debug)]
7#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
8#[cfg_attr(
9 feature = "rkyv",
10 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
11 archive(check_bytes)
12)]
13pub struct TrackedContact<Data> {
15 pub local_p1: Point<Real>,
17 pub local_p2: Point<Real>,
19 pub dist: Real,
21
22 pub fid1: PackedFeatureId,
24 pub fid2: PackedFeatureId,
26 pub data: Data,
28}
29
30impl<Data: Default + Copy> TrackedContact<Data> {
31 pub fn new(
33 local_p1: Point<Real>,
34 local_p2: Point<Real>,
35 fid1: PackedFeatureId,
36 fid2: PackedFeatureId,
37 dist: Real,
38 ) -> Self {
39 Self {
40 local_p1,
41 local_p2,
42 fid1,
43 fid2,
44 dist,
45 data: Data::default(),
46 }
47 }
48
49 pub fn flipped(
51 local_p1: Point<Real>,
52 local_p2: Point<Real>,
53 fid1: PackedFeatureId,
54 fid2: PackedFeatureId,
55 dist: Real,
56 flipped: bool,
57 ) -> Self {
58 if !flipped {
59 Self::new(local_p1, local_p2, fid1, fid2, dist)
60 } else {
61 Self::new(local_p2, local_p1, fid2, fid1, dist)
62 }
63 }
64
65 pub fn copy_geometry_from(&mut self, contact: Self) {
67 self.local_p1 = contact.local_p1;
68 self.local_p2 = contact.local_p2;
69 self.fid1 = contact.fid1;
70 self.fid2 = contact.fid2;
71 self.dist = contact.dist;
72 }
73}
74
75#[derive(Clone, Debug, Default)]
76#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
77pub struct ContactManifold<ManifoldData, ContactData> {
82 #[cfg(feature = "dim2")]
86 pub points: arrayvec::ArrayVec<TrackedContact<ContactData>, 2>,
87 #[cfg(feature = "dim3")]
89 pub points: Vec<TrackedContact<ContactData>>,
90 pub local_n1: Vector<Real>,
92 pub local_n2: Vector<Real>,
94 pub subshape1: u32,
98 pub subshape2: u32,
102 pub subshape_pos1: Option<Isometry<Real>>,
105 pub subshape_pos2: Option<Isometry<Real>>,
108 pub data: ManifoldData,
110}
111
112impl<ManifoldData, ContactData: Default + Copy> ContactManifold<ManifoldData, ContactData> {
113 pub fn new() -> Self
115 where
116 ManifoldData: Default,
117 {
118 Self::default()
119 }
120
121 pub fn with_data(subshape1: u32, subshape2: u32, data: ManifoldData) -> Self {
123 Self {
124 #[cfg(feature = "dim2")]
125 points: arrayvec::ArrayVec::new(),
126 #[cfg(feature = "dim3")]
127 points: Vec::new(),
128 local_n1: Vector::zeros(),
129 local_n2: Vector::zeros(),
130 subshape1,
131 subshape2,
132 subshape_pos1: None,
133 subshape_pos2: None,
134 data,
135 }
136 }
137
138 pub fn take(&mut self) -> Self
140 where
141 ManifoldData: Clone,
142 {
143 #[cfg(feature = "dim2")]
144 let points = self.points.clone();
145 #[cfg(feature = "dim3")]
146 let points = core::mem::take(&mut self.points);
147 self.points.clear();
148
149 ContactManifold {
150 points,
151 local_n1: self.local_n1,
152 local_n2: self.local_n2,
153 subshape1: self.subshape1,
154 subshape2: self.subshape2,
155 subshape_pos1: self.subshape_pos1,
156 subshape_pos2: self.subshape_pos2,
157 data: self.data.clone(),
158 }
159 }
160
161 #[inline]
177 pub fn contacts(&self) -> &[TrackedContact<ContactData>] {
178 &self.points
179 }
180
181 #[inline]
183 pub fn try_update_contacts(&mut self, pos12: &Isometry<Real>) -> bool {
184 const DOT_THRESHOLD: Real = crate::utils::COS_1_DEGREES;
187 const DIST_SQ_THRESHOLD: Real = 1.0e-6; self.try_update_contacts_eps(pos12, DOT_THRESHOLD, DIST_SQ_THRESHOLD)
189 }
190
191 #[inline]
193 pub fn try_update_contacts_eps(
194 &mut self,
195 pos12: &Isometry<Real>,
196 angle_dot_threshold: Real,
197 dist_sq_threshold: Real,
198 ) -> bool {
199 if self.points.is_empty() {
200 return false;
201 }
202
203 let local_n2 = pos12 * self.local_n2;
204
205 if -self.local_n1.dot(&local_n2) < angle_dot_threshold {
206 return false;
207 }
208
209 for pt in &mut self.points {
210 let local_p2 = pos12 * pt.local_p2;
211 let dpt = local_p2 - pt.local_p1;
212 let dist = dpt.dot(&self.local_n1);
213
214 if dist * pt.dist < 0.0 {
215 return false;
218 }
219 let new_local_p1 = local_p2 - self.local_n1 * dist;
220
221 if na::distance_squared(&pt.local_p1, &new_local_p1) > dist_sq_threshold {
222 return false;
223 }
224
225 pt.dist = dist;
226 pt.local_p1 = new_local_p1;
227 }
228
229 true
230 }
231
232 pub fn match_contacts(&mut self, old_contacts: &[TrackedContact<ContactData>]) {
235 for contact in &mut self.points {
236 for old_contact in old_contacts {
237 if contact.fid1 == old_contact.fid1 && contact.fid2 == old_contact.fid2 {
238 contact.data = old_contact.data;
240 }
241 }
242 }
243 }
244
245 pub fn match_contacts_using_positions(
248 &mut self,
249 old_contacts: &[TrackedContact<ContactData>],
250 dist_threshold: Real,
251 ) {
252 let sq_threshold = dist_threshold * dist_threshold;
253 for contact in &mut self.points {
254 for old_contact in old_contacts {
255 if na::distance_squared(&contact.local_p1, &old_contact.local_p1) < sq_threshold
256 && na::distance_squared(&contact.local_p2, &old_contact.local_p2) < sq_threshold
257 {
258 contact.data = old_contact.data;
260 }
261 }
262 }
263 }
264
265 pub fn clear(&mut self) {
267 self.points.clear();
268 }
269
270 pub fn find_deepest_contact(&self) -> Option<&TrackedContact<ContactData>> {
272 let mut deepest = self.points.first()?;
273
274 for pt in &self.points {
275 if pt.dist < deepest.dist {
276 deepest = pt;
277 }
278 }
279
280 Some(deepest)
281 }
282}