Skip to main content

parry3d/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 either shape involved is a composite shape, this contains the positions of the
496    /// subshapes involved in this contact.
497    pub subshape_poses: Option<alloc::boxed::Box<SubshapePoses>>,
498    /// Additional tracked data associated to this contact manifold.
499    pub data: ManifoldData,
500}
501
502/// The positions of the composite-shape subshapes involved in a contact manifold.
503#[derive(Clone, Debug, Default)]
504#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
505pub struct SubshapePoses {
506    /// The position of the first shape's subshape, if it is a composite shape.
507    pub pos1: Option<Pose>,
508    /// The position of the second shape's subshape, if it is a composite shape.
509    pub pos2: Option<Pose>,
510}
511
512impl<ManifoldData, ContactData> ContactManifold<ManifoldData, ContactData> {
513    /// The position of the first shape's subshape involved in this contact, if the first
514    /// shape is a composite shape.
515    #[inline]
516    pub fn subshape_pos1(&self) -> Option<&Pose> {
517        self.subshape_poses.as_ref().and_then(|p| p.pos1.as_ref())
518    }
519
520    /// The position of the second shape's subshape involved in this contact, if the second
521    /// shape is a composite shape.
522    #[inline]
523    pub fn subshape_pos2(&self) -> Option<&Pose> {
524        self.subshape_poses.as_ref().and_then(|p| p.pos2.as_ref())
525    }
526
527    /// Sets the position of the first shape's subshape, reusing the existing allocation.
528    #[inline]
529    pub fn set_subshape_pos1(&mut self, pos: Option<Pose>) {
530        match (&mut self.subshape_poses, pos) {
531            (Some(poses), pos) => poses.pos1 = pos,
532            (slot @ None, Some(pos)) => {
533                *slot = Some(alloc::boxed::Box::new(SubshapePoses {
534                    pos1: Some(pos),
535                    pos2: None,
536                }))
537            }
538            (None, None) => {}
539        }
540    }
541
542    /// Sets the position of the second shape's subshape, reusing the existing allocation.
543    #[inline]
544    pub fn set_subshape_pos2(&mut self, pos: Option<Pose>) {
545        match (&mut self.subshape_poses, pos) {
546            (Some(poses), pos) => poses.pos2 = pos,
547            (slot @ None, Some(pos)) => {
548                *slot = Some(alloc::boxed::Box::new(SubshapePoses {
549                    pos1: None,
550                    pos2: Some(pos),
551                }))
552            }
553            (None, None) => {}
554        }
555    }
556}
557
558impl<ManifoldData, ContactData: Default + Copy> ContactManifold<ManifoldData, ContactData> {
559    /// Create a new empty contact-manifold.
560    pub fn new() -> Self
561    where
562        ManifoldData: Default,
563    {
564        Self::default()
565    }
566
567    /// Create a new empty contact-manifold with the given associated data.
568    pub fn with_data(subshape1: u32, subshape2: u32, data: ManifoldData) -> Self {
569        Self {
570            #[cfg(feature = "dim2")]
571            points: arrayvec::ArrayVec::new(),
572            #[cfg(feature = "dim3")]
573            points: Vec::new(),
574            local_n1: Vector::ZERO,
575            local_n2: Vector::ZERO,
576            subshape1,
577            subshape2,
578            subshape_poses: None,
579            data,
580        }
581    }
582
583    /// Clones `self` and then remove all contact points from `self`.
584    pub fn take(&mut self) -> Self
585    where
586        ManifoldData: Clone,
587    {
588        #[cfg(feature = "dim2")]
589        let points = self.points.clone();
590        #[cfg(feature = "dim3")]
591        let points = core::mem::take(&mut self.points);
592        self.points.clear();
593
594        ContactManifold {
595            points,
596            local_n1: self.local_n1,
597            local_n2: self.local_n2,
598            subshape1: self.subshape1,
599            subshape2: self.subshape2,
600            subshape_poses: self.subshape_poses.clone(),
601            data: self.data.clone(),
602        }
603    }
604
605    /*
606    pub(crate) fn single_manifold<'a, 'b>(
607        manifolds: &mut Vec<Self>,
608        data: &dyn Fn() -> ManifoldData,
609    ) -> &'a mut Self {
610        if manifolds.is_empty() {
611            let manifold_data = data();
612            manifolds.push(ContactManifold::with_data((0, 0), manifold_data));
613        }
614
615        &mut manifolds[0]
616    }
617    */
618
619    /// Returns a slice of all the contact points in this manifold.
620    ///
621    /// This provides read-only access to all contact points. The contacts are stored
622    /// in the order they were added during manifold computation.
623    ///
624    /// # Example
625    ///
626    /// ```
627    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
628    /// use parry3d::query::ContactManifold;
629    /// use parry3d::query::details::contact_manifold_ball_ball;
630    /// use parry3d::shape::Ball;
631    /// use parry3d::math::Pose;
632    ///
633    /// let ball1 = Ball::new(1.0);
634    /// let ball2 = Ball::new(1.0);
635    /// let pos12 = Pose::translation(1.8, 0.0, 0.0);
636    ///
637    /// let mut manifold = ContactManifold::<(), ()>::new();
638    /// contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);
639    ///
640    /// // Access all contacts
641    /// for (i, contact) in manifold.contacts().iter().enumerate() {
642    ///     println!("Contact {}: distance = {}", i, contact.dist);
643    /// }
644    /// # }
645    /// ```
646    #[inline]
647    pub fn contacts(&self) -> &[TrackedContact<ContactData>] {
648        &self.points
649    }
650
651    /// Attempts to efficiently update contact points using spatial coherence.
652    ///
653    /// This method tries to update the contact points based on the new relative position
654    /// of the shapes (`pos12`) without recomputing the entire contact manifold. This is
655    /// much faster than full recomputation but only works when:
656    /// - The shapes haven't moved or rotated too much
657    /// - The contact normal hasn't changed significantly
658    /// - The contact configuration is still valid
659    ///
660    /// Returns `true` if the update succeeded, `false` if full recomputation is needed.
661    ///
662    /// # When to Use This
663    ///
664    /// Use this method every frame after the initial contact computation. It exploits
665    /// temporal coherence in physics simulation where shapes typically move smoothly.
666    /// When it returns `false`, fall back to full contact manifold recomputation.
667    ///
668    /// # Thresholds
669    ///
670    /// This method uses default thresholds for angle and distance changes. For custom
671    /// thresholds, use [`try_update_contacts_eps`](Self::try_update_contacts_eps).
672    ///
673    /// # Example
674    ///
675    /// ```
676    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
677    /// use parry3d::query::ContactManifold;
678    /// use parry3d::query::details::contact_manifold_ball_ball;
679    /// use parry3d::shape::Ball;
680    /// use parry3d::math::Pose;
681    ///
682    /// let ball1 = Ball::new(1.0);
683    /// let ball2 = Ball::new(1.0);
684    /// let mut manifold = ContactManifold::<(), ()>::new();
685    ///
686    /// // Initial computation
687    /// let pos12_old = Pose::translation(1.9, 0.0, 0.0);
688    /// contact_manifold_ball_ball(&pos12_old, &ball1, &ball2, 0.1, &mut manifold);
689    ///
690    /// // Next frame: shapes moved slightly
691    /// let pos12_new = Pose::translation(1.85, 0.05, 0.0);
692    ///
693    /// if manifold.try_update_contacts(&pos12_new) {
694    ///     println!("Updated contacts efficiently!");
695    /// } else {
696    ///     println!("Need to recompute from scratch");
697    ///     contact_manifold_ball_ball(&pos12_new, &ball1, &ball2, 0.1, &mut manifold);
698    /// }
699    /// # }
700    /// ```
701    #[inline]
702    pub fn try_update_contacts(&mut self, pos12: &Pose) -> bool {
703        // const DOT_THRESHOLD: Real = 0.crate::COS_10_DEGREES;
704        // const DOT_THRESHOLD: Real = crate::utils::COS_5_DEGREES;
705        const DOT_THRESHOLD: Real = crate::utils::COS_1_DEGREES;
706        const DIST_SQ_THRESHOLD: Real = 1.0e-6; // TODO: this should not be hard-coded.
707        self.try_update_contacts_eps(pos12, DOT_THRESHOLD, DIST_SQ_THRESHOLD)
708    }
709
710    /// Attempts to use spatial coherence to update contacts points, using user-defined tolerances.
711    #[inline]
712    pub fn try_update_contacts_eps(
713        &mut self,
714        pos12: &Pose,
715        angle_dot_threshold: Real,
716        dist_sq_threshold: Real,
717    ) -> bool {
718        if self.points.is_empty() {
719            return false;
720        }
721
722        let local_n2 = pos12.rotation * self.local_n2;
723
724        if -self.local_n1.dot(local_n2) < angle_dot_threshold {
725            return false;
726        }
727
728        for pt in &self.points {
729            let local_p2 = pos12 * pt.local_p2;
730            let dpt = local_p2 - pt.local_p1;
731            let dist = dpt.dot(self.local_n1);
732
733            if dist * pt.dist < 0.0 {
734                // We switched between penetrating/non-penetrating.
735                // The may result in other contacts to appear.
736                return false;
737            }
738            let new_local_p1 = local_p2 - self.local_n1 * dist;
739
740            if pt.local_p1.distance_squared(new_local_p1) > dist_sq_threshold {
741                return false;
742            }
743        }
744
745        self.update_separations(pos12);
746
747        true
748    }
749
750    /// Refreshes every contact point's separation (`dist`) from the current
751    /// relative pose of the two shapes, keeping the contact points (anchors)
752    /// frozen at their captured material positions.
753    #[inline]
754    pub fn update_separations(&mut self, pos12: &Pose) {
755        for pt in &mut self.points {
756            let local_p2 = pos12 * pt.local_p2;
757            pt.dist = (local_p2 - pt.local_p1).dot(self.local_n1);
758        }
759    }
760
761    /// Transfers contact data from previous frame's contacts to current contacts based on feature IDs.
762    ///
763    /// This method is crucial for maintaining persistent contact information across frames.
764    /// It matches contacts between the old and new manifolds by comparing their feature IDs
765    /// (which geometric features are in contact). When a match is found, the user data is
766    /// transferred from the old contact to the new one.
767    ///
768    /// This enables important physics features like:
769    /// - **Warm-starting**: Reusing accumulated impulses speeds up constraint solving
770    /// - **Contact aging**: Tracking how long a contact has existed
771    /// - **Friction state**: Maintaining tangential impulse information
772    ///
773    /// # When to Use
774    ///
775    /// Call this method after recomputing the contact manifold, passing the old contact
776    /// points from the previous frame. This preserves contact-specific solver state.
777    ///
778    /// # Example
779    ///
780    /// ```
781    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
782    /// use parry3d::query::ContactManifold;
783    /// use parry3d::query::details::contact_manifold_ball_ball;
784    /// use parry3d::shape::Ball;
785    /// use parry3d::math::Pose;
786    ///
787    /// #[derive(Clone, Default, Copy)]
788    /// struct MyContactData {
789    ///     accumulated_impulse: f32,
790    ///     age: u32,
791    /// }
792    ///
793    /// let ball1 = Ball::new(1.0);
794    /// let ball2 = Ball::new(1.0);
795    /// let mut manifold = ContactManifold::<(), MyContactData>::new();
796    ///
797    /// // Frame 1: Compute contacts
798    /// let pos12_frame1 = Pose::translation(1.9, 0.0, 0.0);
799    /// contact_manifold_ball_ball(&pos12_frame1, &ball1, &ball2, 0.0, &mut manifold);
800    ///
801    /// // Simulate physics, accumulate impulse
802    /// if let Some(contact) = manifold.points.first_mut() {
803    ///     contact.data.accumulated_impulse = 42.0;
804    ///     contact.data.age = 1;
805    /// }
806    ///
807    /// // Frame 2: Save old contacts, recompute
808    /// let old_contacts = manifold.points.clone();
809    /// let pos12_frame2 = Pose::translation(1.85, 0.0, 0.0);
810    /// contact_manifold_ball_ball(&pos12_frame2, &ball1, &ball2, 0.0, &mut manifold);
811    ///
812    /// // Transfer data from old to new based on feature ID matching
813    /// manifold.match_contacts(&old_contacts);
814    ///
815    /// // Data is preserved!
816    /// if let Some(contact) = manifold.points.first() {
817    ///     assert_eq!(contact.data.accumulated_impulse, 42.0);
818    /// }
819    /// # }
820    /// ```
821    pub fn match_contacts(&mut self, old_contacts: &[TrackedContact<ContactData>]) {
822        for contact in &mut self.points {
823            for old_contact in old_contacts {
824                if contact.fid1 == old_contact.fid1 && contact.fid2 == old_contact.fid2 {
825                    // Transfer the tracked data.
826                    contact.data = old_contact.data;
827                }
828            }
829        }
830    }
831
832    /// Copy data associated to contacts from `old_contacts` to the new contacts in `self`
833    /// based on matching the contact positions.
834    pub fn match_contacts_using_positions(
835        &mut self,
836        old_contacts: &[TrackedContact<ContactData>],
837        dist_threshold: Real,
838    ) {
839        let sq_threshold = dist_threshold * dist_threshold;
840        for contact in &mut self.points {
841            for old_contact in old_contacts {
842                if contact.local_p1.distance_squared(old_contact.local_p1) < sq_threshold
843                    && contact.local_p2.distance_squared(old_contact.local_p2) < sq_threshold
844                {
845                    // Transfer the tracked data.
846                    contact.data = old_contact.data;
847                }
848            }
849        }
850    }
851
852    /// Removes all the contacts from `self`.
853    pub fn clear(&mut self) {
854        self.points.clear();
855    }
856
857    /// Finds and returns the contact with the deepest penetration.
858    ///
859    /// This returns the contact with the smallest (most negative) distance value,
860    /// which corresponds to the largest penetration depth. Returns `None` if the
861    /// manifold has no contact points.
862    ///
863    /// # Use Cases
864    ///
865    /// - Finding the primary contact for simplified physics resolution
866    /// - Determining the severity of an overlap for collision response
867    /// - Prioritizing contacts in contact reduction algorithms
868    /// - Debug visualization of the most significant contact
869    ///
870    /// # Example
871    ///
872    /// ```
873    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
874    /// use parry3d::query::ContactManifold;
875    /// use parry3d::query::details::contact_manifold_cuboid_cuboid;
876    /// use parry3d::shape::Cuboid;
877    /// use parry3d::math::{Pose, Vector};
878    ///
879    /// let cuboid1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
880    /// let cuboid2 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
881    ///
882    /// // Position with some penetration
883    /// let pos12 = Pose::translation(0.0, 1.8, 0.0);
884    ///
885    /// let mut manifold = ContactManifold::<(), ()>::new();
886    /// contact_manifold_cuboid_cuboid(&pos12, &cuboid1, &cuboid2, 0.0, &mut manifold);
887    ///
888    /// if let Some(deepest) = manifold.find_deepest_contact() {
889    ///     let penetration_depth = -deepest.dist;
890    ///     println!("Maximum penetration: {}", penetration_depth);
891    ///     println!("Deepest contact point (shape 1): {:?}", deepest.local_p1);
892    /// }
893    /// # }
894    /// ```
895    pub fn find_deepest_contact(&self) -> Option<&TrackedContact<ContactData>> {
896        let mut deepest = self.points.first()?;
897
898        for pt in &self.points {
899            if pt.dist < deepest.dist {
900                deepest = pt;
901            }
902        }
903
904        Some(deepest)
905    }
906}