parry3d/query/contact_manifolds/contact_manifold.rs
1use 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)]
13/// A single contact point between two shapes.
14///
15/// A `TrackedContact` represents a single point of contact between two shapes, with enough
16/// information to track the contact across multiple frames and identify which geometric
17/// features (vertices, edges, faces) are in contact.
18///
19/// # Understanding Contact Points
20///
21/// Each contact point consists of:
22/// - Two contact positions (one on each shape, in local coordinates)
23/// - A distance value (negative = penetrating, positive = separated)
24/// - Feature IDs that identify which part of each shape is in contact
25/// - Optional user data for tracking contact-specific information
26///
27/// # Local vs World Space
28///
29/// Contact points are stored in **local space** (the coordinate system of each shape).
30/// This is important because:
31/// - Shapes can move and rotate, but local coordinates remain constant
32/// - Contact tracking works by comparing feature IDs and local positions
33/// - To get world-space positions, transform the local points by the shape's position
34///
35/// # Distance Convention
36///
37/// The `dist` field uses the following convention:
38/// - `dist < 0.0`: Shapes are penetrating (overlapping). The absolute value is the penetration depth.
39/// - `dist == 0.0`: Shapes are exactly touching.
40/// - `dist > 0.0`: Shapes are separated. This happens when using contact prediction.
41///
42/// # Example: Basic Contact Query
43///
44/// ```rust
45/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
46/// use parry3d::query::{ContactManifold, TrackedContact};
47/// use parry3d::query::details::contact_manifold_ball_ball;
48/// use parry3d::shape::Ball;
49/// use parry3d::math::Isometry;
50///
51/// // Two balls, one slightly overlapping the other
52/// let ball1 = Ball::new(1.0);
53/// let ball2 = Ball::new(1.0);
54/// let pos12 = Isometry::translation(1.5, 0.0, 0.0); // Overlapping by 0.5
55///
56/// let mut manifold = ContactManifold::<(), ()>::new();
57/// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
58///
59/// if let Some(contact) = manifold.points.first() {
60/// println!("Penetration depth: {}", -contact.dist);
61/// println!("Contact on ball1 (local): {:?}", contact.local_p1);
62/// println!("Contact on ball2 (local): {:?}", contact.local_p2);
63/// }
64/// # }
65/// ```
66///
67/// # Example: Converting to World Space
68///
69/// ```rust
70/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
71/// use parry3d::query::{ContactManifold, TrackedContact};
72/// use parry3d::query::details::contact_manifold_ball_ball;
73/// use parry3d::shape::Ball;
74/// use parry3d::math::Isometry;
75///
76/// let ball1 = Ball::new(1.0);
77/// let ball2 = Ball::new(1.0);
78///
79/// // Position shapes in world space
80/// let pos1 = Isometry::translation(0.0, 0.0, 0.0);
81/// let pos2 = Isometry::translation(1.5, 0.0, 0.0);
82/// let pos12 = pos1.inverse() * pos2; // Relative position
83///
84/// let mut manifold = ContactManifold::<(), ()>::new();
85/// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
86///
87/// if let Some(contact) = manifold.points.first() {
88/// // Convert local positions to world space
89/// let world_p1 = pos1 * contact.local_p1;
90/// let world_p2 = pos2 * contact.local_p2;
91///
92/// println!("Contact in world space:");
93/// println!(" On ball1: {:?}", world_p1);
94/// println!(" On ball2: {:?}", world_p2);
95/// }
96/// # }
97/// ```
98///
99/// # Feature IDs
100///
101/// The `fid1` and `fid2` fields identify which geometric features are in contact:
102/// - For a ball: Always the face (surface)
103/// - For a box: Could be a vertex, edge, or face
104/// - For a triangle: Could be a vertex, edge, or the face
105///
106/// These IDs are used to track contacts across frames. If the same feature IDs appear
107/// in consecutive frames, it's likely the same physical contact point.
108pub struct TrackedContact<Data> {
109 /// The contact point in the local-space of the first shape.
110 ///
111 /// This is the point on the first shape's surface (or interior if penetrating)
112 /// that is closest to or in contact with the second shape.
113 pub local_p1: Point<Real>,
114
115 /// The contact point in the local-space of the second shape.
116 ///
117 /// This is the point on the second shape's surface (or interior if penetrating)
118 /// that is closest to or in contact with the first shape.
119 pub local_p2: Point<Real>,
120
121 /// The signed distance between the two contact points.
122 ///
123 /// - Negative values indicate penetration (shapes are overlapping)
124 /// - Positive values indicate separation (used with contact prediction)
125 /// - Zero means the shapes are exactly touching
126 ///
127 /// The magnitude represents the distance along the contact normal.
128 pub dist: Real,
129
130 /// The feature ID of the first shape involved in the contact.
131 ///
132 /// This identifies which geometric feature (vertex, edge, or face) of the first
133 /// shape is involved in this contact. Used for contact tracking across frames.
134 pub fid1: PackedFeatureId,
135
136 /// The feature ID of the second shape involved in the contact.
137 ///
138 /// This identifies which geometric feature (vertex, edge, or face) of the second
139 /// shape is involved in this contact. Used for contact tracking across frames.
140 pub fid2: PackedFeatureId,
141
142 /// User-data associated to this contact.
143 ///
144 /// This can be used to store any additional information you need to track
145 /// per-contact, such as:
146 /// - Accumulated impulses for warm-starting in physics solvers
147 /// - Contact age or lifetime
148 /// - Material properties or friction state
149 /// - Custom identifiers or flags
150 pub data: Data,
151}
152
153impl<Data: Default + Copy> TrackedContact<Data> {
154 /// Creates a new tracked contact.
155 ///
156 /// # Arguments
157 ///
158 /// * `local_p1` - Contact point on the first shape (in its local space)
159 /// * `local_p2` - Contact point on the second shape (in its local space)
160 /// * `fid1` - Feature ID of the first shape (which part is in contact)
161 /// * `fid2` - Feature ID of the second shape (which part is in contact)
162 /// * `dist` - Signed distance between the contact points (negative = penetrating)
163 ///
164 /// The contact data is initialized to its default value.
165 ///
166 /// # Example
167 ///
168 /// ```
169 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
170 /// use parry3d::query::TrackedContact;
171 /// use parry3d::shape::PackedFeatureId;
172 /// use parry3d::math::Point;
173 ///
174 /// let contact = TrackedContact::<()>::new(
175 /// Point::new(1.0, 0.0, 0.0), // Point on shape 1
176 /// Point::new(-1.0, 0.0, 0.0), // Point on shape 2
177 /// PackedFeatureId::face(0), // Face 0 of shape 1
178 /// PackedFeatureId::face(0), // Face 0 of shape 2
179 /// -0.1, // Penetration depth of 0.1
180 /// );
181 ///
182 /// assert_eq!(contact.dist, -0.1);
183 /// # }
184 /// ```
185 pub fn new(
186 local_p1: Point<Real>,
187 local_p2: Point<Real>,
188 fid1: PackedFeatureId,
189 fid2: PackedFeatureId,
190 dist: Real,
191 ) -> Self {
192 Self {
193 local_p1,
194 local_p2,
195 fid1,
196 fid2,
197 dist,
198 data: Data::default(),
199 }
200 }
201
202 /// Creates a new tracked contact where its input may need to be flipped.
203 pub fn flipped(
204 local_p1: Point<Real>,
205 local_p2: Point<Real>,
206 fid1: PackedFeatureId,
207 fid2: PackedFeatureId,
208 dist: Real,
209 flipped: bool,
210 ) -> Self {
211 if !flipped {
212 Self::new(local_p1, local_p2, fid1, fid2, dist)
213 } else {
214 Self::new(local_p2, local_p1, fid2, fid1, dist)
215 }
216 }
217
218 /// Copy to `self` the geometric information from `contact`.
219 pub fn copy_geometry_from(&mut self, contact: Self) {
220 self.local_p1 = contact.local_p1;
221 self.local_p2 = contact.local_p2;
222 self.fid1 = contact.fid1;
223 self.fid2 = contact.fid2;
224 self.dist = contact.dist;
225 }
226}
227
228#[derive(Clone, Debug, Default)]
229#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
230/// A contact manifold between two shapes.
231///
232/// A `ContactManifold` describes a collection of contact points between two shapes that share
233/// the same contact normal and contact kinematics. This is a fundamental data structure for
234/// physics simulation, providing stable and persistent contact information across multiple frames.
235///
236/// # Key Concepts
237///
238/// ## What is a Contact Manifold?
239///
240/// Instead of treating each contact point independently, a contact manifold groups together
241/// all contact points that share the same properties:
242/// - **Same contact normal**: All contacts push the shapes apart in the same direction
243/// - **Same contact kinematics**: All contacts describe the same type of interaction
244/// - **Coherent geometry**: All contacts belong to the same collision feature pair
245///
246/// For example, when a box sits on a plane, you get a manifold with 4 contact points (one
247/// for each corner of the box touching the plane), all sharing the same upward normal.
248///
249/// ## Why Use Manifolds?
250///
251/// Contact manifolds are essential for stable physics simulation:
252/// 1. **Stability**: Multiple contact points prevent rotation and provide stable support
253/// 2. **Performance**: Grouped contacts can be processed more efficiently
254/// 3. **Persistence**: Contact tracking across frames enables warm-starting and reduces jitter
255/// 4. **Natural representation**: Matches the physical reality of contact patches
256///
257/// # Generic Parameters
258///
259/// - `ManifoldData`: User-defined data associated with the entire manifold
260/// - `ContactData`: User-defined data associated with each individual contact point
261///
262/// Both can be `()` if you don't need to store additional data.
263///
264/// # Examples
265///
266/// ## Basic Usage: Two Balls Colliding
267///
268/// ```rust
269/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
270/// use parry3d::query::{ContactManifold, TrackedContact};
271/// use parry3d::query::details::contact_manifold_ball_ball;
272/// use parry3d::shape::Ball;
273/// use parry3d::math::Isometry;
274///
275/// // Create two balls
276/// let ball1 = Ball::new(1.0);
277/// let ball2 = Ball::new(1.0);
278///
279/// // Position them so they overlap
280/// let pos12 = Isometry::translation(1.5, 0.0, 0.0); // Overlapping by 0.5
281///
282/// // Create an empty manifold
283/// let mut manifold = ContactManifold::<(), ()>::new();
284///
285/// // Compute contacts (no prediction distance)
286/// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
287///
288/// // Check the results
289/// assert!(!manifold.points.is_empty());
290/// println!("Number of contacts: {}", manifold.points.len());
291/// println!("Contact normal (local): {:?}", manifold.local_n1);
292///
293/// if let Some(contact) = manifold.points.first() {
294/// println!("Penetration depth: {}", -contact.dist);
295/// }
296/// # }
297/// ```
298///
299/// ## Contact Prediction
300///
301/// Contact prediction allows detecting contacts before shapes actually touch,
302/// which is useful for continuous collision detection:
303///
304/// ```rust
305/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
306/// use parry3d::query::ContactManifold;
307/// use parry3d::query::details::contact_manifold_ball_ball;
308/// use parry3d::shape::Ball;
309/// use parry3d::math::Isometry;
310///
311/// let ball1 = Ball::new(1.0);
312/// let ball2 = Ball::new(1.0);
313///
314/// // Balls are separated by 0.1
315/// let pos12 = Isometry::translation(2.1, 0.0, 0.0);
316///
317/// let mut manifold = ContactManifold::<(), ()>::new();
318///
319/// // With prediction distance of 0.2, we can detect the near-contact
320/// let prediction = 0.2;
321/// contact_manifold_ball_ball(&pos12, &ball1, &ball2, prediction, &mut manifold);
322///
323/// if !manifold.points.is_empty() {
324/// let contact = &manifold.points[0];
325/// println!("Predicted contact distance: {}", contact.dist);
326/// assert!(contact.dist > 0.0); // Positive = separated but predicted
327/// }
328/// # }
329/// ```
330///
331/// ## Efficient Contact Updates with Spatial Coherence
332///
333/// One of the main benefits of contact manifolds is efficient updates:
334///
335/// ```rust
336/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
337/// use parry3d::query::ContactManifold;
338/// use parry3d::query::details::contact_manifold_ball_ball;
339/// use parry3d::shape::Ball;
340/// use parry3d::math::Isometry;
341///
342/// let ball1 = Ball::new(1.0);
343/// let ball2 = Ball::new(1.0);
344/// let mut manifold = ContactManifold::<(), ()>::new();
345///
346/// // Frame 1: Initial contact
347/// let pos12_frame1 = Isometry::translation(1.9, 0.0, 0.0);
348/// contact_manifold_ball_ball(&pos12_frame1, &ball1, &ball2, 0.1, &mut manifold);
349/// println!("Frame 1: {} contacts", manifold.points.len());
350///
351/// // Frame 2: Small movement - try to update efficiently
352/// let pos12_frame2 = Isometry::translation(1.85, 0.0, 0.0);
353///
354/// if manifold.try_update_contacts(&pos12_frame2) {
355/// println!("Successfully updated contacts using spatial coherence");
356/// } else {
357/// println!("Shapes moved too much, recomputing from scratch");
358/// contact_manifold_ball_ball(&pos12_frame2, &ball1, &ball2, 0.1, &mut manifold);
359/// }
360/// # }
361/// ```
362///
363/// ## Working with Multiple Contacts
364///
365/// Some shape pairs produce multiple contact points:
366///
367/// ```rust
368/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
369/// use parry3d::query::ContactManifold;
370/// use parry3d::query::details::contact_manifold_cuboid_cuboid;
371/// use parry3d::shape::Cuboid;
372/// use parry3d::math::{Isometry, Vector};
373///
374/// // Two boxes
375/// let cuboid1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
376/// let cuboid2 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
377///
378/// // One box sitting on top of another
379/// let pos12 = Isometry::translation(0.0, 1.9, 0.0); // Slight overlap
380///
381/// let mut manifold = ContactManifold::<(), ()>::new();
382/// contact_manifold_cuboid_cuboid(&pos12, &cuboid1, &cuboid2, 0.0, &mut manifold);
383///
384/// println!("Number of contact points: {}", manifold.points.len());
385///
386/// // Find the deepest penetration
387/// if let Some(deepest) = manifold.find_deepest_contact() {
388/// println!("Deepest penetration: {}", -deepest.dist);
389/// }
390///
391/// // Iterate over all contacts
392/// for (i, contact) in manifold.points.iter().enumerate() {
393/// println!("Contact {}: dist={}, fid1={:?}, fid2={:?}",
394/// i, contact.dist, contact.fid1, contact.fid2);
395/// }
396/// # }
397/// ```
398///
399/// ## Storing Custom Data
400///
401/// You can attach custom data to both the manifold and individual contacts:
402///
403/// ```rust
404/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
405/// use parry3d::query::ContactManifold;
406/// use parry3d::query::details::contact_manifold_ball_ball;
407/// use parry3d::shape::Ball;
408/// use parry3d::math::Isometry;
409///
410/// // Custom data structures
411/// #[derive(Clone, Default, Copy)]
412/// struct MyManifoldData {
413/// collision_id: u32,
414/// first_contact_frame: u32,
415/// }
416///
417/// #[derive(Clone, Default, Copy)]
418/// struct MyContactData {
419/// accumulated_impulse: f32,
420/// contact_age: u32,
421/// }
422///
423/// let ball1 = Ball::new(1.0);
424/// let ball2 = Ball::new(1.0);
425/// let pos12 = Isometry::translation(1.8, 0.0, 0.0);
426///
427/// // Create manifold with custom data
428/// let manifold_data = MyManifoldData {
429/// collision_id: 42,
430/// first_contact_frame: 100,
431/// };
432/// let mut manifold: ContactManifold<MyManifoldData, MyContactData> =
433/// ContactManifold::with_data(0, 0, manifold_data);
434///
435/// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
436///
437/// // Access manifold data
438/// println!("Collision ID: {}", manifold.data.collision_id);
439///
440/// // Set contact-specific data
441/// if let Some(contact) = manifold.points.first_mut() {
442/// contact.data.accumulated_impulse = 10.0;
443/// contact.data.contact_age = 5;
444/// }
445/// # }
446/// ```
447///
448/// # Contact Normal Convention
449///
450/// The contact normal (`local_n1` and `local_n2`) points from the first shape toward the
451/// second shape. To separate the shapes:
452/// - Move shape 1 in the direction of `-local_n1`
453/// - Move shape 2 in the direction of `local_n2` (which equals `-local_n1` in world space)
454///
455/// # Working with Composite Shapes
456///
457/// When dealing with composite shapes (like triangle meshes or compounds), the manifold
458/// tracks which subshapes are involved:
459///
460/// ```rust
461/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
462/// use parry3d::query::ContactManifold;
463///
464/// // For composite shapes, the manifold tracks subshape indices
465/// let manifold = ContactManifold::<(), ()>::with_data(
466/// 5, // subshape1: 5th subshape of first shape
467/// 12, // subshape2: 12th subshape of second shape
468/// (), // manifold data
469/// );
470///
471/// println!("Contact between subshape {} and {}",
472/// manifold.subshape1, manifold.subshape2);
473/// # }
474/// ```
475pub struct ContactManifold<ManifoldData, ContactData> {
476 // NOTE: use a SmallVec instead?
477 // And for 2D use an ArrayVec since there will never be more than 2 contacts anyways.
478 /// The contacts points.
479 #[cfg(feature = "dim2")]
480 pub points: arrayvec::ArrayVec<TrackedContact<ContactData>, 2>,
481 /// The contacts points.
482 #[cfg(feature = "dim3")]
483 pub points: Vec<TrackedContact<ContactData>>,
484 /// The contact normal of all the contacts of this manifold, expressed in the local space of the first shape.
485 pub local_n1: Vector<Real>,
486 /// The contact normal of all the contacts of this manifold, expressed in the local space of the second shape.
487 pub local_n2: Vector<Real>,
488 /// The first subshape involved in this contact manifold.
489 ///
490 /// This is zero if the first shape is not a composite shape.
491 pub subshape1: u32,
492 /// The second subshape involved in this contact manifold.
493 ///
494 /// This is zero if the second shape is not a composite shape.
495 pub subshape2: u32,
496 /// If the first shape involved is a composite shape, this contains the position of its subshape
497 /// involved in this contact.
498 pub subshape_pos1: Option<Isometry<Real>>,
499 /// If the second shape involved is a composite shape, this contains the position of its subshape
500 /// involved in this contact.
501 pub subshape_pos2: Option<Isometry<Real>>,
502 /// Additional tracked data associated to this contact manifold.
503 pub data: ManifoldData,
504}
505
506impl<ManifoldData, ContactData: Default + Copy> ContactManifold<ManifoldData, ContactData> {
507 /// Create a new empty contact-manifold.
508 pub fn new() -> Self
509 where
510 ManifoldData: Default,
511 {
512 Self::default()
513 }
514
515 /// Create a new empty contact-manifold with the given associated data.
516 pub fn with_data(subshape1: u32, subshape2: u32, data: ManifoldData) -> Self {
517 Self {
518 #[cfg(feature = "dim2")]
519 points: arrayvec::ArrayVec::new(),
520 #[cfg(feature = "dim3")]
521 points: Vec::new(),
522 local_n1: Vector::zeros(),
523 local_n2: Vector::zeros(),
524 subshape1,
525 subshape2,
526 subshape_pos1: None,
527 subshape_pos2: None,
528 data,
529 }
530 }
531
532 /// Clones `self` and then remove all contact points from `self`.
533 pub fn take(&mut self) -> Self
534 where
535 ManifoldData: Clone,
536 {
537 #[cfg(feature = "dim2")]
538 let points = self.points.clone();
539 #[cfg(feature = "dim3")]
540 let points = core::mem::take(&mut self.points);
541 self.points.clear();
542
543 ContactManifold {
544 points,
545 local_n1: self.local_n1,
546 local_n2: self.local_n2,
547 subshape1: self.subshape1,
548 subshape2: self.subshape2,
549 subshape_pos1: self.subshape_pos1,
550 subshape_pos2: self.subshape_pos2,
551 data: self.data.clone(),
552 }
553 }
554
555 /*
556 pub(crate) fn single_manifold<'a, 'b>(
557 manifolds: &mut Vec<Self>,
558 data: &dyn Fn() -> ManifoldData,
559 ) -> &'a mut Self {
560 if manifolds.is_empty() {
561 let manifold_data = data();
562 manifolds.push(ContactManifold::with_data((0, 0), manifold_data));
563 }
564
565 &mut manifolds[0]
566 }
567 */
568
569 /// Returns a slice of all the contact points in this manifold.
570 ///
571 /// This provides read-only access to all contact points. The contacts are stored
572 /// in the order they were added during manifold computation.
573 ///
574 /// # Example
575 ///
576 /// ```
577 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
578 /// use parry3d::query::ContactManifold;
579 /// use parry3d::query::details::contact_manifold_ball_ball;
580 /// use parry3d::shape::Ball;
581 /// use parry3d::math::Isometry;
582 ///
583 /// let ball1 = Ball::new(1.0);
584 /// let ball2 = Ball::new(1.0);
585 /// let pos12 = Isometry::translation(1.8, 0.0, 0.0);
586 ///
587 /// let mut manifold = ContactManifold::<(), ()>::new();
588 /// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
589 ///
590 /// // Access all contacts
591 /// for (i, contact) in manifold.contacts().iter().enumerate() {
592 /// println!("Contact {}: distance = {}", i, contact.dist);
593 /// }
594 /// # }
595 /// ```
596 #[inline]
597 pub fn contacts(&self) -> &[TrackedContact<ContactData>] {
598 &self.points
599 }
600
601 /// Attempts to efficiently update contact points using spatial coherence.
602 ///
603 /// This method tries to update the contact points based on the new relative position
604 /// of the shapes (`pos12`) without recomputing the entire contact manifold. This is
605 /// much faster than full recomputation but only works when:
606 /// - The shapes haven't moved or rotated too much
607 /// - The contact normal hasn't changed significantly
608 /// - The contact configuration is still valid
609 ///
610 /// Returns `true` if the update succeeded, `false` if full recomputation is needed.
611 ///
612 /// # When to Use This
613 ///
614 /// Use this method every frame after the initial contact computation. It exploits
615 /// temporal coherence in physics simulation where shapes typically move smoothly.
616 /// When it returns `false`, fall back to full contact manifold recomputation.
617 ///
618 /// # Thresholds
619 ///
620 /// This method uses default thresholds for angle and distance changes. For custom
621 /// thresholds, use [`try_update_contacts_eps`](Self::try_update_contacts_eps).
622 ///
623 /// # Example
624 ///
625 /// ```
626 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
627 /// use parry3d::query::ContactManifold;
628 /// use parry3d::query::details::contact_manifold_ball_ball;
629 /// use parry3d::shape::Ball;
630 /// use parry3d::math::Isometry;
631 ///
632 /// let ball1 = Ball::new(1.0);
633 /// let ball2 = Ball::new(1.0);
634 /// let mut manifold = ContactManifold::<(), ()>::new();
635 ///
636 /// // Initial computation
637 /// let pos12_old = Isometry::translation(1.9, 0.0, 0.0);
638 /// contact_manifold_ball_ball(&pos12_old, &ball1, &ball2, 0.1, &mut manifold);
639 ///
640 /// // Next frame: shapes moved slightly
641 /// let pos12_new = Isometry::translation(1.85, 0.05, 0.0);
642 ///
643 /// if manifold.try_update_contacts(&pos12_new) {
644 /// println!("Updated contacts efficiently!");
645 /// } else {
646 /// println!("Need to recompute from scratch");
647 /// contact_manifold_ball_ball(&pos12_new, &ball1, &ball2, 0.1, &mut manifold);
648 /// }
649 /// # }
650 /// ```
651 #[inline]
652 pub fn try_update_contacts(&mut self, pos12: &Isometry<Real>) -> bool {
653 // const DOT_THRESHOLD: Real = 0.crate::COS_10_DEGREES;
654 // const DOT_THRESHOLD: Real = crate::utils::COS_5_DEGREES;
655 const DOT_THRESHOLD: Real = crate::utils::COS_1_DEGREES;
656 const DIST_SQ_THRESHOLD: Real = 1.0e-6; // TODO: this should not be hard-coded.
657 self.try_update_contacts_eps(pos12, DOT_THRESHOLD, DIST_SQ_THRESHOLD)
658 }
659
660 /// Attempts to use spatial coherence to update contacts points, using user-defined tolerances.
661 #[inline]
662 pub fn try_update_contacts_eps(
663 &mut self,
664 pos12: &Isometry<Real>,
665 angle_dot_threshold: Real,
666 dist_sq_threshold: Real,
667 ) -> bool {
668 if self.points.is_empty() {
669 return false;
670 }
671
672 let local_n2 = pos12 * self.local_n2;
673
674 if -self.local_n1.dot(&local_n2) < angle_dot_threshold {
675 return false;
676 }
677
678 for pt in &mut self.points {
679 let local_p2 = pos12 * pt.local_p2;
680 let dpt = local_p2 - pt.local_p1;
681 let dist = dpt.dot(&self.local_n1);
682
683 if dist * pt.dist < 0.0 {
684 // We switched between penetrating/non-penetrating.
685 // The may result in other contacts to appear.
686 return false;
687 }
688 let new_local_p1 = local_p2 - self.local_n1 * dist;
689
690 if na::distance_squared(&pt.local_p1, &new_local_p1) > dist_sq_threshold {
691 return false;
692 }
693
694 pt.dist = dist;
695 pt.local_p1 = new_local_p1;
696 }
697
698 true
699 }
700
701 /// Transfers contact data from previous frame's contacts to current contacts based on feature IDs.
702 ///
703 /// This method is crucial for maintaining persistent contact information across frames.
704 /// It matches contacts between the old and new manifolds by comparing their feature IDs
705 /// (which geometric features are in contact). When a match is found, the user data is
706 /// transferred from the old contact to the new one.
707 ///
708 /// This enables important physics features like:
709 /// - **Warm-starting**: Reusing accumulated impulses speeds up constraint solving
710 /// - **Contact aging**: Tracking how long a contact has existed
711 /// - **Friction state**: Maintaining tangential impulse information
712 ///
713 /// # When to Use
714 ///
715 /// Call this method after recomputing the contact manifold, passing the old contact
716 /// points from the previous frame. This preserves contact-specific solver state.
717 ///
718 /// # Example
719 ///
720 /// ```
721 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
722 /// use parry3d::query::ContactManifold;
723 /// use parry3d::query::details::contact_manifold_ball_ball;
724 /// use parry3d::shape::Ball;
725 /// use parry3d::math::Isometry;
726 ///
727 /// #[derive(Clone, Default, Copy)]
728 /// struct MyContactData {
729 /// accumulated_impulse: f32,
730 /// age: u32,
731 /// }
732 ///
733 /// let ball1 = Ball::new(1.0);
734 /// let ball2 = Ball::new(1.0);
735 /// let mut manifold = ContactManifold::<(), MyContactData>::new();
736 ///
737 /// // Frame 1: Compute contacts
738 /// let pos12_frame1 = Isometry::translation(1.9, 0.0, 0.0);
739 /// contact_manifold_ball_ball(&pos12_frame1, &ball1, &ball2, 0.0, &mut manifold);
740 ///
741 /// // Simulate physics, accumulate impulse
742 /// if let Some(contact) = manifold.points.first_mut() {
743 /// contact.data.accumulated_impulse = 42.0;
744 /// contact.data.age = 1;
745 /// }
746 ///
747 /// // Frame 2: Save old contacts, recompute
748 /// let old_contacts = manifold.points.clone();
749 /// let pos12_frame2 = Isometry::translation(1.85, 0.0, 0.0);
750 /// contact_manifold_ball_ball(&pos12_frame2, &ball1, &ball2, 0.0, &mut manifold);
751 ///
752 /// // Transfer data from old to new based on feature ID matching
753 /// manifold.match_contacts(&old_contacts);
754 ///
755 /// // Data is preserved!
756 /// if let Some(contact) = manifold.points.first() {
757 /// assert_eq!(contact.data.accumulated_impulse, 42.0);
758 /// }
759 /// # }
760 /// ```
761 pub fn match_contacts(&mut self, old_contacts: &[TrackedContact<ContactData>]) {
762 for contact in &mut self.points {
763 for old_contact in old_contacts {
764 if contact.fid1 == old_contact.fid1 && contact.fid2 == old_contact.fid2 {
765 // Transfer the tracked data.
766 contact.data = old_contact.data;
767 }
768 }
769 }
770 }
771
772 /// Copy data associated to contacts from `old_contacts` to the new contacts in `self`
773 /// based on matching the contact positions.
774 pub fn match_contacts_using_positions(
775 &mut self,
776 old_contacts: &[TrackedContact<ContactData>],
777 dist_threshold: Real,
778 ) {
779 let sq_threshold = dist_threshold * dist_threshold;
780 for contact in &mut self.points {
781 for old_contact in old_contacts {
782 if na::distance_squared(&contact.local_p1, &old_contact.local_p1) < sq_threshold
783 && na::distance_squared(&contact.local_p2, &old_contact.local_p2) < sq_threshold
784 {
785 // Transfer the tracked data.
786 contact.data = old_contact.data;
787 }
788 }
789 }
790 }
791
792 /// Removes all the contacts from `self`.
793 pub fn clear(&mut self) {
794 self.points.clear();
795 }
796
797 /// Finds and returns the contact with the deepest penetration.
798 ///
799 /// This returns the contact with the smallest (most negative) distance value,
800 /// which corresponds to the largest penetration depth. Returns `None` if the
801 /// manifold has no contact points.
802 ///
803 /// # Use Cases
804 ///
805 /// - Finding the primary contact for simplified physics resolution
806 /// - Determining the severity of an overlap for collision response
807 /// - Prioritizing contacts in contact reduction algorithms
808 /// - Debug visualization of the most significant contact
809 ///
810 /// # Example
811 ///
812 /// ```
813 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
814 /// use parry3d::query::ContactManifold;
815 /// use parry3d::query::details::contact_manifold_cuboid_cuboid;
816 /// use parry3d::shape::Cuboid;
817 /// use parry3d::math::{Isometry, Vector};
818 ///
819 /// let cuboid1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
820 /// let cuboid2 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
821 ///
822 /// // Position with some penetration
823 /// let pos12 = Isometry::translation(0.0, 1.8, 0.0);
824 ///
825 /// let mut manifold = ContactManifold::<(), ()>::new();
826 /// contact_manifold_cuboid_cuboid(&pos12, &cuboid1, &cuboid2, 0.0, &mut manifold);
827 ///
828 /// if let Some(deepest) = manifold.find_deepest_contact() {
829 /// let penetration_depth = -deepest.dist;
830 /// println!("Maximum penetration: {}", penetration_depth);
831 /// println!("Deepest contact point (shape 1): {:?}", deepest.local_p1);
832 /// }
833 /// # }
834 /// ```
835 pub fn find_deepest_contact(&self) -> Option<&TrackedContact<ContactData>> {
836 let mut deepest = self.points.first()?;
837
838 for pt in &self.points {
839 if pt.dist < deepest.dist {
840 deepest = pt;
841 }
842 }
843
844 Some(deepest)
845 }
846}