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}