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}