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}