Skip to main content

parry3d/query/intersection_test/
intersection_test.rs

1use crate::shape::SubShapeId;
2
3/// Whether two shapes intersect, and the sub-shapes that do.
4#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5pub struct ShapeIntersection {
6    /// Whether the two shapes intersect.
7    pub intersecting: bool,
8    /// The sub-shape of the first shape that intersects.
9    ///
10    /// Always `0` for a shape with no sub-shapes, and meaningless unless `intersecting` is `true`.
11    pub subshape1: SubShapeId,
12    /// The sub-shape of the second shape that intersects.
13    ///
14    /// Always `0` for a shape with no sub-shapes, and meaningless unless `intersecting` is `true`.
15    pub subshape2: SubShapeId,
16}
17
18impl ShapeIntersection {
19    /// A result between shapes with no sub-shapes to distinguish.
20    pub fn new(intersecting: bool) -> Self {
21        Self {
22            intersecting,
23            subshape1: 0,
24            subshape2: 0,
25        }
26    }
27
28    /// Sets the sub-shapes that intersect.
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
42impl From<bool> for ShapeIntersection {
43    fn from(intersecting: bool) -> Self {
44        Self::new(intersecting)
45    }
46}
47
48use crate::math::Pose;
49use crate::query::{DefaultQueryDispatcher, QueryDispatcher, Unsupported};
50use crate::shape::Shape;
51
52/// Tests whether two shapes are intersecting (overlapping).
53///
54/// This is the fastest collision query, returning only a boolean result without
55/// computing contact points, normals, or penetration depth. Use this when you only
56/// need to know **if** shapes collide, not **where** or **how much**.
57///
58/// # Behavior
59///
60/// Returns `true` if:
61/// - Shapes are **touching** (surfaces just make contact)
62/// - Shapes are **penetrating** (overlapping with any amount)
63///
64/// Returns `false` if:
65/// - Shapes are **separated** (any distance apart, even very close)
66///
67/// # Performance
68///
69/// This is the fastest collision detection query:
70/// - **Ball-Ball**: Extremely fast (just distance check)
71/// - **AABB-AABB**: Very fast (6 comparisons in 3D)
72/// - **Convex-Convex**: Fast (early-exit GJK algorithm)
73/// - **Concave shapes**: Uses BVH for acceleration
74///
75/// Significantly faster than `contact()` or `distance()` because it can
76/// terminate early once any intersection is found.
77///
78/// # Arguments
79///
80/// * `pos1` - Position and orientation of the first shape
81/// * `g1` - The first shape
82/// * `pos2` - Position and orientation of the second shape
83/// * `g2` - The second shape
84///
85/// # Returns
86///
87/// * `Ok(true)` - Shapes are intersecting
88/// * `Ok(false)` - Shapes are not intersecting
89/// * `Err(Unsupported)` - This shape pair combination is not supported
90///
91/// # Example
92///
93/// ```rust
94/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
95/// use parry3d::query::intersection_test;
96/// use parry3d::shape::Ball;
97/// use parry3d::math::Pose;
98///
99/// let ball1 = Ball::new(1.0);
100/// let ball2 = Ball::new(1.0);
101///
102/// // Overlapping balls
103/// let pos1 = Pose::translation(0.0, 0.0, 0.0);
104/// let pos2 = Pose::translation(1.5, 0.0, 0.0);
105///
106/// let intersecting = intersection_test(&pos1, &ball1, &pos2, &ball2).unwrap().intersecting;
107/// assert!(intersecting); // Distance 1.5 < combined radii 2.0
108///
109/// // Separated balls
110/// let pos3 = Pose::translation(5.0, 0.0, 0.0);
111/// let not_intersecting = intersection_test(&pos1, &ball1, &pos3, &ball2).unwrap().intersecting;
112/// assert!(!not_intersecting); // Distance 5.0 > combined radii 2.0
113/// # }
114/// ```
115///
116/// # Use Cases
117///
118/// - **Trigger volumes**: Detect when player enters an area
119/// - **Broad-phase**: Quickly filter out distant object pairs
120/// - **Game logic**: Simple overlap detection (pickup items, damage zones)
121/// - **Optimization**: Pre-check before expensive narrow-phase queries
122///
123/// # When to Use Other Queries
124///
125/// - Need contact points/normal? → Use [`contact`](crate::query::contact())
126/// - Need penetration depth? → Use [`contact`](crate::query::contact())
127/// - Need separation distance? → Use [`distance`](crate::query::distance())
128/// - Need closest points? → Use [`closest_points`](crate::query::closest_points())
129///
130/// # See Also
131///
132/// - [`contact`](crate::query::contact()) - Get contact information if intersecting
133/// - [`distance`](crate::query::distance()) - Get separation distance
134/// - [`closest_points`](crate::query::closest_points()) - Get closest point locations
135pub fn intersection_test(
136    pos1: &Pose,
137    g1: &dyn Shape,
138    pos2: &Pose,
139    g2: &dyn Shape,
140) -> Result<ShapeIntersection, Unsupported> {
141    let pos12 = pos1.inv_mul(pos2);
142    DefaultQueryDispatcher.intersection_test(&pos12, g1, g2)
143}