avian3d/spatial_query/
system_param.rs

1use crate::prelude::*;
2use bevy::{ecs::system::SystemParam, prelude::*};
3
4/// A system parameter for performing [spatial queries](spatial_query).
5///
6/// # Methods
7///
8/// - [Raycasting](spatial_query#raycasting): [`cast_ray`](SpatialQuery::cast_ray), [`cast_ray_predicate`](SpatialQuery::cast_ray_predicate),
9///   [`ray_hits`](SpatialQuery::ray_hits), [`ray_hits_callback`](SpatialQuery::ray_hits_callback)
10/// - [Shapecasting](spatial_query#shapecasting): [`cast_shape`](SpatialQuery::cast_shape), [`cast_shape_predicate`](SpatialQuery::cast_shape_predicate),
11///   [`shape_hits`](SpatialQuery::shape_hits), [`shape_hits_callback`](SpatialQuery::shape_hits_callback)
12/// - [Point projection](spatial_query#point-projection): [`project_point`](SpatialQuery::project_point) and [`project_point_predicate`](SpatialQuery::project_point_predicate)
13/// - [Intersection tests](spatial_query#intersection-tests)
14///     - Point intersections: [`point_intersections`](SpatialQuery::point_intersections),
15///       [`point_intersections_callback`](SpatialQuery::point_intersections_callback)
16///     - AABB intersections: [`aabb_intersections_with_aabb`](SpatialQuery::aabb_intersections_with_aabb),
17///       [`aabb_intersections_with_aabb_callback`](SpatialQuery::aabb_intersections_with_aabb_callback)
18///     - Shape intersections: [`shape_intersections`](SpatialQuery::shape_intersections)
19///       [`shape_intersections_callback`](SpatialQuery::shape_intersections_callback)
20///
21/// For simple raycasts and shapecasts, consider using the [`RayCaster`] and [`ShapeCaster`] components that
22/// provide a more ECS-based approach and perform casts on every frame.
23///
24/// # Example
25///
26/// ```
27/// # #[cfg(feature = "2d")]
28/// # use avian2d::prelude::*;
29/// # #[cfg(feature = "3d")]
30/// use avian3d::prelude::*;
31/// use bevy::prelude::*;
32///
33/// # #[cfg(all(feature = "3d", feature = "f32"))]
34/// fn print_hits(spatial_query: SpatialQuery) {
35///     // Ray origin and direction
36///     let origin = Vec3::ZERO;
37///     let direction = Dir3::X;
38///
39///     // Configuration for the ray cast
40///     let max_distance = 100.0;
41///     let solid = true;
42///     let filter = SpatialQueryFilter::default();
43///
44///     // Cast ray and print first hit
45///     if let Some(first_hit) = spatial_query.cast_ray(origin, direction, max_distance, solid, &filter) {
46///         println!("First hit: {:?}", first_hit);
47///     }
48///
49///     // Cast ray and get up to 20 hits
50///     let hits = spatial_query.ray_hits(origin, direction, max_distance, 20, solid, &filter);
51///
52///     // Print hits
53///     for hit in hits.iter() {
54///         println!("Hit: {:?}", hit);
55///     }
56/// }
57/// ```
58#[derive(SystemParam)]
59pub struct SpatialQuery<'w, 's> {
60    pub(crate) colliders: Query<
61        'w,
62        's,
63        (
64            Entity,
65            &'static Position,
66            &'static Rotation,
67            &'static Collider,
68            &'static CollisionLayers,
69        ),
70        Without<ColliderDisabled>,
71    >,
72    /// The [`SpatialQueryPipeline`].
73    pub query_pipeline: ResMut<'w, SpatialQueryPipeline>,
74}
75
76impl SpatialQuery<'_, '_> {
77    /// Updates the colliders in the pipeline. This is done automatically once per physics frame in
78    /// [`PhysicsStepSet::SpatialQuery`], but if you modify colliders or their positions before that, you can
79    /// call this to make sure the data is up to date when performing spatial queries using [`SpatialQuery`].
80    pub fn update_pipeline(&mut self) {
81        self.query_pipeline.update(self.colliders.iter());
82    }
83
84    /// Casts a [ray](spatial_query#raycasting) and computes the closest [hit](RayHitData) with a collider.
85    /// If there are no hits, `None` is returned.
86    ///
87    /// # Arguments
88    ///
89    /// - `origin`: Where the ray is cast from.
90    /// - `direction`: What direction the ray is cast in.
91    /// - `max_distance`: The maximum distance the ray can travel.
92    /// - `solid`: If true *and* the ray origin is inside of a collider, the hit point will be the ray origin itself.
93    ///   Otherwise, the collider will be treated as hollow, and the hit point will be at its boundary.
94    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
95    ///
96    /// # Example
97    ///
98    /// ```
99    /// # #[cfg(feature = "2d")]
100    /// # use avian2d::prelude::*;
101    /// # #[cfg(feature = "3d")]
102    /// use avian3d::prelude::*;
103    /// use bevy::prelude::*;
104    ///
105    /// # #[cfg(all(feature = "3d", feature = "f32"))]
106    /// fn print_hits(spatial_query: SpatialQuery) {
107    ///     // Ray origin and direction
108    ///     let origin = Vec3::ZERO;
109    ///     let direction = Dir3::X;
110    ///
111    ///     // Configuration for the ray cast
112    ///     let max_distance = 100.0;
113    ///     let solid = true;
114    ///     let filter = SpatialQueryFilter::default();
115    ///
116    ///     // Cast ray and print first hit
117    ///     if let Some(first_hit) = spatial_query.cast_ray(origin, direction, max_distance, solid, &filter) {
118    ///         println!("First hit: {:?}", first_hit);
119    ///     }
120    /// }
121    /// ```
122    ///
123    /// # Related Methods
124    ///
125    /// - [`SpatialQuery::cast_ray_predicate`]
126    /// - [`SpatialQuery::ray_hits`]
127    /// - [`SpatialQuery::ray_hits_callback`]
128    pub fn cast_ray(
129        &self,
130        origin: Vector,
131        direction: Dir,
132        max_distance: Scalar,
133        solid: bool,
134        filter: &SpatialQueryFilter,
135    ) -> Option<RayHitData> {
136        self.query_pipeline
137            .cast_ray(origin, direction, max_distance, solid, filter)
138    }
139
140    /// Casts a [ray](spatial_query#raycasting) and computes the closest [hit](RayHitData) with a collider.
141    /// If there are no hits, `None` is returned.
142    ///
143    /// # Arguments
144    ///
145    /// - `origin`: Where the ray is cast from.
146    /// - `direction`: What direction the ray is cast in.
147    /// - `max_distance`: The maximum distance the ray can travel.
148    /// - `solid`: If true *and* the ray origin is inside of a collider, the hit point will be the ray origin itself.
149    ///   Otherwise, the collider will be treated as hollow, and the hit point will be at its boundary.
150    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
151    /// - `predicate`: A function called on each entity hit by the ray. The ray keeps travelling until the predicate returns `false`.
152    ///
153    /// # Example
154    ///
155    /// ```
156    /// # #[cfg(feature = "2d")]
157    /// # use avian2d::prelude::*;
158    /// # #[cfg(feature = "3d")]
159    /// use avian3d::prelude::*;
160    /// use bevy::prelude::*;
161    ///
162    /// #[derive(Component)]
163    /// struct Invisible;
164    ///
165    /// # #[cfg(all(feature = "3d", feature = "f32"))]
166    /// fn print_hits(spatial_query: SpatialQuery, query: Query<&Invisible>) {
167    ///     // Ray origin and direction
168    ///     let origin = Vec3::ZERO;
169    ///     let direction = Dir3::X;
170    ///
171    ///     // Configuration for the ray cast
172    ///     let max_distance = 100.0;
173    ///     let solid = true;
174    ///     let filter = SpatialQueryFilter::default();
175    ///
176    ///     // Cast ray and get the first hit that matches the predicate
177    ///     let hit = spatial_query.cast_ray_predicate(origin, direction, max_distance, solid, &filter, &|entity| {
178    ///         // Skip entities with the `Invisible` component.
179    ///         !query.contains(entity)
180    ///     });
181    ///
182    ///     // Print first hit
183    ///     if let Some(first_hit) = hit {
184    ///         println!("First hit: {:?}", first_hit);
185    ///     }
186    /// }
187    /// ```
188    ///
189    /// # Related Methods
190    ///
191    /// - [`SpatialQuery::cast_ray`]
192    /// - [`SpatialQuery::ray_hits`]
193    /// - [`SpatialQuery::ray_hits_callback`]
194    pub fn cast_ray_predicate(
195        &self,
196        origin: Vector,
197        direction: Dir,
198        max_distance: Scalar,
199        solid: bool,
200        filter: &SpatialQueryFilter,
201        predicate: &dyn Fn(Entity) -> bool,
202    ) -> Option<RayHitData> {
203        self.query_pipeline.cast_ray_predicate(
204            origin,
205            direction,
206            max_distance,
207            solid,
208            filter,
209            predicate,
210        )
211    }
212
213    /// Casts a [ray](spatial_query#raycasting) and computes all [hits](RayHitData) until `max_hits` is reached.
214    ///
215    /// Note that the order of the results is not guaranteed, and if there are more hits than `max_hits`,
216    /// some hits will be missed.
217    ///
218    /// # Arguments
219    ///
220    /// - `origin`: Where the ray is cast from.
221    /// - `direction`: What direction the ray is cast in.
222    /// - `max_distance`: The maximum distance the ray can travel.
223    /// - `max_hits`: The maximum number of hits. Additional hits will be missed.
224    /// - `solid`: If true *and* the ray origin is inside of a collider, the hit point will be the ray origin itself.
225    ///   Otherwise, the collider will be treated as hollow, and the hit point will be at its boundary.
226    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
227    ///
228    /// # Example
229    ///
230    /// ```
231    /// # #[cfg(feature = "2d")]
232    /// # use avian2d::prelude::*;
233    /// # #[cfg(feature = "3d")]
234    /// use avian3d::prelude::*;
235    /// use bevy::prelude::*;
236    ///
237    /// # #[cfg(all(feature = "3d", feature = "f32"))]
238    /// fn print_hits(spatial_query: SpatialQuery) {
239    ///     // Ray origin and direction
240    ///     let origin = Vec3::ZERO;
241    ///     let direction = Dir3::X;
242    ///
243    ///     // Configuration for the ray cast
244    ///     let max_distance = 100.0;
245    ///     let solid = true;
246    ///     let filter = SpatialQueryFilter::default();
247    ///
248    ///     // Cast ray and get up to 20 hits
249    ///     let hits = spatial_query.ray_hits(origin, direction, max_distance, 20, solid, &filter);
250    ///
251    ///     // Print hits
252    ///     for hit in hits.iter() {
253    ///         println!("Hit: {:?}", hit);
254    ///     }
255    /// }
256    /// ```
257    ///
258    /// # Related Methods
259    ///
260    /// - [`SpatialQuery::cast_ray`]
261    /// - [`SpatialQuery::cast_ray_predicate`]
262    /// - [`SpatialQuery::ray_hits_callback`]
263    pub fn ray_hits(
264        &self,
265        origin: Vector,
266        direction: Dir,
267        max_distance: Scalar,
268        max_hits: u32,
269        solid: bool,
270        filter: &SpatialQueryFilter,
271    ) -> Vec<RayHitData> {
272        self.query_pipeline
273            .ray_hits(origin, direction, max_distance, max_hits, solid, filter)
274    }
275
276    /// Casts a [ray](spatial_query#raycasting) and computes all [hits](RayHitData), calling the given `callback`
277    /// for each hit. The raycast stops when `callback` returns false or all hits have been found.
278    ///
279    /// Note that the order of the results is not guaranteed.
280    ///
281    /// # Arguments
282    ///
283    /// - `origin`: Where the ray is cast from.
284    /// - `direction`: What direction the ray is cast in.
285    /// - `max_distance`: The maximum distance the ray can travel.
286    /// - `solid`: If true *and* the ray origin is inside of a collider, the hit point will be the ray origin itself.
287    ///   Otherwise, the collider will be treated as hollow, and the hit point will be at its boundary.
288    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
289    /// - `callback`: A callback function called for each hit.
290    ///
291    /// # Example
292    ///
293    /// ```
294    /// # #[cfg(feature = "2d")]
295    /// # use avian2d::prelude::*;
296    /// # #[cfg(feature = "3d")]
297    /// use avian3d::prelude::*;
298    /// use bevy::prelude::*;
299    ///
300    /// # #[cfg(all(feature = "3d", feature = "f32"))]
301    /// fn print_hits(spatial_query: SpatialQuery) {
302    ///     // Ray origin and direction
303    ///     let origin = Vec3::ZERO;
304    ///     let direction = Dir3::X;
305    ///
306    ///     // Configuration for the ray cast
307    ///     let max_distance = 100.0;
308    ///     let solid = true;
309    ///     let filter = SpatialQueryFilter::default();
310    ///
311    ///     // Cast ray and get all hits
312    ///     let mut hits = vec![];
313    ///     spatial_query.ray_hits_callback(origin, direction, max_distance, 20, solid, &filter, |hit| {
314    ///         hits.push(hit);
315    ///         true
316    ///     });
317    ///
318    ///     // Print hits
319    ///     for hit in hits.iter() {
320    ///         println!("Hit: {:?}", hit);
321    ///     }
322    /// }
323    /// ```
324    ///
325    /// # Related Methods
326    ///
327    /// - [`SpatialQuery::cast_ray`]
328    /// - [`SpatialQuery::cast_ray_predicate`]
329    /// - [`SpatialQuery::ray_hits`]
330    pub fn ray_hits_callback(
331        &self,
332        origin: Vector,
333        direction: Dir,
334        max_distance: Scalar,
335        solid: bool,
336        filter: &SpatialQueryFilter,
337        callback: impl FnMut(RayHitData) -> bool,
338    ) {
339        self.query_pipeline.ray_hits_callback(
340            origin,
341            direction,
342            max_distance,
343            solid,
344            filter,
345            callback,
346        )
347    }
348
349    /// Casts a [shape](spatial_query#shapecasting) with a given rotation and computes the closest [hit](ShapeHitData)
350    /// with a collider. If there are no hits, `None` is returned.
351    ///
352    /// For a more ECS-based approach, consider using the [`ShapeCaster`] component instead.
353    ///
354    /// # Arguments
355    ///
356    /// - `shape`: The shape being cast represented as a [`Collider`].
357    /// - `origin`: Where the shape is cast from.
358    /// - `shape_rotation`: The rotation of the shape being cast.
359    /// - `direction`: What direction the shape is cast in.
360    /// - `config`: A [`ShapeCastConfig`] that determines the behavior of the cast.
361    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
362    ///
363    /// # Example
364    ///
365    /// ```
366    /// # #[cfg(feature = "2d")]
367    /// # use avian2d::prelude::*;
368    /// # #[cfg(feature = "3d")]
369    /// use avian3d::prelude::*;
370    /// use bevy::prelude::*;
371    ///
372    /// # #[cfg(all(feature = "3d", feature = "f32"))]
373    /// fn print_hits(spatial_query: SpatialQuery) {
374    ///     // Shape properties
375    ///     let shape = Collider::sphere(0.5);
376    ///     let origin = Vec3::ZERO;
377    ///     let rotation = Quat::default();
378    ///     let direction = Dir3::X;
379    ///
380    ///     // Configuration for the shape cast
381    ///     let config = ShapeCastConfig::from_max_distance(100.0);
382    ///     let filter = SpatialQueryFilter::default();
383    ///
384    ///     // Cast shape and print first hit
385    ///     if let Some(first_hit) = spatial_query.cast_shape(&shape, origin, rotation, direction, &config, &filter)
386    ///     {
387    ///         println!("First hit: {:?}", first_hit);
388    ///     }
389    /// }
390    /// ```
391    ///
392    /// # Related Methods
393    ///
394    /// - [`SpatialQuery::cast_shape_predicate`]
395    /// - [`SpatialQuery::shape_hits`]
396    /// - [`SpatialQuery::shape_hits_callback`]
397    #[allow(clippy::too_many_arguments)]
398    pub fn cast_shape(
399        &self,
400        shape: &Collider,
401        origin: Vector,
402        shape_rotation: RotationValue,
403        direction: Dir,
404        config: &ShapeCastConfig,
405        filter: &SpatialQueryFilter,
406    ) -> Option<ShapeHitData> {
407        self.query_pipeline
408            .cast_shape(shape, origin, shape_rotation, direction, config, filter)
409    }
410
411    /// Casts a [shape](spatial_query#shapecasting) with a given rotation and computes the closest [hit](ShapeHitData)
412    /// with a collider. If there are no hits, `None` is returned.
413    ///
414    /// For a more ECS-based approach, consider using the [`ShapeCaster`] component instead.
415    ///
416    /// # Arguments
417    ///
418    /// - `shape`: The shape being cast represented as a [`Collider`].
419    /// - `origin`: Where the shape is cast from.
420    /// - `shape_rotation`: The rotation of the shape being cast.
421    /// - `direction`: What direction the shape is cast in.
422    /// - `config`: A [`ShapeCastConfig`] that determines the behavior of the cast.
423    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
424    /// - `predicate`: A function called on each entity hit by the shape. The shape keeps travelling until the predicate returns `false`.
425    ///
426    /// # Example
427    ///
428    /// ```
429    /// # #[cfg(feature = "2d")]
430    /// # use avian2d::prelude::*;
431    /// # #[cfg(feature = "3d")]
432    /// use avian3d::prelude::*;
433    /// use bevy::prelude::*;
434    ///
435    /// #[derive(Component)]
436    /// struct Invisible;
437    ///
438    /// # #[cfg(all(feature = "3d", feature = "f32"))]
439    /// fn print_hits(spatial_query: SpatialQuery, query: Query<&Invisible>) {
440    ///     // Shape properties
441    ///     let shape = Collider::sphere(0.5);
442    ///     let origin = Vec3::ZERO;
443    ///     let rotation = Quat::default();
444    ///     let direction = Dir3::X;
445    ///
446    ///     // Configuration for the shape cast
447    ///     let config = ShapeCastConfig::from_max_distance(100.0);
448    ///     let filter = SpatialQueryFilter::default();
449    ///
450    ///     // Cast shape and get the first hit that matches the predicate
451    ///     let hit = spatial_query.cast_shape(&shape, origin, rotation, direction, &config, &filter, &|entity| {
452    ///        // Skip entities with the `Invisible` component.
453    ///        !query.contains(entity)
454    ///     });
455    ///
456    ///     // Print first hit
457    ///     if let Some(first_hit) = hit {
458    ///         println!("First hit: {:?}", first_hit);
459    ///     }
460    /// }
461    /// ```
462    ///
463    /// # Related Methods
464    ///
465    /// - [`SpatialQuery::cast_ray`]
466    /// - [`SpatialQuery::ray_hits`]
467    /// - [`SpatialQuery::ray_hits_callback`]
468    pub fn cast_shape_predicate(
469        &self,
470        shape: &Collider,
471        origin: Vector,
472        shape_rotation: RotationValue,
473        direction: Dir,
474        config: &ShapeCastConfig,
475        filter: &SpatialQueryFilter,
476        predicate: &dyn Fn(Entity) -> bool,
477    ) -> Option<ShapeHitData> {
478        self.query_pipeline.cast_shape_predicate(
479            shape,
480            origin,
481            shape_rotation,
482            direction,
483            config,
484            filter,
485            predicate,
486        )
487    }
488
489    /// Casts a [shape](spatial_query#shapecasting) with a given rotation and computes computes all [hits](ShapeHitData)
490    /// in the order of distance until `max_hits` is reached.
491    ///
492    /// # Arguments
493    ///
494    /// - `shape`: The shape being cast represented as a [`Collider`].
495    /// - `origin`: Where the shape is cast from.
496    /// - `shape_rotation`: The rotation of the shape being cast.
497    /// - `direction`: What direction the shape is cast in.
498    /// - `max_hits`: The maximum number of hits. Additional hits will be missed.
499    /// - `config`: A [`ShapeCastConfig`] that determines the behavior of the cast.
500    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
501    /// - `callback`: A callback function called for each hit.
502    ///
503    /// # Example
504    ///
505    /// ```
506    /// # #[cfg(feature = "2d")]
507    /// # use avian2d::prelude::*;
508    /// # #[cfg(feature = "3d")]
509    /// use avian3d::prelude::*;
510    /// use bevy::prelude::*;
511    ///
512    /// # #[cfg(all(feature = "3d", feature = "f32"))]
513    /// fn print_hits(spatial_query: SpatialQuery) {
514    ///     // Shape properties
515    ///     let shape = Collider::sphere(0.5);
516    ///     let origin = Vec3::ZERO;
517    ///     let rotation = Quat::default();
518    ///     let direction = Dir3::X;
519    ///
520    ///     // Configuration for the shape cast
521    ///     let config = ShapeCastConfig::from_max_distance(100.0);
522    ///     let filter = SpatialQueryFilter::default();
523    ///
524    ///     // Cast shape and get up to 20 hits
525    ///     let hits = spatial_query.cast_shape(&shape, origin, rotation, direction, 20, &config, &filter);
526    ///
527    ///     // Print hits
528    ///     for hit in hits.iter() {
529    ///         println!("Hit: {:?}", hit);
530    ///     }
531    /// }
532    /// ```
533    ///
534    /// # Related Methods
535    ///
536    /// - [`SpatialQuery::cast_shape`]
537    /// - [`SpatialQuery::cast_shape_predicate`]
538    /// - [`SpatialQuery::shape_hits_callback`]
539    #[allow(clippy::too_many_arguments)]
540    pub fn shape_hits(
541        &self,
542        shape: &Collider,
543        origin: Vector,
544        shape_rotation: RotationValue,
545        direction: Dir,
546        max_hits: u32,
547        config: &ShapeCastConfig,
548        filter: &SpatialQueryFilter,
549    ) -> Vec<ShapeHitData> {
550        self.query_pipeline.shape_hits(
551            shape,
552            origin,
553            shape_rotation,
554            direction,
555            max_hits,
556            config,
557            filter,
558        )
559    }
560
561    /// Casts a [shape](spatial_query#shapecasting) with a given rotation and computes computes all [hits](ShapeHitData)
562    /// in the order of distance, calling the given `callback` for each hit. The shapecast stops when
563    /// `callback` returns false or all hits have been found.
564    ///
565    /// # Arguments
566    ///
567    /// - `shape`: The shape being cast represented as a [`Collider`].
568    /// - `origin`: Where the shape is cast from.
569    /// - `shape_rotation`: The rotation of the shape being cast.
570    /// - `direction`: What direction the shape is cast in.
571    /// - `config`: A [`ShapeCastConfig`] that determines the behavior of the cast.
572    /// - `filter`: A [`SpatialQueryFilter`] that determines which entities are included in the cast.
573    /// - `callback`: A callback function called for each hit.
574    ///
575    /// # Example
576    ///
577    /// ```
578    /// # #[cfg(feature = "2d")]
579    /// # use avian2d::prelude::*;
580    /// # #[cfg(feature = "3d")]
581    /// use avian3d::prelude::*;
582    /// use bevy::prelude::*;
583    ///
584    /// # #[cfg(all(feature = "3d", feature = "f32"))]
585    /// fn print_hits(spatial_query: SpatialQuery) {
586    ///     // Shape properties
587    ///     let shape = Collider::sphere(0.5);
588    ///     let origin = Vec3::ZERO;
589    ///     let rotation = Quat::default();
590    ///     let direction = Dir3::X;
591    ///
592    ///     // Configuration for the shape cast
593    ///     let config = ShapeCastConfig::from_max_distance(100.0);
594    ///     let filter = SpatialQueryFilter::default();
595    ///
596    ///     // Cast shape and get up to 20 hits
597    ///     let mut hits = vec![];
598    ///     spatial_query.shape_hits_callback(&shape, origin, rotation, direction, 20, &config, &filter, |hit| {
599    ///         hits.push(hit);
600    ///         true
601    ///     });
602    ///
603    ///     // Print hits
604    ///     for hit in hits.iter() {
605    ///         println!("Hit: {:?}", hit);
606    ///     }
607    /// }
608    /// ```
609    ///
610    /// # Related Methods
611    ///
612    /// - [`SpatialQuery::cast_shape`]
613    /// - [`SpatialQuery::cast_shape_predicate`]
614    /// - [`SpatialQuery::shape_hits`]
615    #[allow(clippy::too_many_arguments)]
616    pub fn shape_hits_callback(
617        &self,
618        shape: &Collider,
619        origin: Vector,
620        shape_rotation: RotationValue,
621        direction: Dir,
622        config: &ShapeCastConfig,
623        filter: &SpatialQueryFilter,
624        callback: impl FnMut(ShapeHitData) -> bool,
625    ) {
626        self.query_pipeline.shape_hits_callback(
627            shape,
628            origin,
629            shape_rotation,
630            direction,
631            config,
632            filter,
633            callback,
634        )
635    }
636
637    /// Finds the [projection](spatial_query#point-projection) of a given point on the closest [collider](Collider).
638    /// If one isn't found, `None` is returned.
639    ///
640    /// # Arguments
641    ///
642    /// - `point`: The point that should be projected.
643    /// - `solid`: If true and the point is inside of a collider, the projection will be at the point.
644    ///   Otherwise, the collider will be treated as hollow, and the projection will be at the collider's boundary.
645    /// - `query_filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
646    ///
647    /// # Example
648    ///
649    /// ```
650    /// # #[cfg(feature = "2d")]
651    /// # use avian2d::prelude::*;
652    /// # #[cfg(feature = "3d")]
653    /// use avian3d::prelude::*;
654    /// use bevy::prelude::*;
655    ///
656    /// # #[cfg(all(feature = "3d", feature = "f32"))]
657    /// fn print_point_projection(spatial_query: SpatialQuery) {
658    ///     // Project a point and print the result
659    ///     if let Some(projection) = spatial_query.project_point(
660    ///         Vec3::ZERO,                    // Point
661    ///         true,                          // Are colliders treated as "solid"
662    ///         &SpatialQueryFilter::default(),// Query filter
663    ///     ) {
664    ///         println!("Projection: {:?}", projection);
665    ///     }
666    /// }
667    /// ```
668    ///
669    /// # Related Methods
670    ///
671    /// - [`SpatialQuery::project_point_predicate`]
672    pub fn project_point(
673        &self,
674        point: Vector,
675        solid: bool,
676        filter: &SpatialQueryFilter,
677    ) -> Option<PointProjection> {
678        self.query_pipeline.project_point(point, solid, filter)
679    }
680
681    /// Finds the [projection](spatial_query#point-projection) of a given point on the closest [collider](Collider).
682    /// If one isn't found, `None` is returned.
683    ///
684    /// # Arguments
685    ///
686    /// - `point`: The point that should be projected.
687    /// - `solid`: If true and the point is inside of a collider, the projection will be at the point.
688    ///   Otherwise, the collider will be treated as hollow, and the projection will be at the collider's boundary.
689    /// - `filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
690    /// - `predicate`: A function for filtering which entities are considered in the query. The projection will be on the closest collider that passes the predicate.
691    ///
692    /// # Example
693    ///
694    /// ```
695    /// # #[cfg(feature = "2d")]
696    /// # use avian2d::prelude::*;
697    /// # #[cfg(feature = "3d")]
698    /// use avian3d::prelude::*;
699    /// use bevy::prelude::*;
700    ///
701    /// #[derive(Component)]
702    /// struct Invisible;
703    ///
704    /// # #[cfg(all(feature = "3d", feature = "f32"))]
705    /// fn print_point_projection(spatial_query: SpatialQuery, query: Query<&Invisible>) {
706    ///     // Project a point and print the result
707    ///     if let Some(projection) = spatial_query.project_point_predicate(
708    ///         Vec3::ZERO,                    // Point
709    ///         true,                          // Are colliders treated as "solid"
710    ///         SpatialQueryFilter::default(), // Query filter
711    ///         &|entity| {                    // Predicate
712    ///             // Skip entities with the `Invisible` component.
713    ///             !query.contains(entity)
714    ///         }
715    ///     ) {
716    ///         println!("Projection: {:?}", projection);
717    ///     }
718    /// }
719    /// ```
720    ///
721    /// # Related Methods
722    ///
723    /// - [`SpatialQuery::project_point`]
724    pub fn project_point_predicate(
725        &self,
726        point: Vector,
727        solid: bool,
728        filter: &SpatialQueryFilter,
729        predicate: &dyn Fn(Entity) -> bool,
730    ) -> Option<PointProjection> {
731        self.query_pipeline
732            .project_point_predicate(point, solid, filter, predicate)
733    }
734
735    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [collider](Collider)
736    /// that contains the given point.
737    ///
738    /// # Arguments
739    ///
740    /// - `point`: The point that intersections are tested against.
741    /// - `filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
742    ///
743    /// # Example
744    ///
745    /// ```
746    /// # #[cfg(feature = "2d")]
747    /// # use avian2d::prelude::*;
748    /// # #[cfg(feature = "3d")]
749    /// use avian3d::prelude::*;
750    /// use bevy::prelude::*;
751    ///
752    /// # #[cfg(all(feature = "3d", feature = "f32"))]
753    /// fn print_point_intersections(spatial_query: SpatialQuery) {
754    ///     let intersections =
755    ///         spatial_query.point_intersections(Vec3::ZERO, &SpatialQueryFilter::default());
756    ///
757    ///     for entity in intersections.iter() {
758    ///         println!("Entity: {}", entity);
759    ///     }
760    /// }
761    /// ```
762    ///
763    /// # Related Methods
764    ///
765    /// - [`SpatialQuery::point_intersections_callback`]
766    pub fn point_intersections(&self, point: Vector, filter: &SpatialQueryFilter) -> Vec<Entity> {
767        self.query_pipeline.point_intersections(point, filter)
768    }
769
770    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [collider](Collider)
771    /// that contains the given point, calling the given `callback` for each intersection.
772    /// The search stops when `callback` returns `false` or all intersections have been found.
773    ///
774    /// # Arguments
775    ///
776    /// - `point`: The point that intersections are tested against.
777    /// - `filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
778    /// - `callback`: A callback function called for each intersection.
779    ///
780    /// # Example
781    ///
782    /// ```
783    /// # #[cfg(feature = "2d")]
784    /// # use avian2d::prelude::*;
785    /// # #[cfg(feature = "3d")]
786    /// use avian3d::prelude::*;
787    /// use bevy::prelude::*;
788    ///
789    /// # #[cfg(all(feature = "3d", feature = "f32"))]
790    /// fn print_point_intersections(spatial_query: SpatialQuery) {
791    ///     let mut intersections = vec![];
792    ///     
793    ///     spatial_query.point_intersections_callback(
794    ///         Vec3::ZERO,                     // Point
795    ///         &SpatialQueryFilter::default(), // Query filter
796    ///         |entity| {                      // Callback function
797    ///             intersections.push(entity);
798    ///             true
799    ///         },
800    ///     );
801    ///
802    ///     for entity in intersections.iter() {
803    ///         println!("Entity: {}", entity);
804    ///     }
805    /// }
806    /// ```
807    ///
808    /// # Related Methods
809    ///
810    /// - [`SpatialQuery::point_intersections`]
811    pub fn point_intersections_callback(
812        &self,
813        point: Vector,
814        filter: &SpatialQueryFilter,
815        callback: impl FnMut(Entity) -> bool,
816    ) {
817        self.query_pipeline
818            .point_intersections_callback(point, filter, callback)
819    }
820
821    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [`ColliderAabb`]
822    /// that is intersecting the given `aabb`.
823    ///
824    /// # Example
825    ///
826    /// ```
827    /// # #[cfg(feature = "2d")]
828    /// # use avian2d::prelude::*;
829    /// # #[cfg(feature = "3d")]
830    /// use avian3d::prelude::*;
831    /// use bevy::prelude::*;
832    ///
833    /// # #[cfg(all(feature = "3d", feature = "f32"))]
834    /// fn print_aabb_intersections(spatial_query: SpatialQuery) {
835    ///     let aabb = Collider::sphere(0.5).aabb(Vec3::ZERO, Quat::default());
836    ///     let intersections = spatial_query.aabb_intersections_with_aabb(aabb);
837    ///
838    ///     for entity in intersections.iter() {
839    ///         println!("Entity: {}", entity);
840    ///     }
841    /// }
842    /// ```
843    ///
844    /// # Related Methods
845    ///
846    /// - [`SpatialQuery::aabb_intersections_with_aabb_callback`]
847    pub fn aabb_intersections_with_aabb(&self, aabb: ColliderAabb) -> Vec<Entity> {
848        self.query_pipeline.aabb_intersections_with_aabb(aabb)
849    }
850
851    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [`ColliderAabb`]
852    /// that is intersecting the given `aabb`, calling `callback` for each intersection.
853    /// The search stops when `callback` returns `false` or all intersections have been found.
854    ///
855    /// # Example
856    ///
857    /// ```
858    /// # #[cfg(feature = "2d")]
859    /// # use avian2d::prelude::*;
860    /// # #[cfg(feature = "3d")]
861    /// use avian3d::prelude::*;
862    /// use bevy::prelude::*;
863    ///
864    /// # #[cfg(all(feature = "3d", feature = "f32"))]
865    /// fn print_aabb_intersections(spatial_query: SpatialQuery) {
866    ///     let mut intersections = vec![];
867    ///
868    ///     spatial_query.aabb_intersections_with_aabb_callback(
869    ///         Collider::sphere(0.5).aabb(Vec3::ZERO, Quat::default()),
870    ///         |entity| {
871    ///             intersections.push(entity);
872    ///             true
873    ///         }
874    ///     );
875    ///
876    ///     for entity in intersections.iter() {
877    ///         println!("Entity: {}", entity);
878    ///     }
879    /// }
880    /// ```
881    ///
882    /// # Related Methods
883    ///
884    /// - [`SpatialQuery::aabb_intersections_with_aabb`]
885    pub fn aabb_intersections_with_aabb_callback(
886        &self,
887        aabb: ColliderAabb,
888        callback: impl FnMut(Entity) -> bool,
889    ) {
890        self.query_pipeline
891            .aabb_intersections_with_aabb_callback(aabb, callback)
892    }
893
894    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [`Collider`]
895    /// that is intersecting the given `shape` with a given position and rotation.
896    ///
897    /// # Arguments
898    ///
899    /// - `shape`: The shape that intersections are tested against represented as a [`Collider`].
900    /// - `shape_position`: The position of the shape.
901    /// - `shape_rotation`: The rotation of the shape.
902    /// - `filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
903    ///
904    /// # Example
905    ///
906    /// ```
907    /// # #[cfg(feature = "2d")]
908    /// # use avian2d::prelude::*;
909    /// # #[cfg(feature = "3d")]
910    /// use avian3d::prelude::*;
911    /// use bevy::prelude::*;
912    ///
913    /// # #[cfg(all(feature = "3d", feature = "f32"))]
914    /// fn print_shape_intersections(spatial_query: SpatialQuery) {
915    ///     let intersections = spatial_query.shape_intersections(
916    ///         &Collider::sphere(0.5),          // Shape
917    ///         Vec3::ZERO,                      // Shape position
918    ///         Quat::default(),                 // Shape rotation
919    ///         &SpatialQueryFilter::default(),  // Query filter
920    ///     );
921    ///
922    ///     for entity in intersections.iter() {
923    ///         println!("Entity: {}", entity);
924    ///     }
925    /// }
926    /// ```
927    ///
928    /// # Related Methods
929    ///
930    /// - [`SpatialQuery::shape_intersections_callback`]
931    pub fn shape_intersections(
932        &self,
933        shape: &Collider,
934        shape_position: Vector,
935        shape_rotation: RotationValue,
936        filter: &SpatialQueryFilter,
937    ) -> Vec<Entity> {
938        self.query_pipeline
939            .shape_intersections(shape, shape_position, shape_rotation, filter)
940    }
941
942    /// An [intersection test](spatial_query#intersection-tests) that finds all entities with a [`Collider`]
943    /// that is intersecting the given `shape` with a given position and rotation, calling `callback` for each
944    /// intersection. The search stops when `callback` returns `false` or all intersections have been found.
945    ///
946    /// # Arguments
947    ///
948    /// - `shape`: The shape that intersections are tested against represented as a [`Collider`].
949    /// - `shape_position`: The position of the shape.
950    /// - `shape_rotation`: The rotation of the shape.
951    /// - `filter`: A [`SpatialQueryFilter`] that determines which colliders are taken into account in the query.
952    /// - `callback`: A callback function called for each intersection.
953    ///
954    /// # Example
955    ///
956    /// ```
957    /// # #[cfg(feature = "2d")]
958    /// # use avian2d::prelude::*;
959    /// # #[cfg(feature = "3d")]
960    /// use avian3d::prelude::*;
961    /// use bevy::prelude::*;
962    ///
963    /// # #[cfg(all(feature = "3d", feature = "f32"))]
964    /// fn print_shape_intersections(spatial_query: SpatialQuery) {
965    ///     let mut intersections = vec![];
966    ///
967    ///     spatial_query.shape_intersections_callback(
968    ///         &Collider::sphere(0.5),          // Shape
969    ///         Vec3::ZERO,                      // Shape position
970    ///         Quat::default(),                 // Shape rotation
971    ///         &SpatialQueryFilter::default(),  // Query filter
972    ///         |entity| {                       // Callback function
973    ///             intersections.push(entity);
974    ///             true
975    ///         },
976    ///     );
977    ///
978    ///     for entity in intersections.iter() {
979    ///         println!("Entity: {}", entity);
980    ///     }
981    /// }
982    /// ```
983    ///
984    /// # Related Methods
985    ///
986    /// - [`SpatialQuery::shape_intersections`]
987    pub fn shape_intersections_callback(
988        &self,
989        shape: &Collider,
990        shape_position: Vector,
991        shape_rotation: RotationValue,
992        filter: &SpatialQueryFilter,
993        callback: impl FnMut(Entity) -> bool,
994    ) {
995        self.query_pipeline.shape_intersections_callback(
996            shape,
997            shape_position,
998            shape_rotation,
999            filter,
1000            callback,
1001        )
1002    }
1003}