parry2d/query/contact/contact_shape_shape.rs
1use crate::math::{Isometry, Real};
2use crate::query::{Contact, DefaultQueryDispatcher, QueryDispatcher, Unsupported};
3use crate::shape::Shape;
4
5/// Computes contact information between two shapes.
6///
7/// This function finds a single contact point (or pair of points) where two shapes
8/// touch or penetrate. It's one of the most important queries for physics simulation
9/// and collision response.
10///
11/// # Behavior
12///
13/// The function returns contact data in the following cases:
14///
15/// - **Penetrating**: Shapes overlap → Returns contact with `dist < 0.0`
16/// - **Touching**: Shapes just make contact → Returns contact with `dist ≈ 0.0`
17/// - **Nearly touching**: Distance ≤ `prediction` → Returns contact with `dist > 0.0`
18/// - **Separated**: Distance > `prediction` → Returns `None`
19///
20/// # Prediction Distance
21///
22/// The `prediction` parameter allows detecting contacts before shapes actually touch.
23/// This is useful for:
24///
25/// - **Continuous collision detection**: Predict contacts that will occur soon
26/// - **Speculative contacts**: Pre-compute contacts for upcoming frames
27/// - **Efficiency**: Avoid re-computing contacts every frame for slow-moving objects
28///
29/// Use `0.0` for exact contact detection only.
30///
31/// # Arguments
32///
33/// * `pos1` - Position and orientation of the first shape in world space
34/// * `g1` - The first shape
35/// * `pos2` - Position and orientation of the second shape in world space
36/// * `g2` - The second shape
37/// * `prediction` - Maximum separation distance for contact detection (typically `0.0`)
38///
39/// # Returns
40///
41/// * `Ok(Some(contact))` - Contact found (touching, penetrating, or within prediction distance)
42/// * `Ok(None)` - No contact (shapes separated by more than `prediction`)
43/// * `Err(Unsupported)` - This shape pair combination is not supported
44///
45/// # Contact Data
46///
47/// The returned [`Contact`] contains:
48/// - `point1`, `point2`: Contact points on each shape's surface (world space)
49/// - `normal1`, `normal2`: Surface normals at contact points (world space)
50/// - `dist`: Signed distance (negative = penetration depth)
51///
52/// # Performance
53///
54/// Performance depends on shape types:
55/// - **Convex-Convex**: Fast (GJK/EPA algorithms)
56/// - **Concave shapes**: Slower (requires BVH traversal)
57///
58/// # Example
59///
60/// ```rust
61/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
62/// use parry3d::query::contact;
63/// use parry3d::shape::{Ball, Cuboid};
64/// use nalgebra::{Isometry3, Vector3};
65///
66/// let ball = Ball::new(1.0);
67/// let cuboid = Cuboid::new(Vector3::new(2.0, 2.0, 2.0));
68///
69/// // Position shapes so they're penetrating
70/// let pos_ball = Isometry3::translation(2.5, 0.0, 0.0);
71/// let pos_cuboid = Isometry3::identity();
72///
73/// // Compute contact (no prediction distance)
74/// if let Ok(Some(contact)) = contact(&pos_ball, &ball, &pos_cuboid, &cuboid, 0.0) {
75/// // Penetration depth (negative distance)
76/// let penetration = -contact.dist;
77/// println!("Penetrating by: {} units", penetration);
78///
79/// // Normal points from ball toward cuboid
80/// println!("Separation direction: {:?}", contact.normal1);
81///
82/// // Use contact to resolve collision:
83/// // - Apply impulse along normal1 to separate shapes
84/// // - Move shapes apart by penetration depth
85/// }
86/// # }
87/// ```
88///
89/// # Example with Prediction
90///
91/// ```rust
92/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
93/// use parry3d::query::contact;
94/// use parry3d::shape::Ball;
95/// use nalgebra::Isometry3;
96///
97/// let ball1 = Ball::new(1.0);
98/// let ball2 = Ball::new(1.0);
99///
100/// // Balls separated by 2.2 units (just outside contact range)
101/// let pos1 = Isometry3::translation(0.0, 0.0, 0.0);
102/// let pos2 = Isometry3::translation(4.2, 0.0, 0.0); // radii sum = 2.0, gap = 2.2
103///
104/// // Without prediction: no contact
105/// assert!(contact(&pos1, &ball1, &pos2, &ball2, 0.0).unwrap().is_none());
106///
107/// // With prediction of 0.5: still no contact (gap 2.2 > 0.5)
108/// assert!(contact(&pos1, &ball1, &pos2, &ball2, 0.5).unwrap().is_none());
109///
110/// // With prediction of 3.0: contact detected
111/// if let Ok(Some(c)) = contact(&pos1, &ball1, &pos2, &ball2, 3.0) {
112/// assert!(c.dist > 0.0); // Positive = separated but within prediction
113/// assert!(c.dist <= 3.0); // Within prediction distance
114/// }
115/// # }
116/// ```
117///
118/// # See Also
119///
120/// - [`distance`](crate::query::distance::distance()) - For just the distance value
121/// - [`closest_points`](crate::query::closest_points::closest_points()) - For closest point locations
122/// - [`intersection_test`](crate::query::intersection_test::intersection_test()) - For boolean overlap test
123pub fn contact(
124 pos1: &Isometry<Real>,
125 g1: &dyn Shape,
126 pos2: &Isometry<Real>,
127 g2: &dyn Shape,
128 prediction: Real,
129) -> Result<Option<Contact>, Unsupported> {
130 let pos12 = pos1.inv_mul(pos2);
131 let mut result = DefaultQueryDispatcher.contact(&pos12, g1, g2, prediction);
132
133 if let Ok(Some(contact)) = &mut result {
134 contact.transform_by_mut(pos1, pos2);
135 }
136
137 result
138}