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}