parry3d/query/
query_dispatcher.rs

1use crate::math::{Isometry, Real, Vector};
2use crate::query::details::ShapeCastOptions;
3#[cfg(feature = "alloc")]
4use crate::query::{
5    contact_manifolds::{ContactManifoldsWorkspace, NormalConstraints},
6    ContactManifold,
7};
8use crate::query::{ClosestPoints, Contact, NonlinearRigidMotion, ShapeCastHit, Unsupported};
9use crate::shape::Shape;
10#[cfg(feature = "alloc")]
11use alloc::vec::Vec;
12
13#[cfg(feature = "alloc")]
14/// A query dispatcher for queries relying on spatial coherence, including contact-manifold computation.
15pub trait PersistentQueryDispatcher<ManifoldData = (), ContactData = ()>: QueryDispatcher {
16    /// Compute all the contacts between two shapes.
17    ///
18    /// The output is written into `manifolds` and `context`. Both can persist
19    /// between multiple calls to `contacts` by re-using the result of the previous
20    /// call to `contacts`. This persistence can significantly improve collision
21    /// detection performances by allowing the underlying algorithms to exploit
22    /// spatial and temporal coherence.
23    fn contact_manifolds(
24        &self,
25        pos12: &Isometry<Real>,
26        g1: &dyn Shape,
27        g2: &dyn Shape,
28        prediction: Real,
29        manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
30        workspace: &mut Option<ContactManifoldsWorkspace>,
31    ) -> Result<(), Unsupported>;
32
33    /// Computes the contact-manifold between two convex shapes.
34    fn contact_manifold_convex_convex(
35        &self,
36        pos12: &Isometry<Real>,
37        g1: &dyn Shape,
38        g2: &dyn Shape,
39        normal_constraints1: Option<&dyn NormalConstraints>,
40        normal_constraints2: Option<&dyn NormalConstraints>,
41        prediction: Real,
42        manifold: &mut ContactManifold<ManifoldData, ContactData>,
43    ) -> Result<(), Unsupported>;
44}
45
46/// Dispatcher for pairwise queries.
47///
48/// Custom implementations allow crates that support an abstract `QueryDispatcher` to handle custom
49/// shapes.
50///
51/// The `pos12` argument to most queries is the transform from the local space of `g2` to that of
52/// `g1`.
53pub trait QueryDispatcher: Send + Sync {
54    /// Tests whether two shapes are intersecting.
55    fn intersection_test(
56        &self,
57        pos12: &Isometry<Real>,
58        g1: &dyn Shape,
59        g2: &dyn Shape,
60    ) -> Result<bool, Unsupported>;
61
62    /// Computes the minimum distance separating two shapes.
63    ///
64    /// Returns `0.0` if the objects are touching or penetrating.
65    fn distance(
66        &self,
67        pos12: &Isometry<Real>,
68        g1: &dyn Shape,
69        g2: &dyn Shape,
70    ) -> Result<Real, Unsupported>;
71
72    /// Computes one pair of contact points point between two shapes.
73    ///
74    /// Returns `None` if the objects are separated by a distance greater than `prediction`.
75    fn contact(
76        &self,
77        pos12: &Isometry<Real>,
78        g1: &dyn Shape,
79        g2: &dyn Shape,
80        prediction: Real,
81    ) -> Result<Option<Contact>, Unsupported>;
82
83    /// Computes the pair of closest points between two shapes.
84    ///
85    /// Returns `ClosestPoints::Disjoint` if the objects are separated by a distance greater than `max_dist`.
86    fn closest_points(
87        &self,
88        pos12: &Isometry<Real>,
89        g1: &dyn Shape,
90        g2: &dyn Shape,
91        max_dist: Real,
92    ) -> Result<ClosestPoints, Unsupported>;
93
94    /// Computes the smallest time when two shapes under translational movement are separated by a
95    /// distance smaller or equal to `distance`.
96    ///
97    /// Returns `0.0` if the objects are touching or penetrating.
98    ///
99    /// # Parameters
100    /// - `pos12`: the position of the second shape relative to the first shape.
101    /// - `local_vel12`: the relative velocity between the two shapes, expressed in the local-space
102    ///   of the first shape. In other world: `pos1.inverse() * (vel2 - vel1)`.
103    /// - `g1`: the first shape involved in the shape-cast.
104    /// - `g2`: the second shape involved in the shape-cast.
105    /// - `target_dist`: a hit will be returned as soon as the two shapes get closer than `target_dist`.
106    /// - `max_time_of_impact`: the maximum allowed travel time. This method returns `None` if the time-of-impact
107    ///   detected is theater than this value.
108    fn cast_shapes(
109        &self,
110        pos12: &Isometry<Real>,
111        local_vel12: &Vector<Real>,
112        g1: &dyn Shape,
113        g2: &dyn Shape,
114        options: ShapeCastOptions,
115    ) -> Result<Option<ShapeCastHit>, Unsupported>;
116
117    /// Construct a `QueryDispatcher` that falls back on `other` for cases not handled by `self`
118    fn chain<U: QueryDispatcher>(self, other: U) -> QueryDispatcherChain<Self, U>
119    where
120        Self: Sized,
121    {
122        QueryDispatcherChain(self, other)
123    }
124
125    /// Computes the smallest time of impact of two shapes under translational and rotational movement.
126    ///
127    /// # Parameters
128    /// * `motion1` - The motion of the first shape.
129    /// * `g1` - The first shape involved in the query.
130    /// * `motion2` - The motion of the second shape.
131    /// * `g2` - The second shape involved in the query.
132    /// * `start_time` - The starting time of the interval where the motion takes place.
133    /// * `end_time` - The end time of the interval where the motion takes place.
134    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
135    ///   collider, two results are possible. If `stop_at_penetration` is `true` then, the
136    ///   result will have a `time_of_impact` equal to `start_time`. If `stop_at_penetration` is `false`
137    ///   then the nonlinear shape-casting will see if further motion wrt. the penetration normal
138    ///   would result in tunnelling. If it does not (i.e. we have a separating velocity along
139    ///   that normal) then the nonlinear shape-casting will attempt to find another impact,
140    ///   at a time `> start_time` that could result in tunnelling.
141    fn cast_shapes_nonlinear(
142        &self,
143        motion1: &NonlinearRigidMotion,
144        g1: &dyn Shape,
145        motion2: &NonlinearRigidMotion,
146        g2: &dyn Shape,
147        start_time: Real,
148        end_time: Real,
149        stop_at_penetration: bool,
150    ) -> Result<Option<ShapeCastHit>, Unsupported>;
151}
152
153/// The composition of two dispatchers
154pub struct QueryDispatcherChain<T, U>(T, U);
155
156macro_rules! chain_method {
157    ($name:ident ( $( $arg:ident : $ty:ty,)*) -> $result:ty) => {
158        fn $name(&self, $($arg : $ty,)*
159        ) -> Result<$result, Unsupported> {
160            (self.0).$name($($arg,)*)
161                .or_else(|Unsupported| (self.1).$name($($arg,)*))
162        }
163    }
164}
165
166impl<T, U> QueryDispatcher for QueryDispatcherChain<T, U>
167where
168    T: QueryDispatcher,
169    U: QueryDispatcher,
170{
171    chain_method!(intersection_test(
172        pos12: &Isometry<Real>,
173        g1: &dyn Shape,
174        g2: &dyn Shape,
175    ) -> bool);
176
177    chain_method!(distance(pos12: &Isometry<Real>, g1: &dyn Shape, g2: &dyn Shape,) -> Real);
178
179    chain_method!(contact(
180        pos12: &Isometry<Real>,
181        g1: &dyn Shape,
182        g2: &dyn Shape,
183        prediction: Real,
184    ) -> Option<Contact>);
185
186    chain_method!(closest_points(
187        pos12: &Isometry<Real>,
188        g1: &dyn Shape,
189        g2: &dyn Shape,
190        max_dist: Real,
191    ) -> ClosestPoints);
192
193    chain_method!(cast_shapes(
194        pos12: &Isometry<Real>,
195        vel12: &Vector<Real>,
196        g1: &dyn Shape,
197        g2: &dyn Shape,
198        options: ShapeCastOptions,
199    ) -> Option<ShapeCastHit>);
200
201    chain_method!(cast_shapes_nonlinear(
202        motion1: &NonlinearRigidMotion,
203        g1: &dyn Shape,
204        motion2: &NonlinearRigidMotion,
205        g2: &dyn Shape,
206        start_time: Real,
207        end_time: Real,
208        stop_at_penetration: bool,
209    ) -> Option<ShapeCastHit>);
210}
211
212#[cfg(feature = "alloc")]
213impl<ManifoldData, ContactData, T, U> PersistentQueryDispatcher<ManifoldData, ContactData>
214    for QueryDispatcherChain<T, U>
215where
216    T: PersistentQueryDispatcher<ManifoldData, ContactData>,
217    U: PersistentQueryDispatcher<ManifoldData, ContactData>,
218{
219    chain_method!(contact_manifolds(
220        pos12: &Isometry<Real>,
221        g1: &dyn Shape,
222        g2: &dyn Shape,
223        prediction: Real,
224        manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
225        workspace: &mut Option<ContactManifoldsWorkspace>,
226    ) -> ());
227
228    chain_method!(contact_manifold_convex_convex(
229        pos12: &Isometry<Real>,
230        g1: &dyn Shape,
231        g2: &dyn Shape,
232        normal_constraints1: Option<&dyn NormalConstraints>,
233        normal_constraints2: Option<&dyn NormalConstraints>,
234        prediction: Real,
235        manifold: &mut ContactManifold<ManifoldData, ContactData>,
236    ) -> ());
237}