Skip to main content

parry3d/query/distance/
distance.rs

1use crate::shape::SubShapeId;
2
3/// The distance between two shapes, and the sub-shapes it was measured between.
4#[derive(Copy, Clone, Debug, PartialEq)]
5pub struct ShapeDistance {
6    /// The separation between the two shapes; `0.0` when they touch or overlap.
7    pub distance: Real,
8    /// The sub-shape of the first shape the distance was measured to.
9    ///
10    /// Always `0` for a shape with no sub-shapes.
11    pub subshape1: SubShapeId,
12    /// The sub-shape of the second shape the distance was measured to.
13    ///
14    /// Always `0` for a shape with no sub-shapes.
15    pub subshape2: SubShapeId,
16}
17
18impl ShapeDistance {
19    /// A distance measured between shapes with no sub-shapes to distinguish.
20    pub fn new(distance: Real) -> Self {
21        Self {
22            distance,
23            subshape1: 0,
24            subshape2: 0,
25        }
26    }
27
28    /// Sets the sub-shapes this distance was measured between.
29    pub fn with_subshapes(mut self, subshape1: SubShapeId, subshape2: SubShapeId) -> Self {
30        self.subshape1 = subshape1;
31        self.subshape2 = subshape2;
32        self
33    }
34
35    /// Swaps the roles of the two shapes.
36    pub fn swapped(mut self) -> Self {
37        core::mem::swap(&mut self.subshape1, &mut self.subshape2);
38        self
39    }
40}
41
42#[cfg(feature = "alloc")]
43impl crate::partitioning::BvhLeafCost for ShapeDistance {
44    #[inline]
45    fn cost(&self) -> Real {
46        self.distance
47    }
48}
49
50impl From<Real> for ShapeDistance {
51    fn from(distance: Real) -> Self {
52        Self::new(distance)
53    }
54}
55
56use crate::math::{Pose, Real};
57
58use crate::query::{DefaultQueryDispatcher, QueryDispatcher, Unsupported};
59use crate::shape::Shape;
60
61/// Computes the minimum distance separating two shapes.
62///
63/// This is one of the most fundamental geometric queries in collision detection.
64/// It calculates the shortest distance between any two points on the surfaces of
65/// the two shapes.
66///
67/// # Behavior
68///
69/// - Returns the **shortest distance** between the two shape surfaces
70/// - Returns `0.0` if the shapes are **touching** (surfaces just make contact)
71/// - Returns `0.0` if the shapes are **penetrating** (overlapping)
72/// - Always returns a **non-negative** value
73///
74/// # Arguments
75///
76/// * `pos1` - Position and orientation of the first shape in world space
77/// * `g1` - The first shape (can be any shape implementing the `Shape` trait)
78/// * `pos2` - Position and orientation of the second shape in world space
79/// * `g2` - The second shape (can be any shape implementing the `Shape` trait)
80///
81/// # Returns
82///
83/// * `Ok(distance)` - The minimum distance between the shapes
84/// * `Err(Unsupported)` - If this shape pair combination is not supported
85///
86/// # Performance
87///
88/// Performance varies by shape type:
89/// - **Ball-Ball**: Very fast (analytical solution)
90/// - **Cuboid-Cuboid**: Fast (SAT-based)
91/// - **Convex-Convex**: Moderate (GJK algorithm)
92/// - **Concave shapes**: Slower (requires BVH traversal)
93///
94/// # Example
95///
96/// ```rust
97/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
98/// use parry3d::query::distance;
99/// use parry3d::shape::Ball;
100/// use parry3d::math::Pose;
101///
102/// // Create two balls
103/// let ball1 = Ball::new(1.0);
104/// let ball2 = Ball::new(2.0);
105///
106/// // Position them 10 units apart along the x-axis
107/// let pos1 = Pose::translation(0.0, 0.0, 0.0);
108/// let pos2 = Pose::translation(10.0, 0.0, 0.0);
109///
110/// // Compute distance
111/// let dist = distance(&pos1, &ball1, &pos2, &ball2).unwrap().distance;
112///
113/// // Distance = 10.0 (separation) - 1.0 (radius1) - 2.0 (radius2) = 7.0
114/// assert_eq!(dist, 7.0);
115/// # }
116/// ```
117///
118/// ```rust
119/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
120/// use parry3d::query::distance;
121/// use parry3d::shape::Cuboid;
122/// use parry3d::math::{Pose, Vector};
123///
124/// // Create two boxes
125/// let box1 = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
126/// let box2 = Cuboid::new(Vector::new(0.5, 0.5, 0.5));
127///
128/// // Position them so they're touching
129/// let pos1 = Pose::translation(0.0, 0.0, 0.0);
130/// let pos2 = Pose::translation(1.5, 0.0, 0.0); // Edge to edge
131///
132/// let dist = distance(&pos1, &box1, &pos2, &box2).unwrap().distance;
133///
134/// // They're touching, so distance is 0.0
135/// assert_eq!(dist, 0.0);
136/// # }
137/// ```
138///
139/// # See Also
140///
141/// - [`closest_points`](crate::query::closest_points()) - For finding the actual closest points
142/// - [`contact`](crate::query::contact()) - For penetration depth when overlapping
143/// - [`intersection_test`](crate::query::intersection_test()) - For boolean overlap test
144pub fn distance(
145    pos1: &Pose,
146    g1: &dyn Shape,
147    pos2: &Pose,
148    g2: &dyn Shape,
149) -> Result<ShapeDistance, Unsupported> {
150    let pos12 = pos1.inv_mul(pos2);
151    DefaultQueryDispatcher.distance(&pos12, g1, g2)
152}