avian2d/collision/contact_types/
mod.rs

1//! Contact types and data structures used in the collision pipeline.
2
3mod contact_graph;
4mod feature_id;
5mod system_param;
6
7pub use contact_graph::{ContactGraph, ContactGraphInternal};
8pub use feature_id::PackedFeatureId;
9use smallvec::SmallVec;
10pub use system_param::Collisions;
11
12use crate::{
13    data_structures::graph::EdgeIndex,
14    dynamics::solver::{constraint_graph::ContactConstraintHandle, islands::IslandNode},
15    prelude::*,
16};
17use bevy::prelude::*;
18
19/// A stable identifier for a [`ContactEdge`].
20#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Reflect)]
21#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
23#[reflect(Debug, PartialEq)]
24pub struct ContactId(pub u32);
25
26impl ContactId {
27    /// A placeholder identifier for a [`ContactEdge`].
28    ///
29    /// Used as a temporary value before the contact pair is added to the [`ContactGraph`].
30    pub const PLACEHOLDER: Self = Self(u32::MAX);
31}
32
33impl From<ContactId> for EdgeIndex {
34    fn from(id: ContactId) -> Self {
35        Self(id.0)
36    }
37}
38
39impl From<EdgeIndex> for ContactId {
40    fn from(id: EdgeIndex) -> Self {
41        Self(id.0)
42    }
43}
44
45impl core::fmt::Display for ContactId {
46    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47        write!(f, "ContactId({})", self.0)
48    }
49}
50
51/// Cold contact data stored in the [`ContactGraph`]. Used as a persistent handle for a [`ContactPair`].
52#[derive(Clone, Debug, PartialEq)]
53#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
54pub struct ContactEdge {
55    /// The stable identifier of this contact edge.
56    pub id: ContactId,
57
58    /// The first collider entity in the contact.
59    pub collider1: Entity,
60
61    /// The second collider entity in the contact.
62    pub collider2: Entity,
63
64    /// The entity of the first body involved in the contact.
65    pub body1: Option<Entity>,
66
67    /// The entity of the second body involved in the contact.
68    pub body2: Option<Entity>,
69
70    /// The index of the [`ContactPair`] in the [`ContactGraph`].
71    pub pair_index: usize,
72
73    /// The handles to the constraints associated with this contact edge.
74    #[cfg(feature = "2d")]
75    pub constraint_handles: SmallVec<[ContactConstraintHandle; 2]>,
76
77    /// The handles to the constraints associated with this contact edge.
78    #[cfg(feature = "3d")]
79    pub constraint_handles: SmallVec<[ContactConstraintHandle; 4]>,
80
81    /// The [`IslandNode`] associated with this contact edge.
82    pub island: Option<IslandNode<ContactId>>,
83
84    /// Flags for the contact edge.
85    pub flags: ContactEdgeFlags,
86}
87
88impl ContactEdge {
89    /// Creates a new non-touching [`ContactEdge`] with the given entities.
90    #[inline]
91    pub fn new(collider1: Entity, collider2: Entity) -> Self {
92        Self {
93            // This gets set to a valid ID when the contact pair is added to the `ContactGraph`.
94            id: ContactId::PLACEHOLDER,
95            collider1,
96            collider2,
97            body1: None,
98            body2: None,
99            pair_index: 0,
100            constraint_handles: SmallVec::new(),
101            island: None,
102            flags: ContactEdgeFlags::empty(),
103        }
104    }
105
106    /// Returns `true` if the colliders are touching.
107    #[inline]
108    pub fn is_touching(&self) -> bool {
109        self.flags.contains(ContactEdgeFlags::TOUCHING)
110    }
111
112    /// Returns `true` if the contact pair is between sleeping bodies.
113    #[inline]
114    pub fn is_sleeping(&self) -> bool {
115        self.flags.contains(ContactEdgeFlags::SLEEPING)
116    }
117
118    /// Returns `true` if collision events are enabled for the contact.
119    #[inline]
120    pub fn events_enabled(&self) -> bool {
121        self.flags.contains(ContactEdgeFlags::CONTACT_EVENTS)
122    }
123}
124
125// These are stored separately from `ContactPairFlags` to avoid needing to fetch
126// the `ContactPair` when for example querying for touching contacts.
127/// Flags for a [`ContactEdge`].
128#[repr(transparent)]
129#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
130#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
131#[reflect(opaque, Hash, PartialEq, Debug)]
132pub struct ContactEdgeFlags(u8);
133
134bitflags::bitflags! {
135    impl ContactEdgeFlags: u8 {
136        /// Set if the colliders are touching, including sensors.
137        const TOUCHING = 1 << 0;
138        /// Set if the contact pair is between sleeping bodies.
139        const SLEEPING = 1 << 1;
140        /// Set if the contact pair should emit contact events or sensor events.
141        const CONTACT_EVENTS = 1 << 2;
142    }
143}
144
145/// A contact pair between two colliders.
146///
147/// Each contact pair has one or more [contact manifolds](ContactManifold),
148/// which represent contact surfaces between the two colliders.
149/// Each of these manifolds contains one or more [contact points](ContactPoint).
150///
151/// Contact pairs exist in the [`ContactGraph`] between colliders whose [`ColliderAabb`]s
152/// are overlapping, even if the colliders themselves are not touching.
153#[derive(Clone, Debug, PartialEq)]
154#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
155pub struct ContactPair {
156    /// The stable identifier of the [`ContactEdge`] in the [`ContactGraph`].
157    pub contact_id: ContactId,
158    /// The first collider entity in the contact.
159    pub collider1: Entity,
160    /// The second collider entity in the contact.
161    pub collider2: Entity,
162    /// The entity of the first body involved in the contact.
163    pub body1: Option<Entity>,
164    /// The entity of the second body involved in the contact.
165    pub body2: Option<Entity>,
166    /// A list of contact manifolds between two colliders.
167    /// Each manifold contains one or more contact points, but each contact
168    /// in a given manifold shares the same contact normal.
169    pub manifolds: Vec<ContactManifold>,
170    /// The number of manifolds that were added or removed during the current time step.
171    ///
172    /// This is used to manage contact constraint updates for persistent contacts in the [`ConstraintGraph`].
173    ///
174    /// [`ConstraintGraph`]: crate::dynamics::solver::constraint_graph::ConstraintGraph
175    pub(crate) manifold_count_change: i16,
176    /// Flag indicating the status and type of the contact pair.
177    pub flags: ContactPairFlags,
178}
179
180/// Flags indicating the status and type of a [contact pair](ContactPair).
181#[repr(transparent)]
182#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
183#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
184#[reflect(opaque, Hash, PartialEq, Debug)]
185pub struct ContactPairFlags(u16);
186
187bitflags::bitflags! {
188    impl ContactPairFlags: u16 {
189        /// Set if the colliders are touching, including sensors.
190        const TOUCHING = 1 << 0;
191        /// Set if the AABBs of the colliders are no longer overlapping.
192        const DISJOINT_AABB = 1 << 1;
193        /// Set if the colliders are touching and were not touching previously.
194        const STARTED_TOUCHING = 1 << 2;
195        /// Set if the colliders are not touching and were touching previously.
196        const STOPPED_TOUCHING = 1 << 3;
197        /// Set if the contact pair should generate contact constraints.
198        const GENERATE_CONSTRAINTS = 1 << 4;
199        /// Set if the contact pair just started generating contact constraints,
200        /// for example because a sensor became a normal collider or a collider was attached to a rigid body.
201        const STARTED_GENERATING_CONSTRAINTS = 1 << 5;
202        /// Set if the first rigid body is static.
203        const STATIC1 = 1 << 6;
204        /// Set if the second rigid body is static.
205        const STATIC2 = 1 << 7;
206        /// Set if the contact pair should have a custom contact modification hook applied.
207        const MODIFY_CONTACTS = 1 << 8;
208    }
209}
210
211impl ContactPair {
212    /// Creates a new [`ContactPair`] with the given entities.
213    #[inline]
214    pub fn new(collider1: Entity, collider2: Entity, contact_id: ContactId) -> Self {
215        Self {
216            contact_id,
217            collider1,
218            collider2,
219            body1: None,
220            body2: None,
221            manifolds: Vec::new(),
222            manifold_count_change: 0,
223            flags: ContactPairFlags::empty(),
224        }
225    }
226
227    /// Computes the sum of all impulses applied along contact normals between the contact pair.
228    ///
229    /// To get the corresponding force, divide the impulse by the time step.
230    #[inline]
231    pub fn total_normal_impulse(&self) -> Vector {
232        self.manifolds.iter().fold(Vector::ZERO, |acc, manifold| {
233            acc + manifold.normal * manifold.total_normal_impulse()
234        })
235    }
236
237    /// Computes the sum of the magnitudes of all impulses applied along contact normals between the contact pair.
238    ///
239    /// This is the sum of impulse magnitudes, *not* the magnitude of the [`total_normal_impulse`](Self::total_normal_impulse).
240    ///
241    /// To get the corresponding force, divide the impulse by the time step.
242    #[inline]
243    pub fn total_normal_impulse_magnitude(&self) -> Scalar {
244        self.manifolds
245            .iter()
246            .fold(0.0, |acc, manifold| acc + manifold.total_normal_impulse())
247    }
248
249    // TODO: We could also return a reference to the whole manifold. Would that be useful?
250    /// Finds the largest normal impulse between the contact pair, pointing along the world-space contact normal
251    /// from the first shape to the second.
252    ///
253    /// To get the corresponding force, divide the impulse by the time step.
254    #[inline]
255    pub fn max_normal_impulse(&self) -> Vector {
256        let mut magnitude: Scalar = Scalar::MIN;
257        let mut normal = Vector::ZERO;
258
259        for manifold in &self.manifolds {
260            let impulse = manifold.max_normal_impulse();
261            if impulse > magnitude {
262                magnitude = impulse;
263                normal = manifold.normal;
264            }
265        }
266        normal * magnitude
267    }
268
269    /// Finds the magnitude of the largest normal impulse between the contact pair.
270    ///
271    /// To get the corresponding force, divide the impulse by the time step.
272    #[inline]
273    pub fn max_normal_impulse_magnitude(&self) -> Scalar {
274        self.manifolds
275            .iter()
276            .fold(0.0, |acc, manifold| acc.max(manifold.max_normal_impulse()))
277    }
278
279    /// Returns `true` if the colliders are touching, including sensors.
280    #[inline]
281    pub fn is_touching(&self) -> bool {
282        self.flags.contains(ContactPairFlags::TOUCHING)
283    }
284
285    /// Returns `true` if the AABBs of the colliders are no longer overlapping.
286    #[inline]
287    pub fn aabbs_disjoint(&self) -> bool {
288        self.flags.contains(ContactPairFlags::DISJOINT_AABB)
289    }
290
291    /// Returns `true` if a collision started during the current frame.
292    #[inline]
293    pub fn collision_started(&self) -> bool {
294        self.flags.contains(ContactPairFlags::STARTED_TOUCHING)
295    }
296
297    /// Returns `true` if a collision ended during the current frame.
298    #[inline]
299    pub fn collision_ended(&self) -> bool {
300        self.flags.contains(ContactPairFlags::STOPPED_TOUCHING)
301    }
302
303    /// Returns `true` if the contact pair should generate contact constraints.
304    ///
305    /// This is typically `true` unless the contact pair involves a [`Sensor`] or a disabled rigid body.
306    #[inline]
307    pub fn generates_constraints(&self) -> bool {
308        self.flags.contains(ContactPairFlags::GENERATE_CONSTRAINTS)
309    }
310
311    /// Returns the contact with the largest penetration depth.
312    ///
313    /// If the objects are separated but there is still a speculative contact,
314    /// the penetration depth will be negative.
315    ///
316    /// If there are no contacts, `None` is returned.
317    #[inline]
318    pub fn find_deepest_contact(&self) -> Option<&ContactPoint> {
319        self.manifolds
320            .iter()
321            .filter_map(|manifold| manifold.find_deepest_contact())
322            .max_by(|a, b| {
323                a.penetration
324                    .partial_cmp(&b.penetration)
325                    .unwrap_or(core::cmp::Ordering::Equal)
326            })
327    }
328}
329
330/// A contact manifold describing a contact surface between two colliders,
331/// represented by a set of [contact points](ContactPoint) and surface properties.
332///
333/// A manifold can typically be a single point, a line segment, or a polygon formed by its contact points.
334/// Each contact point in a manifold shares the same contact normal.
335#[cfg_attr(
336    feature = "2d",
337    doc = "
338In 2D, contact manifolds are limited to 2 points."
339)]
340#[derive(Clone, Debug, PartialEq)]
341#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
342pub struct ContactManifold {
343    /// The contact points in this manifold. Limited to 2 points in 2D.
344    ///
345    /// Each point in a manifold shares the same `normal`.
346    #[cfg(feature = "2d")]
347    pub points: arrayvec::ArrayVec<ContactPoint, 2>,
348    /// The contact points in this manifold.
349    ///
350    /// Each point in a manifold shares the same `normal`.
351    #[cfg(feature = "3d")]
352    // TODO: Store a maximum of 4 points in 3D in an `ArrayVec`.
353    pub points: Vec<ContactPoint>,
354    /// The unit contact normal in world space, pointing from the first shape to the second.
355    ///
356    /// The same normal is shared by all `points` in a manifold.
357    pub normal: Vector,
358    /// The effective coefficient of dynamic [friction](Friction) used for the contact surface.
359    pub friction: Scalar,
360    /// The effective coefficient of [restitution](Restitution) used for the contact surface.
361    pub restitution: Scalar,
362    /// The desired relative linear speed of the bodies along the surface,
363    /// expressed in world space as `tangent_speed2 - tangent_speed1`.
364    ///
365    /// Defaults to zero. If set to a non-zero value, this can be used to simulate effects
366    /// such as conveyor belts.
367    #[cfg(feature = "2d")]
368    pub tangent_speed: Scalar,
369    // TODO: Jolt also supports a relative angular surface velocity, which can be used for making
370    //       objects rotate on platforms. Would that be useful enough to warrant the extra memory usage?
371    /// The desired relative linear velocity of the bodies along the surface,
372    /// expressed in world space as `tangent_velocity2 - tangent_velocity1`.
373    ///
374    /// Defaults to zero. If set to a non-zero value, this can be used to simulate effects
375    /// such as conveyor belts.
376    #[cfg(feature = "3d")]
377    pub tangent_velocity: Vector,
378}
379
380impl ContactManifold {
381    /// Creates a new [`ContactManifold`] with the given contact points and surface normals,
382    /// expressed in local space.
383    ///
384    /// `index` represents the index of the manifold in the collision.
385    #[inline]
386    pub fn new(points: impl IntoIterator<Item = ContactPoint>, normal: Vector) -> Self {
387        Self {
388            #[cfg(feature = "2d")]
389            points: arrayvec::ArrayVec::from_iter(points),
390            #[cfg(feature = "3d")]
391            points: points.into_iter().collect(),
392            normal,
393            friction: 0.0,
394            restitution: 0.0,
395            #[cfg(feature = "2d")]
396            tangent_speed: 0.0,
397            #[cfg(feature = "3d")]
398            tangent_velocity: Vector::ZERO,
399        }
400    }
401
402    /// The sum of the impulses applied at the contact points in the manifold along the contact normal.
403    #[inline]
404    pub fn total_normal_impulse(&self) -> Scalar {
405        self.points
406            .iter()
407            .fold(0.0, |acc, contact| acc + contact.normal_impulse)
408    }
409
410    /// The magnitude of the largest impulse applied at a contact point in the manifold along the contact normal.
411    #[inline]
412    pub fn max_normal_impulse(&self) -> Scalar {
413        self.points
414            .iter()
415            .map(|contact| contact.normal_impulse)
416            .max_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal))
417            .unwrap_or(0.0)
418    }
419
420    /// Copies impulses from previous contacts to matching contacts in `self`.
421    ///
422    /// Contacts are first matched based on their [feature IDs](PackedFeatureId), and if they are unknown,
423    /// matching is done based on contact positions using the given `distance_threshold`
424    /// for determining if points are too far away from each other to be considered matching.
425    #[inline]
426    pub fn match_contacts(
427        &mut self,
428        previous_contacts: &[ContactPoint],
429        distance_threshold: Scalar,
430    ) {
431        // The squared maximum distance for two contact points to be considered matching.
432        let distance_threshold_squared = distance_threshold.powi(2);
433
434        for contact in self.points.iter_mut() {
435            for previous_contact in previous_contacts.iter() {
436                // If the feature IDs match, copy the contact impulses over for warm starting.
437                if (contact.feature_id1 == previous_contact.feature_id1
438                    && contact.feature_id2 == previous_contact.feature_id2) ||
439                    // we have to check both directions because the entities are sorted in order
440                    // of aabb.min.x, which could have changed even the two objects in contact are the same
441                    (contact.feature_id2 == previous_contact.feature_id1
442                    && contact.feature_id1 == previous_contact.feature_id2)
443                {
444                    contact.warm_start_normal_impulse = previous_contact.warm_start_normal_impulse;
445                    contact.warm_start_tangent_impulse =
446                        previous_contact.warm_start_tangent_impulse;
447                    break;
448                }
449
450                let unknown_features = contact.feature_id1 == PackedFeatureId::UNKNOWN
451                    || contact.feature_id2 == PackedFeatureId::UNKNOWN;
452
453                // If the feature IDs are unknown and the contact positions match closely enough,
454                // copy the contact impulses over for warm starting.
455                if unknown_features
456                    && (contact.anchor1.distance_squared(previous_contact.anchor1)
457                        < distance_threshold_squared
458                        && contact.anchor2.distance_squared(previous_contact.anchor2)
459                            < distance_threshold_squared)
460                    || (contact.anchor1.distance_squared(previous_contact.anchor2)
461                        < distance_threshold_squared
462                        && contact.anchor2.distance_squared(previous_contact.anchor1)
463                            < distance_threshold_squared)
464                {
465                    contact.warm_start_normal_impulse = previous_contact.warm_start_normal_impulse;
466                    contact.warm_start_tangent_impulse =
467                        previous_contact.warm_start_tangent_impulse;
468                    break;
469                }
470            }
471        }
472    }
473
474    /// Prunes the contact points in the manifold to a maximum of 4 points.
475    /// This is done to improve performance and stability.
476    #[inline]
477    #[cfg(feature = "3d")]
478    pub fn prune_points(&mut self) {
479        // Based on `PruneContactPoints` in Jolt by Jorrit Rouwe.
480        // https://github.com/jrouwe/JoltPhysics/blob/f3dbdd2dadac4a5510391f103f264c0427d55c50/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp#L16
481
482        debug_assert!(
483            self.points.len() > 4,
484            "`ContactManifold::prune_points` called on a manifold with 4 or fewer points"
485        );
486
487        // We use a heuristic of `distance_to_com * penetration` to find the points we should keep.
488        // Neither of these should become zero, so we clamp the minimum distance.
489        const MIN_DISTANCE_SQUARED: Scalar = 1e-6;
490
491        // Project the contact points onto the contact normal and compute the squared penetration depths.
492        let (projected, penetrations_squared): (Vec<Vector>, Vec<Scalar>) = self
493            .points
494            .iter()
495            .map(|point| {
496                (
497                    point.anchor1.reject_from_normalized(self.normal),
498                    (point.penetration * point.penetration).max(MIN_DISTANCE_SQUARED),
499                )
500            })
501            .unzip();
502
503        // Find the point that is farthest from the center of mass, as its torque has the largest influence,
504        // while also taking into account the penetration depth heuristic.
505        let mut point1_index = 0;
506        let mut value = Scalar::MIN;
507        for (i, point) in projected.iter().enumerate() {
508            let v = point.length_squared().max(MIN_DISTANCE_SQUARED) * penetrations_squared[i];
509            if v > value {
510                value = v;
511                point1_index = i;
512            }
513        }
514        let point1 = projected[point1_index];
515
516        // Find the point farthest from the first point, forming a line segment.
517        // Also consider the penetration depth heuristic.
518        let mut point2_index = usize::MAX;
519        let mut max_distance = Scalar::MIN;
520        for (i, point) in projected.iter().enumerate() {
521            if i == point1_index {
522                continue;
523            }
524            let v =
525                point.distance_squared(point1).max(MIN_DISTANCE_SQUARED) * penetrations_squared[i];
526            if v > max_distance {
527                max_distance = v;
528                point2_index = i;
529            }
530        }
531        debug_assert!(point2_index != usize::MAX, "No second point found");
532        let point2 = projected[point2_index];
533
534        // Find the farthest points on both sides of the line segment in order to maximize the area.
535        let mut point3_index = usize::MAX;
536        let mut point4_index = usize::MAX;
537        let mut min_value = 0.0;
538        let mut max_value = 0.0;
539        let perp = (point2 - point1).cross(self.normal);
540        for (i, point) in projected.iter().enumerate() {
541            if i == point1_index || i == point2_index {
542                continue;
543            }
544            let v = perp.dot(point - point1);
545            if v < min_value {
546                min_value = v;
547                point3_index = i;
548            } else if v > max_value {
549                max_value = v;
550                point4_index = i;
551            }
552        }
553
554        // Construct the manifold, ensuring the order is correct to form a convex polygon.
555        // TODO: The points in the manifold should be in an `ArrayVec`, and the input points should be separate.
556        let points = core::mem::take(&mut self.points);
557        self.points.push(points[point1_index]);
558        if point3_index != usize::MAX {
559            self.points.push(points[point3_index]);
560        }
561        self.points.push(points[point2_index]);
562        if point4_index != usize::MAX {
563            debug_assert_ne!(point3_index, point4_index);
564            self.points.push(points[point4_index]);
565        }
566    }
567
568    /// Returns the contact point with the largest penetration depth.
569    ///
570    /// If the objects are separated but there is still a speculative contact,
571    /// the penetration depth will be negative.
572    ///
573    /// If there are no contacts, `None` is returned.
574    #[inline]
575    pub fn find_deepest_contact(&self) -> Option<&ContactPoint> {
576        self.points.iter().max_by(|a, b| {
577            a.penetration
578                .partial_cmp(&b.penetration)
579                .unwrap_or(core::cmp::Ordering::Equal)
580        })
581    }
582
583    /// Retains only the elements specified by the predicate.
584    #[inline]
585    pub(crate) fn retain_points_mut<F>(&mut self, f: F)
586    where
587        F: FnMut(&mut ContactPoint) -> bool,
588    {
589        #[cfg(feature = "2d")]
590        {
591            self.points.retain(f);
592        }
593        #[cfg(feature = "3d")]
594        {
595            self.points.retain_mut(f);
596        }
597    }
598}
599
600/// Data associated with a contact point in a [`ContactManifold`].
601#[derive(Clone, Copy, Debug, PartialEq)]
602#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
603pub struct ContactPoint {
604    /// The world-space contact point on the first shape relative to the center of mass.
605    pub anchor1: Vector,
606    /// The world-space contact point on the second shape relative to the center of mass.
607    pub anchor2: Vector,
608    /// The contact point in world space.
609    ///
610    /// This is the midpoint between the closest points on the surfaces of the two shapes.
611    ///
612    /// Note that because the contact point is expressed in world space,
613    /// it is subject to precision loss at large coordinates.
614    pub point: Vector,
615    /// The penetration depth.
616    ///
617    /// Can be negative if the objects are separated and [speculative collision] is enabled.
618    ///
619    /// [speculative collision]: crate::dynamics::ccd#speculative-collision
620    pub penetration: Scalar,
621    /// The total normal impulse applied to the first body at this contact point.
622    /// The unit is typically N⋅s or kg⋅m/s.
623    ///
624    /// This can be used to determine how "strong" a contact is. To compute the corresponding
625    /// contact force, divide the impulse by the time step.
626    pub normal_impulse: Scalar,
627    /// The relative velocity of the bodies at the contact point along the contact normal.
628    /// If negative, the bodies are approaching. The unit is typically m/s.
629    ///
630    /// This is computed before the solver, and can be used as an impact velocity to determine
631    /// how "strong" the contact is in a mass-independent way.
632    ///
633    /// Internally, the `normal_speed` is used for restitution.
634    pub normal_speed: Scalar,
635    /// The normal impulse used to warm start the contact solver.
636    ///
637    /// This corresponds to the clamped accumulated impulse from the last substep
638    /// of the previous time step.
639    pub warm_start_normal_impulse: Scalar,
640    /// The frictional impulse used to warm start the contact solver.
641    ///
642    /// This corresponds to the clamped accumulated impulse from the last substep
643    /// of the previous time step.
644    #[cfg(feature = "2d")]
645    #[doc(alias = "warm_start_friction_impulse")]
646    pub warm_start_tangent_impulse: Scalar,
647    /// The frictional impulse used to warm start the contact solver.
648    ///
649    /// This corresponds to the clamped accumulated impulse from the last substep
650    /// of the previous time step.
651    #[cfg(feature = "3d")]
652    #[doc(alias = "warm_start_friction_impulse")]
653    pub warm_start_tangent_impulse: Vector2,
654    /// The contact feature ID on the first shape. This indicates the ID of
655    /// the vertex, edge, or face of the contact, if one can be determined.
656    pub feature_id1: PackedFeatureId,
657    /// The contact feature ID on the second shape. This indicates the ID of
658    /// the vertex, edge, or face of the contact, if one can be determined.
659    pub feature_id2: PackedFeatureId,
660}
661
662impl ContactPoint {
663    /// Creates a new [`ContactPoint`] with the given world-space contact points
664    /// relative to the centers of mass of the two shapes, the world-space contact point,
665    /// and the penetration depth.
666    ///
667    /// [Feature IDs](PackedFeatureId) can be specified for the contact points using [`with_feature_ids`](Self::with_feature_ids).
668    #[allow(clippy::too_many_arguments)]
669    pub fn new(anchor1: Vector, anchor2: Vector, world_point: Vector, penetration: Scalar) -> Self {
670        Self {
671            anchor1,
672            anchor2,
673            point: world_point,
674            penetration,
675            normal_impulse: 0.0,
676            normal_speed: 0.0,
677            warm_start_normal_impulse: 0.0,
678            #[cfg(feature = "2d")]
679            warm_start_tangent_impulse: 0.0,
680            #[cfg(feature = "3d")]
681            warm_start_tangent_impulse: Vector2::ZERO,
682            feature_id1: PackedFeatureId::UNKNOWN,
683            feature_id2: PackedFeatureId::UNKNOWN,
684        }
685    }
686
687    /// Sets the [feature IDs](PackedFeatureId) of the contact points.
688    #[inline]
689    pub fn with_feature_ids(mut self, id1: PackedFeatureId, id2: PackedFeatureId) -> Self {
690        self.feature_id1 = id1;
691        self.feature_id2 = id2;
692        self
693    }
694
695    /// Flips the contact data, swapping the points and feature IDs,
696    /// and negating the impulses.
697    #[inline]
698    pub fn flip(&mut self) {
699        core::mem::swap(&mut self.anchor1, &mut self.anchor2);
700        core::mem::swap(&mut self.feature_id1, &mut self.feature_id2);
701        self.normal_impulse = -self.normal_impulse;
702        self.warm_start_normal_impulse = -self.warm_start_normal_impulse;
703        self.warm_start_tangent_impulse = -self.warm_start_tangent_impulse;
704    }
705
706    /// Returns a flipped copy of the contact data, swapping the points and feature IDs,
707    /// and negating the impulses.
708    #[inline]
709    pub fn flipped(&self) -> Self {
710        Self {
711            anchor1: self.anchor2,
712            anchor2: self.anchor1,
713            point: self.point,
714            penetration: self.penetration,
715            normal_impulse: -self.normal_impulse,
716            normal_speed: self.normal_speed,
717            warm_start_normal_impulse: -self.warm_start_normal_impulse,
718            warm_start_tangent_impulse: -self.warm_start_tangent_impulse,
719            feature_id1: self.feature_id2,
720            feature_id2: self.feature_id1,
721        }
722    }
723}
724
725/// Data related to a single contact between two bodies.
726///
727/// If you want a contact that belongs to a [contact manifold](ContactManifold) and has more data,
728/// see [`ContactPoint`].
729#[derive(Clone, Copy, Debug, PartialEq)]
730#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
731pub struct SingleContact {
732    /// The contact point on the first shape in local space.
733    pub local_point1: Vector,
734    /// The contact point on the second shape in local space.
735    pub local_point2: Vector,
736    /// The contact normal expressed in the local space of the first shape.
737    pub local_normal1: Vector,
738    /// The contact normal expressed in the local space of the second shape.
739    pub local_normal2: Vector,
740    /// Penetration depth.
741    pub penetration: Scalar,
742}
743
744impl SingleContact {
745    /// Creates a new [`SingleContact`]. The contact points and normals should be given in local space.
746    #[inline]
747    pub fn new(
748        local_point1: Vector,
749        local_point2: Vector,
750        local_normal1: Vector,
751        local_normal2: Vector,
752        penetration: Scalar,
753    ) -> Self {
754        Self {
755            local_point1,
756            local_point2,
757            local_normal1,
758            local_normal2,
759            penetration,
760        }
761    }
762
763    /// Returns the global contact point on the first shape,
764    /// transforming the local point by the given position and rotation.
765    #[inline]
766    pub fn global_point1(&self, position: &Position, rotation: &Rotation) -> Vector {
767        position.0 + rotation * self.local_point1
768    }
769
770    /// Returns the global contact point on the second shape,
771    /// transforming the local point by the given position and rotation.
772    #[inline]
773    pub fn global_point2(&self, position: &Position, rotation: &Rotation) -> Vector {
774        position.0 + rotation * self.local_point2
775    }
776
777    /// Returns the world-space contact normal pointing from the first shape to the second.
778    #[inline]
779    pub fn global_normal1(&self, rotation: &Rotation) -> Vector {
780        rotation * self.local_normal1
781    }
782
783    /// Returns the world-space contact normal pointing from the second shape to the first.
784    #[inline]
785    pub fn global_normal2(&self, rotation: &Rotation) -> Vector {
786        rotation * self.local_normal2
787    }
788
789    /// Flips the contact data, swapping the points and normals.
790    #[inline]
791    pub fn flip(&mut self) {
792        core::mem::swap(&mut self.local_point1, &mut self.local_point2);
793        core::mem::swap(&mut self.local_normal1, &mut self.local_normal2);
794    }
795
796    /// Returns a flipped copy of the contact data, swapping the points and normals.
797    #[inline]
798    pub fn flipped(&self) -> Self {
799        Self {
800            local_point1: self.local_point2,
801            local_point2: self.local_point1,
802            local_normal1: self.local_normal2,
803            local_normal2: self.local_normal1,
804            penetration: self.penetration,
805        }
806    }
807}