Struct ContactManifold

Source
pub struct ContactManifold<ManifoldData, ContactData> {
    pub points: Vec<TrackedContact<ContactData>>,
    pub local_n1: Vector<f32>,
    pub local_n2: Vector<f32>,
    pub subshape1: u32,
    pub subshape2: u32,
    pub subshape_pos1: Option<Isometry<f32>>,
    pub subshape_pos2: Option<Isometry<f32>>,
    pub data: ManifoldData,
}
Expand description

A contact manifold between two shapes.

A ContactManifold describes a collection of contact points between two shapes that share the same contact normal and contact kinematics. This is a fundamental data structure for physics simulation, providing stable and persistent contact information across multiple frames.

§Key Concepts

§What is a Contact Manifold?

Instead of treating each contact point independently, a contact manifold groups together all contact points that share the same properties:

  • Same contact normal: All contacts push the shapes apart in the same direction
  • Same contact kinematics: All contacts describe the same type of interaction
  • Coherent geometry: All contacts belong to the same collision feature pair

For example, when a box sits on a plane, you get a manifold with 4 contact points (one for each corner of the box touching the plane), all sharing the same upward normal.

§Why Use Manifolds?

Contact manifolds are essential for stable physics simulation:

  1. Stability: Multiple contact points prevent rotation and provide stable support
  2. Performance: Grouped contacts can be processed more efficiently
  3. Persistence: Contact tracking across frames enables warm-starting and reduces jitter
  4. Natural representation: Matches the physical reality of contact patches

§Generic Parameters

  • ManifoldData: User-defined data associated with the entire manifold
  • ContactData: User-defined data associated with each individual contact point

Both can be () if you don’t need to store additional data.

§Examples

§Basic Usage: Two Balls Colliding

use parry3d::query::{ContactManifold, TrackedContact};
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

// Create two balls
let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);

// Position them so they overlap
let pos12 = Isometry::translation(1.5, 0.0, 0.0); // Overlapping by 0.5

// Create an empty manifold
let mut manifold = ContactManifold::<(), ()>::new();

// Compute contacts (no prediction distance)
contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);

// Check the results
assert!(!manifold.points.is_empty());
println!("Number of contacts: {}", manifold.points.len());
println!("Contact normal (local): {:?}", manifold.local_n1);

if let Some(contact) = manifold.points.first() {
    println!("Penetration depth: {}", -contact.dist);
}

§Contact Prediction

Contact prediction allows detecting contacts before shapes actually touch, which is useful for continuous collision detection:

use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);

// Balls are separated by 0.1
let pos12 = Isometry::translation(2.1, 0.0, 0.0);

let mut manifold = ContactManifold::<(), ()>::new();

// With prediction distance of 0.2, we can detect the near-contact
let prediction = 0.2;
contact_manifold_ball_ball(&pos12, &ball1, &ball2, prediction, &mut manifold);

if !manifold.points.is_empty() {
    let contact = &manifold.points[0];
    println!("Predicted contact distance: {}", contact.dist);
    assert!(contact.dist > 0.0); // Positive = separated but predicted
}

§Efficient Contact Updates with Spatial Coherence

One of the main benefits of contact manifolds is efficient updates:

use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
let mut manifold = ContactManifold::<(), ()>::new();

// Frame 1: Initial contact
let pos12_frame1 = Isometry::translation(1.9, 0.0, 0.0);
contact_manifold_ball_ball(&pos12_frame1, &ball1, &ball2, 0.1, &mut manifold);
println!("Frame 1: {} contacts", manifold.points.len());

// Frame 2: Small movement - try to update efficiently
let pos12_frame2 = Isometry::translation(1.85, 0.0, 0.0);

if manifold.try_update_contacts(&pos12_frame2) {
    println!("Successfully updated contacts using spatial coherence");
} else {
    println!("Shapes moved too much, recomputing from scratch");
    contact_manifold_ball_ball(&pos12_frame2, &ball1, &ball2, 0.1, &mut manifold);
}

§Working with Multiple Contacts

Some shape pairs produce multiple contact points:

use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_cuboid_cuboid;
use parry3d::shape::Cuboid;
use parry3d::math::{Isometry, Vector};

// Two boxes
let cuboid1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
let cuboid2 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));

// One box sitting on top of another
let pos12 = Isometry::translation(0.0, 1.9, 0.0); // Slight overlap

let mut manifold = ContactManifold::<(), ()>::new();
contact_manifold_cuboid_cuboid(&pos12, &cuboid1, &cuboid2, 0.0, &mut manifold);

println!("Number of contact points: {}", manifold.points.len());

// Find the deepest penetration
if let Some(deepest) = manifold.find_deepest_contact() {
    println!("Deepest penetration: {}", -deepest.dist);
}

// Iterate over all contacts
for (i, contact) in manifold.points.iter().enumerate() {
    println!("Contact {}: dist={}, fid1={:?}, fid2={:?}",
             i, contact.dist, contact.fid1, contact.fid2);
}

§Storing Custom Data

You can attach custom data to both the manifold and individual contacts:

use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

// Custom data structures
#[derive(Clone, Default, Copy)]
struct MyManifoldData {
    collision_id: u32,
    first_contact_frame: u32,
}

#[derive(Clone, Default, Copy)]
struct MyContactData {
    accumulated_impulse: f32,
    contact_age: u32,
}

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
let pos12 = Isometry::translation(1.8, 0.0, 0.0);

// Create manifold with custom data
let manifold_data = MyManifoldData {
    collision_id: 42,
    first_contact_frame: 100,
};
let mut manifold: ContactManifold<MyManifoldData, MyContactData> =
    ContactManifold::with_data(0, 0, manifold_data);

contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);

// Access manifold data
println!("Collision ID: {}", manifold.data.collision_id);

// Set contact-specific data
if let Some(contact) = manifold.points.first_mut() {
    contact.data.accumulated_impulse = 10.0;
    contact.data.contact_age = 5;
}

§Contact Normal Convention

The contact normal (local_n1 and local_n2) points from the first shape toward the second shape. To separate the shapes:

  • Move shape 1 in the direction of -local_n1
  • Move shape 2 in the direction of local_n2 (which equals -local_n1 in world space)

§Working with Composite Shapes

When dealing with composite shapes (like triangle meshes or compounds), the manifold tracks which subshapes are involved:

use parry3d::query::ContactManifold;

// For composite shapes, the manifold tracks subshape indices
let manifold = ContactManifold::<(), ()>::with_data(
    5,  // subshape1: 5th subshape of first shape
    12, // subshape2: 12th subshape of second shape
    (), // manifold data
);

println!("Contact between subshape {} and {}",
         manifold.subshape1, manifold.subshape2);

Fields§

§points: Vec<TrackedContact<ContactData>>

The contacts points.

§local_n1: Vector<f32>

The contact normal of all the contacts of this manifold, expressed in the local space of the first shape.

§local_n2: Vector<f32>

The contact normal of all the contacts of this manifold, expressed in the local space of the second shape.

§subshape1: u32

The first subshape involved in this contact manifold.

This is zero if the first shape is not a composite shape.

§subshape2: u32

The second subshape involved in this contact manifold.

This is zero if the second shape is not a composite shape.

§subshape_pos1: Option<Isometry<f32>>

If the first shape involved is a composite shape, this contains the position of its subshape involved in this contact.

§subshape_pos2: Option<Isometry<f32>>

If the second shape involved is a composite shape, this contains the position of its subshape involved in this contact.

§data: ManifoldData

Additional tracked data associated to this contact manifold.

Implementations§

Source§

impl<ManifoldData, ContactData: Default + Copy> ContactManifold<ManifoldData, ContactData>

Source

pub fn new() -> Self
where ManifoldData: Default,

Create a new empty contact-manifold.

Source

pub fn with_data(subshape1: u32, subshape2: u32, data: ManifoldData) -> Self

Create a new empty contact-manifold with the given associated data.

Source

pub fn take(&mut self) -> Self
where ManifoldData: Clone,

Clones self and then remove all contact points from self.

Source

pub fn contacts(&self) -> &[TrackedContact<ContactData>]

Returns a slice of all the contact points in this manifold.

This provides read-only access to all contact points. The contacts are stored in the order they were added during manifold computation.

§Example
use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
let pos12 = Isometry::translation(1.8, 0.0, 0.0);

let mut manifold = ContactManifold::<(), ()>::new();
contact_manifold_ball_ball(&pos12, &ball1, &ball2, 0.0, &mut manifold);

// Access all contacts
for (i, contact) in manifold.contacts().iter().enumerate() {
    println!("Contact {}: distance = {}", i, contact.dist);
}
Source

pub fn try_update_contacts(&mut self, pos12: &Isometry<f32>) -> bool

Attempts to efficiently update contact points using spatial coherence.

This method tries to update the contact points based on the new relative position of the shapes (pos12) without recomputing the entire contact manifold. This is much faster than full recomputation but only works when:

  • The shapes haven’t moved or rotated too much
  • The contact normal hasn’t changed significantly
  • The contact configuration is still valid

Returns true if the update succeeded, false if full recomputation is needed.

§When to Use This

Use this method every frame after the initial contact computation. It exploits temporal coherence in physics simulation where shapes typically move smoothly. When it returns false, fall back to full contact manifold recomputation.

§Thresholds

This method uses default thresholds for angle and distance changes. For custom thresholds, use try_update_contacts_eps.

§Example
use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
let mut manifold = ContactManifold::<(), ()>::new();

// Initial computation
let pos12_old = Isometry::translation(1.9, 0.0, 0.0);
contact_manifold_ball_ball(&pos12_old, &ball1, &ball2, 0.1, &mut manifold);

// Next frame: shapes moved slightly
let pos12_new = Isometry::translation(1.85, 0.05, 0.0);

if manifold.try_update_contacts(&pos12_new) {
    println!("Updated contacts efficiently!");
} else {
    println!("Need to recompute from scratch");
    contact_manifold_ball_ball(&pos12_new, &ball1, &ball2, 0.1, &mut manifold);
}
Source

pub fn try_update_contacts_eps( &mut self, pos12: &Isometry<f32>, angle_dot_threshold: f32, dist_sq_threshold: f32, ) -> bool

Attempts to use spatial coherence to update contacts points, using user-defined tolerances.

Source

pub fn match_contacts(&mut self, old_contacts: &[TrackedContact<ContactData>])

Transfers contact data from previous frame’s contacts to current contacts based on feature IDs.

This method is crucial for maintaining persistent contact information across frames. It matches contacts between the old and new manifolds by comparing their feature IDs (which geometric features are in contact). When a match is found, the user data is transferred from the old contact to the new one.

This enables important physics features like:

  • Warm-starting: Reusing accumulated impulses speeds up constraint solving
  • Contact aging: Tracking how long a contact has existed
  • Friction state: Maintaining tangential impulse information
§When to Use

Call this method after recomputing the contact manifold, passing the old contact points from the previous frame. This preserves contact-specific solver state.

§Example
use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_ball_ball;
use parry3d::shape::Ball;
use parry3d::math::Isometry;

#[derive(Clone, Default, Copy)]
struct MyContactData {
    accumulated_impulse: f32,
    age: u32,
}

let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
let mut manifold = ContactManifold::<(), MyContactData>::new();

// Frame 1: Compute contacts
let pos12_frame1 = Isometry::translation(1.9, 0.0, 0.0);
contact_manifold_ball_ball(&pos12_frame1, &ball1, &ball2, 0.0, &mut manifold);

// Simulate physics, accumulate impulse
if let Some(contact) = manifold.points.first_mut() {
    contact.data.accumulated_impulse = 42.0;
    contact.data.age = 1;
}

// Frame 2: Save old contacts, recompute
let old_contacts = manifold.points.clone();
let pos12_frame2 = Isometry::translation(1.85, 0.0, 0.0);
contact_manifold_ball_ball(&pos12_frame2, &ball1, &ball2, 0.0, &mut manifold);

// Transfer data from old to new based on feature ID matching
manifold.match_contacts(&old_contacts);

// Data is preserved!
if let Some(contact) = manifold.points.first() {
    assert_eq!(contact.data.accumulated_impulse, 42.0);
}
Source

pub fn match_contacts_using_positions( &mut self, old_contacts: &[TrackedContact<ContactData>], dist_threshold: f32, )

Copy data associated to contacts from old_contacts to the new contacts in self based on matching the contact positions.

Source

pub fn clear(&mut self)

Removes all the contacts from self.

Source

pub fn find_deepest_contact(&self) -> Option<&TrackedContact<ContactData>>

Finds and returns the contact with the deepest penetration.

This returns the contact with the smallest (most negative) distance value, which corresponds to the largest penetration depth. Returns None if the manifold has no contact points.

§Use Cases
  • Finding the primary contact for simplified physics resolution
  • Determining the severity of an overlap for collision response
  • Prioritizing contacts in contact reduction algorithms
  • Debug visualization of the most significant contact
§Example
use parry3d::query::ContactManifold;
use parry3d::query::details::contact_manifold_cuboid_cuboid;
use parry3d::shape::Cuboid;
use parry3d::math::{Isometry, Vector};

let cuboid1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
let cuboid2 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));

// Position with some penetration
let pos12 = Isometry::translation(0.0, 1.8, 0.0);

let mut manifold = ContactManifold::<(), ()>::new();
contact_manifold_cuboid_cuboid(&pos12, &cuboid1, &cuboid2, 0.0, &mut manifold);

if let Some(deepest) = manifold.find_deepest_contact() {
    let penetration_depth = -deepest.dist;
    println!("Maximum penetration: {}", penetration_depth);
    println!("Deepest contact point (shape 1): {:?}", deepest.local_p1);
}

Trait Implementations§

Source§

impl<ManifoldData: Clone, ContactData: Clone> Clone for ContactManifold<ManifoldData, ContactData>

Source§

fn clone(&self) -> ContactManifold<ManifoldData, ContactData>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<ManifoldData: Debug, ContactData: Debug> Debug for ContactManifold<ManifoldData, ContactData>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<ManifoldData: Default, ContactData: Default> Default for ContactManifold<ManifoldData, ContactData>

Source§

fn default() -> ContactManifold<ManifoldData, ContactData>

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<ManifoldData, ContactData> Freeze for ContactManifold<ManifoldData, ContactData>
where ManifoldData: Freeze,

§

impl<ManifoldData, ContactData> RefUnwindSafe for ContactManifold<ManifoldData, ContactData>
where ManifoldData: RefUnwindSafe, ContactData: RefUnwindSafe,

§

impl<ManifoldData, ContactData> Send for ContactManifold<ManifoldData, ContactData>
where ManifoldData: Send, ContactData: Send,

§

impl<ManifoldData, ContactData> Sync for ContactManifold<ManifoldData, ContactData>
where ManifoldData: Sync, ContactData: Sync,

§

impl<ManifoldData, ContactData> Unpin for ContactManifold<ManifoldData, ContactData>
where ManifoldData: Unpin, ContactData: Unpin,

§

impl<ManifoldData, ContactData> UnwindSafe for ContactManifold<ManifoldData, ContactData>
where ManifoldData: UnwindSafe, ContactData: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.