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}