parry2d/query/contact_manifolds/
contact_manifold.rs

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