Skip to main content

bevy_rapier3d/plugin/context/systemparams/
rapier_context_systemparam.rs

1use crate::math::{Rot, Vect};
2use bevy::ecs::{query, system::SystemParam};
3use bevy::prelude::*;
4use rapier::prelude::Real;
5
6pub(crate) const RAPIER_CONTEXT_EXPECT_ERROR: &str =
7    "RapierContextEntityLink.0 refers to an entity missing components from RapierContextSimulation.";
8
9use crate::plugin::context::{
10    DefaultRapierContext, RapierContextColliders, RapierContextJoints, RapierContextSimulation,
11    RapierQueryPipeline, RapierRigidBodySet,
12};
13
14/// Utility [`SystemParam`] to easily access every required components of a [`RapierContext`] immutably.
15///
16/// This uses the [`DefaultRapierContext`] filter by default, but you can use a custom query filter with the `T` type parameter.
17#[derive(SystemParam)]
18pub struct ReadRapierContext<'w, 's, T: query::QueryFilter + 'static = With<DefaultRapierContext>> {
19    /// The query used to feed components into [`RapierContext`] struct through [`ReadRapierContext::single`].
20    pub rapier_context: Query<
21        'w,
22        's,
23        (
24            &'static RapierContextSimulation,
25            &'static RapierContextColliders,
26            &'static RapierContextJoints,
27            &'static RapierRigidBodySet,
28        ),
29        T,
30    >,
31}
32
33impl<'w, 's, T: query::QueryFilter + 'static> ReadRapierContext<'w, 's, T> {
34    /// Returns a single [`RapierContext`] corresponding to the filter (T) of [`ReadRapierContext`].
35    ///
36    /// If the number of query items is not exactly one, a [`bevy::ecs::query::QuerySingleError`] is returned instead.
37    ///
38    /// You can also use the underlying query [`ReadRapierContext::rapier_context`] for finer grained queries.
39    pub fn single(&self) -> Result<RapierContext<'_>> {
40        let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single()?;
41        Ok(RapierContext {
42            simulation,
43            colliders,
44            joints,
45            rigidbody_set,
46        })
47    }
48}
49
50/// A helper struct to avoid passing too many parameters to most rapier functions.
51/// This helps with reducing boilerplate, at the (small) price of maybe getting too much information from the ECS.
52///
53/// Note: This is not a component, refer to [`ReadRapierContext`], [`WriteRapierContext`], or [`RapierContextSimulation`]
54#[cfg_attr(feature = "serde-serialize", derive(Serialize))]
55#[derive(query::QueryData)]
56pub struct RapierContext<'a> {
57    /// The Rapier context, containing all the state of the physics engine.
58    pub simulation: &'a RapierContextSimulation,
59    /// The set of colliders part of the simulation.
60    pub colliders: &'a RapierContextColliders,
61    /// The sets of joints part of the simulation.
62    pub joints: &'a RapierContextJoints,
63    /// The set of rigid-bodies part of the simulation.
64    pub rigidbody_set: &'a RapierRigidBodySet,
65}
66
67/// Utility [`SystemParam`] to easily access every required components of a [`RapierContext`] mutably.
68///
69/// This uses the [`DefaultRapierContext`] filter by default, but you can use a custom query filter with the `T` type parameter.
70#[derive(SystemParam)]
71pub struct WriteRapierContext<'w, 's, T: query::QueryFilter + 'static = With<DefaultRapierContext>>
72{
73    /// The query used to feed components into [`RapierContext`] struct through [`ReadRapierContext::single`].
74    pub rapier_context: Query<
75        'w,
76        's,
77        (
78            &'static mut RapierContextSimulation,
79            &'static mut RapierContextColliders,
80            &'static mut RapierContextJoints,
81            &'static mut RapierRigidBodySet,
82        ),
83        T,
84    >,
85}
86
87impl<'w, 's, T: query::QueryFilter + 'static> WriteRapierContext<'w, 's, T> {
88    /// Returns a single [`RapierContext`] corresponding to the filter (T) of [`WriteRapierContext`].
89    ///
90    /// If the number of query items is not exactly one, a [`bevy::ecs::query::QuerySingleError`] is returned instead.
91    ///
92    /// You can also use the underlying query [`WriteRapierContext::rapier_context`] for finer grained queries.
93    pub fn single(&self) -> Result<RapierContext<'_>> {
94        let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single()?;
95        Ok(RapierContext {
96            simulation,
97            colliders,
98            joints,
99            rigidbody_set,
100        })
101    }
102
103    /// Returns a single mutable [`RapierContextMut`] corresponding to the filter (T) of [`WriteRapierContext`].
104    ///
105    /// If the number of query items is not exactly one, a [`bevy::ecs::query::QuerySingleError`] is returned instead.
106    ///
107    /// You can also use the underlying query [`WriteRapierContext::rapier_context`] for finer grained queries.
108    pub fn single_mut(&mut self) -> Result<RapierContextMut<'_>> {
109        let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single_mut()?;
110        Ok(RapierContextMut {
111            simulation,
112            colliders,
113            joints,
114            rigidbody_set,
115        })
116    }
117}
118
119/// A helper struct to avoid passing too many parameters to most rapier functions.
120/// This helps with reducing boilerplate, at the (small) price of maybe getting too much information from the ECS.
121///
122/// If you need more granular control over mutability of each component, use a regular [`Query`]
123pub struct RapierContextMut<'a> {
124    /// The Rapier context, containing all the state of the physics engine.
125    pub simulation: Mut<'a, RapierContextSimulation>,
126    /// The set of colliders part of the simulation.
127    pub colliders: Mut<'a, RapierContextColliders>,
128    /// The sets of joints part of the simulation.
129    pub joints: Mut<'a, RapierContextJoints>,
130    /// The set of rigid-bodies part of the simulation.
131    pub rigidbody_set: Mut<'a, RapierRigidBodySet>,
132}
133
134/// [`RapierRigidBodySet`] functions
135mod simulation {
136    use crate::control::CharacterCollision;
137    use crate::control::MoveShapeOptions;
138    use crate::control::MoveShapeOutput;
139    use crate::geometry::AsShape;
140    use crate::plugin::context::SimulationToRenderTime;
141    use crate::plugin::ContactPairView;
142    use crate::plugin::{SimulationMode, TimestepMode};
143    use crate::prelude::CollisionEvent;
144    use crate::prelude::ContactForceEvent;
145    use crate::prelude::QueryFilter;
146    use crate::prelude::RapierRigidBodyHandle;
147    use crate::prelude::TransformInterpolation;
148    use rapier::prelude::PhysicsHooks;
149
150    use super::*;
151
152    /// [`RapierContextSimulation`] functions for immutable accesses
153    impl RapierContext<'_> {
154        /// Shortcut to [`RapierContextSimulation::contact_pair`].
155        pub fn contact_pair(
156            &self,
157            collider1: Entity,
158            collider2: Entity,
159        ) -> Option<ContactPairView<'_>> {
160            self.simulation
161                .contact_pair(self.colliders, self.rigidbody_set, collider1, collider2)
162        }
163
164        /// Shortcut to [`RapierContextSimulation::contact_pairs_with`].
165        pub fn contact_pairs_with(
166            &self,
167            collider: Entity,
168        ) -> impl Iterator<Item = ContactPairView<'_>> {
169            self.simulation
170                .contact_pairs_with(self.colliders, self.rigidbody_set, collider)
171        }
172
173        /// Shortcut to [`RapierContextSimulation::intersection_pair`].
174        pub fn intersection_pair(&self, collider1: Entity, collider2: Entity) -> Option<bool> {
175            self.simulation
176                .intersection_pair(self.colliders, collider1, collider2)
177        }
178
179        /// Shortcut to [`RapierContextSimulation::intersection_pairs_with`].
180        pub fn intersection_pairs_with(
181            &self,
182            collider: Entity,
183        ) -> impl Iterator<Item = (Entity, Entity, bool)> + '_ {
184            self.simulation
185                .intersection_pairs_with(self.colliders, collider)
186        }
187    }
188
189    /// [`RapierContextSimulation`] functions for mutable accesses
190    impl RapierContextMut<'_> {
191        /// Shortcut to [`RapierContextSimulation::step_simulation`].
192        #[expect(clippy::too_many_arguments)]
193        pub fn step_simulation(
194            &mut self,
195            gravity: Vect,
196            simulation_mode: SimulationMode,
197            timestep_mode: TimestepMode,
198            events: Option<(
199                &MessageWriter<CollisionEvent>,
200                &MessageWriter<ContactForceEvent>,
201            )>,
202            hooks: &dyn PhysicsHooks,
203            time: &Time,
204            sim_to_render_time: &mut SimulationToRenderTime,
205            interpolation_query: Option<
206                &mut Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
207            >,
208        ) {
209            self.simulation.step_simulation(
210                &mut self.colliders,
211                &mut self.joints,
212                &mut self.rigidbody_set,
213                gravity,
214                simulation_mode,
215                timestep_mode,
216                events,
217                hooks,
218                time,
219                sim_to_render_time,
220                interpolation_query,
221            )
222        }
223
224        /// Shortcut to [`RapierContextSimulation::move_shape`].
225        ///
226        /// The scene queries are filtered with `filter`, like the other scene queries of this
227        /// context.
228        #[expect(clippy::too_many_arguments)]
229        pub fn move_shape(
230            &mut self,
231            movement: Vect,
232            shape: &(impl AsShape + ?Sized),
233            shape_translation: Vect,
234            shape_rotation: Rot,
235            shape_mass: Real,
236            options: &MoveShapeOptions,
237            filter: QueryFilter,
238            events: impl FnMut(CharacterCollision),
239        ) -> MoveShapeOutput {
240            self.simulation.move_shape(
241                &mut self.colliders,
242                &mut self.rigidbody_set,
243                movement,
244                shape,
245                shape_translation,
246                shape_rotation,
247                shape_mass,
248                options,
249                filter,
250                events,
251            )
252        }
253
254        /// Shortcut to [`RapierContextSimulation::contact_pair`].
255        pub fn contact_pair(
256            &self,
257            collider1: Entity,
258            collider2: Entity,
259        ) -> Option<ContactPairView<'_>> {
260            self.simulation
261                .contact_pair(&self.colliders, &self.rigidbody_set, collider1, collider2)
262        }
263
264        /// Shortcut to [`RapierContextSimulation::contact_pairs_with`].
265        pub fn contact_pairs_with(
266            &self,
267            collider: Entity,
268        ) -> impl Iterator<Item = ContactPairView<'_>> {
269            self.simulation
270                .contact_pairs_with(&self.colliders, &self.rigidbody_set, collider)
271        }
272
273        /// Shortcut to [`RapierContextSimulation::intersection_pair`].
274        pub fn intersection_pair(&self, collider1: Entity, collider2: Entity) -> Option<bool> {
275            self.simulation
276                .intersection_pair(&self.colliders, collider1, collider2)
277        }
278
279        /// Shortcut to [`RapierContextSimulation::intersection_pairs_with`].
280        pub fn intersection_pairs_with(
281            &self,
282            collider: Entity,
283        ) -> impl Iterator<Item = (Entity, Entity, bool)> + '_ {
284            self.simulation
285                .intersection_pairs_with(&self.colliders, collider)
286        }
287    }
288}
289
290mod query_pipeline {
291    use rapier::{
292        geometry::Collider as RapierCollider,
293        parry::{partitioning::Bvh, query::ShapeCastOptions},
294        prelude::FeatureId,
295    };
296
297    use crate::geometry::AsShape;
298
299    use crate::prelude::{
300        NonlinearMotion, PointProjection, QueryFilter, RayIntersection, ShapeCastHit,
301        ShapeClosestPoints, ShapeContact,
302    };
303
304    use super::*;
305
306    impl RapierContext<'_> {
307        /// Shortcut to [RapierQueryPipeline::new_scoped].
308        pub fn with_query_pipeline<'a, T>(
309            &'a self,
310            filter: QueryFilter<'a>,
311            scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
312        ) -> T {
313            crate::prelude::RapierQueryPipeline::new_scoped(
314                &self.simulation.broad_phase,
315                self.colliders,
316                self.rigidbody_set,
317                &filter,
318                self.simulation.query_dispatcher(),
319                scoped_fn,
320            )
321        }
322        /// Shortcut to [`RapierQueryPipeline::cast_ray`].
323        pub fn cast_ray(
324            &self,
325            ray_origin: Vect,
326            ray_dir: Vect,
327            max_toi: Real,
328            solid: bool,
329            filter: QueryFilter,
330        ) -> Option<(Entity, Real)> {
331            self.with_query_pipeline(filter, |query_pipeline| {
332                query_pipeline.cast_ray(ray_origin, ray_dir, max_toi, solid)
333            })
334        }
335
336        /// Shortcut to [`RapierQueryPipeline::cast_ray_and_get_normal`].
337        pub fn cast_ray_and_get_normal(
338            &self,
339            ray_origin: Vect,
340            ray_dir: Vect,
341            max_toi: Real,
342            solid: bool,
343            filter: QueryFilter,
344        ) -> Option<(Entity, RayIntersection)> {
345            self.with_query_pipeline(filter, |query_pipeline| {
346                query_pipeline.cast_ray_and_get_normal(ray_origin, ray_dir, max_toi, solid)
347            })
348        }
349
350        /// Shortcut to [`RapierQueryPipeline::intersect_point`].
351        ///
352        /// Stops the query if `callback` returns false.
353        pub fn intersect_point(
354            &self,
355            point: Vect,
356            filter: QueryFilter,
357            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
358        ) {
359            self.with_query_pipeline(filter, |query_pipeline| {
360                for (e, co) in query_pipeline.intersect_point(point) {
361                    if !callback(e, co) {
362                        break;
363                    }
364                }
365            });
366        }
367
368        /// Shortcut to [`RapierQueryPipeline::intersect_ray`].
369        ///
370        /// Stops the query if `callback` returns false.
371        pub fn intersect_ray(
372            &self,
373            ray_origin: Vect,
374            ray_dir: Vect,
375            max_toi: Real,
376            solid: bool,
377            filter: QueryFilter,
378            mut callback: impl FnMut(Entity, &RapierCollider, RayIntersection) -> bool,
379        ) {
380            self.with_query_pipeline(filter, |query_pipeline| {
381                for (e, co, intersection) in
382                    query_pipeline.intersect_ray(ray_origin, ray_dir, max_toi, solid)
383                {
384                    if !callback(e, co, intersection) {
385                        break;
386                    }
387                }
388            });
389        }
390
391        /// Shortcut to [`RapierQueryPipeline::intersect_shape`].
392        pub fn intersect_shape(
393            &self,
394            shape_pos: Vect,
395            shape_rot: Rot,
396            shape: &(impl AsShape + ?Sized),
397            filter: QueryFilter,
398            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
399        ) {
400            self.with_query_pipeline(filter, |query_pipeline| {
401                for (e, co) in query_pipeline.intersect_shape(shape_pos, shape_rot, shape) {
402                    if !callback(e, co) {
403                        break;
404                    }
405                }
406            });
407        }
408
409        /// Shortcut to [`RapierQueryPipeline::intersect_aabb_conservative`].
410        pub fn intersect_aabb_conservative(
411            &self,
412            #[cfg(feature = "dim2")] aabb: bevy::shape::Aabb2d,
413            #[cfg(feature = "dim3")] aabb: bevy::shape::Aabb3d,
414            filter: QueryFilter,
415            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
416        ) {
417            self.with_query_pipeline(filter, |query_pipeline| {
418                for (e, co) in query_pipeline.intersect_aabb_conservative(aabb) {
419                    if !callback(e, co) {
420                        break;
421                    }
422                }
423            });
424        }
425
426        /// Shortcut to [`RapierQueryPipeline::cast_shape`].
427        pub fn cast_shape(
428            &self,
429            shape_pos: Vect,
430            shape_rot: Rot,
431            shape_vel: Vect,
432            shape: &(impl AsShape + ?Sized),
433            options: ShapeCastOptions,
434            filter: QueryFilter,
435        ) -> Option<(Entity, ShapeCastHit)> {
436            self.with_query_pipeline(filter, |query_pipeline| {
437                query_pipeline.cast_shape(shape_pos, shape_rot, shape_vel, shape, options)
438            })
439        }
440
441        /// Shortcut to [`RapierQueryPipeline::project_point`].
442        pub fn project_point(
443            &self,
444            point: Vect,
445            max_dist: f32,
446            solid: bool,
447            filter: QueryFilter,
448        ) -> Option<(Entity, PointProjection)> {
449            self.with_query_pipeline(filter, |query_pipeline| {
450                query_pipeline.project_point(point, max_dist, solid)
451            })
452        }
453    }
454
455    impl RapierContext<'_> {
456        /// Shortcut to [`RapierQueryPipeline::project_point_and_get_feature`].
457        pub fn project_point_and_get_feature(
458            &self,
459            point: Vect,
460            max_dist: Real,
461            filter: QueryFilter,
462        ) -> Option<(Entity, PointProjection, FeatureId)> {
463            self.with_query_pipeline(filter, |query_pipeline| {
464                query_pipeline.project_point_and_get_feature(point, max_dist)
465            })
466        }
467
468        /// Shortcut to [`RapierQueryPipeline::cast_shape_nonlinear`].
469        pub fn cast_shape_nonlinear(
470            &self,
471            shape_motion: &NonlinearMotion,
472            shape: &(impl AsShape + ?Sized),
473            start_time: Real,
474            end_time: Real,
475            stop_at_penetration: bool,
476            filter: QueryFilter,
477        ) -> Option<(Entity, ShapeCastHit)> {
478            self.with_query_pipeline(filter, |query_pipeline| {
479                query_pipeline.cast_shape_nonlinear(
480                    shape_motion,
481                    shape,
482                    start_time,
483                    end_time,
484                    stop_at_penetration,
485                )
486            })
487        }
488
489        /// Shortcut to [`RapierQueryPipeline::distance_to_shape`].
490        pub fn distance_to_shape(
491            &self,
492            shape_pos: Vect,
493            shape_rot: Rot,
494            shape: &(impl AsShape + ?Sized),
495            filter: QueryFilter,
496        ) -> Option<(Entity, Real)> {
497            self.with_query_pipeline(filter, |query_pipeline| {
498                query_pipeline.distance_to_shape(shape_pos, shape_rot, shape)
499            })
500        }
501
502        /// Shortcut to [`RapierQueryPipeline::closest_points_to_shape`].
503        pub fn closest_points_to_shape(
504            &self,
505            shape_pos: Vect,
506            shape_rot: Rot,
507            shape: &(impl AsShape + ?Sized),
508            max_dist: Real,
509            filter: QueryFilter,
510        ) -> Option<(Entity, ShapeClosestPoints)> {
511            self.with_query_pipeline(filter, |query_pipeline| {
512                query_pipeline.closest_points_to_shape(shape_pos, shape_rot, shape, max_dist)
513            })
514        }
515
516        /// Shortcut to [`RapierQueryPipeline::contact_with_shape`].
517        pub fn contact_with_shape(
518            &self,
519            shape_pos: Vect,
520            shape_rot: Rot,
521            shape: &(impl AsShape + ?Sized),
522            prediction: Real,
523            filter: QueryFilter,
524        ) -> Option<(Entity, ShapeContact)> {
525            self.with_query_pipeline(filter, |query_pipeline| {
526                query_pipeline.contact_with_shape(shape_pos, shape_rot, shape, prediction)
527            })
528        }
529
530        /// Shortcut to [`RapierQueryPipeline::bvh`].
531        pub fn bvh(&self) -> &Bvh {
532            self.simulation
533                .broad_phase
534                .as_query_pipeline(
535                    self.simulation.query_dispatcher(),
536                    &self.rigidbody_set.bodies,
537                    &self.colliders.colliders,
538                    Default::default(),
539                )
540                .bvh
541        }
542    }
543
544    impl RapierContextMut<'_> {
545        /// Reborrows this context immutably.
546        fn as_read(&self) -> RapierContext<'_> {
547            RapierContext {
548                simulation: &self.simulation,
549                colliders: &self.colliders,
550                joints: &self.joints,
551                rigidbody_set: &self.rigidbody_set,
552            }
553        }
554
555        /// Shortcut to [`RapierQueryPipeline::project_point_and_get_feature`].
556        pub fn project_point_and_get_feature(
557            &self,
558            point: Vect,
559            max_dist: Real,
560            filter: QueryFilter,
561        ) -> Option<(Entity, PointProjection, FeatureId)> {
562            self.as_read()
563                .project_point_and_get_feature(point, max_dist, filter)
564        }
565
566        /// Shortcut to [`RapierQueryPipeline::cast_shape_nonlinear`].
567        pub fn cast_shape_nonlinear(
568            &self,
569            shape_motion: &NonlinearMotion,
570            shape: &(impl AsShape + ?Sized),
571            start_time: Real,
572            end_time: Real,
573            stop_at_penetration: bool,
574            filter: QueryFilter,
575        ) -> Option<(Entity, ShapeCastHit)> {
576            self.as_read().cast_shape_nonlinear(
577                shape_motion,
578                shape,
579                start_time,
580                end_time,
581                stop_at_penetration,
582                filter,
583            )
584        }
585
586        /// Shortcut to [`RapierQueryPipeline::distance_to_shape`].
587        pub fn distance_to_shape(
588            &self,
589            shape_pos: Vect,
590            shape_rot: Rot,
591            shape: &(impl AsShape + ?Sized),
592            filter: QueryFilter,
593        ) -> Option<(Entity, Real)> {
594            self.as_read()
595                .distance_to_shape(shape_pos, shape_rot, shape, filter)
596        }
597
598        /// Shortcut to [`RapierQueryPipeline::closest_points_to_shape`].
599        pub fn closest_points_to_shape(
600            &self,
601            shape_pos: Vect,
602            shape_rot: Rot,
603            shape: &(impl AsShape + ?Sized),
604            max_dist: Real,
605            filter: QueryFilter,
606        ) -> Option<(Entity, ShapeClosestPoints)> {
607            self.as_read()
608                .closest_points_to_shape(shape_pos, shape_rot, shape, max_dist, filter)
609        }
610
611        /// Shortcut to [`RapierQueryPipeline::contact_with_shape`].
612        pub fn contact_with_shape(
613            &self,
614            shape_pos: Vect,
615            shape_rot: Rot,
616            shape: &(impl AsShape + ?Sized),
617            prediction: Real,
618            filter: QueryFilter,
619        ) -> Option<(Entity, ShapeContact)> {
620            self.as_read()
621                .contact_with_shape(shape_pos, shape_rot, shape, prediction, filter)
622        }
623
624        /// Shortcut to [`RapierQueryPipeline::bvh`].
625        pub fn bvh(&self) -> &Bvh {
626            self.simulation
627                .broad_phase
628                .as_query_pipeline(
629                    self.simulation.query_dispatcher(),
630                    &self.rigidbody_set.bodies,
631                    &self.colliders.colliders,
632                    Default::default(),
633                )
634                .bvh
635        }
636    }
637
638    // Copied from `RapierContext`.
639    impl RapierContextMut<'_> {
640        /// Shortcut to [`RapierQueryPipeline::cast_ray`].
641        pub fn cast_ray(
642            &self,
643            ray_origin: Vect,
644            ray_dir: Vect,
645            max_toi: Real,
646            solid: bool,
647            filter: QueryFilter,
648        ) -> Option<(Entity, Real)> {
649            self.with_query_pipeline(filter, |query_pipeline| {
650                query_pipeline.cast_ray(ray_origin, ray_dir, max_toi, solid)
651            })
652        }
653
654        /// Shortcut to [`RapierQueryPipeline::cast_ray_and_get_normal`].
655        pub fn cast_ray_and_get_normal(
656            &self,
657            ray_origin: Vect,
658            ray_dir: Vect,
659            max_toi: Real,
660            solid: bool,
661            filter: QueryFilter,
662        ) -> Option<(Entity, RayIntersection)> {
663            self.with_query_pipeline(filter, |query_pipeline| {
664                query_pipeline.cast_ray_and_get_normal(ray_origin, ray_dir, max_toi, solid)
665            })
666        }
667
668        /// Shortcut to [`RapierQueryPipeline::intersect_point`].
669        ///
670        /// Stops the query if `callback` returns false.
671        pub fn intersect_point(
672            &self,
673            point: Vect,
674            filter: QueryFilter,
675            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
676        ) {
677            self.with_query_pipeline(filter, |query_pipeline| {
678                for (e, co) in query_pipeline.intersect_point(point) {
679                    if !callback(e, co) {
680                        break;
681                    }
682                }
683            });
684        }
685
686        /// Shortcut to [`RapierQueryPipeline::intersect_ray`].
687        ///
688        /// Stops the query if `callback` returns false.
689        pub fn intersect_ray(
690            &self,
691            ray_origin: Vect,
692            ray_dir: Vect,
693            max_toi: Real,
694            solid: bool,
695            filter: QueryFilter,
696            mut callback: impl FnMut(Entity, &RapierCollider, RayIntersection) -> bool,
697        ) {
698            self.with_query_pipeline(filter, |query_pipeline| {
699                for (e, co, intersection) in
700                    query_pipeline.intersect_ray(ray_origin, ray_dir, max_toi, solid)
701                {
702                    if !callback(e, co, intersection) {
703                        break;
704                    }
705                }
706            });
707        }
708
709        /// Shortcut to [`RapierQueryPipeline::intersect_shape`].
710        pub fn intersect_shape(
711            &self,
712            shape_pos: Vect,
713            shape_rot: Rot,
714            shape: &(impl AsShape + ?Sized),
715            filter: QueryFilter,
716            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
717        ) {
718            self.with_query_pipeline(filter, |query_pipeline| {
719                for (e, co) in query_pipeline.intersect_shape(shape_pos, shape_rot, shape) {
720                    if !callback(e, co) {
721                        break;
722                    }
723                }
724            });
725        }
726
727        /// Shortcut to [`RapierQueryPipeline::intersect_aabb_conservative`].
728        pub fn intersect_aabb_conservative(
729            &self,
730            #[cfg(feature = "dim2")] aabb: bevy::shape::Aabb2d,
731            #[cfg(feature = "dim3")] aabb: bevy::shape::Aabb3d,
732            filter: QueryFilter,
733            mut callback: impl FnMut(Entity, &RapierCollider) -> bool,
734        ) {
735            self.with_query_pipeline(filter, |query_pipeline| {
736                for (e, co) in query_pipeline.intersect_aabb_conservative(aabb) {
737                    if !callback(e, co) {
738                        break;
739                    }
740                }
741            });
742        }
743
744        /// Shortcut to [`RapierQueryPipeline::cast_shape`].
745        pub fn cast_shape(
746            &self,
747            shape_pos: Vect,
748            shape_rot: Rot,
749            shape_vel: Vect,
750            shape: &(impl AsShape + ?Sized),
751            options: ShapeCastOptions,
752            filter: QueryFilter,
753        ) -> Option<(Entity, ShapeCastHit)> {
754            self.with_query_pipeline(filter, |query_pipeline| {
755                query_pipeline.cast_shape(shape_pos, shape_rot, shape_vel, shape, options)
756            })
757        }
758
759        /// Shortcut to [`RapierQueryPipeline::project_point`].
760        pub fn project_point(
761            &self,
762            point: Vect,
763            max_dist: f32,
764            solid: bool,
765            filter: QueryFilter,
766        ) -> Option<(Entity, PointProjection)> {
767            self.with_query_pipeline(filter, |query_pipeline| {
768                query_pipeline.project_point(point, max_dist, solid)
769            })
770        }
771    }
772
773    impl RapierContextMut<'_> {
774        /// Shortcut to [RapierQueryPipeline::new_scoped].
775        pub fn with_query_pipeline<'a, T>(
776            &'a self,
777            filter: QueryFilter<'a>,
778            scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
779        ) -> T {
780            crate::prelude::RapierQueryPipeline::new_scoped(
781                &self.simulation.broad_phase,
782                &self.colliders,
783                &self.rigidbody_set,
784                &filter,
785                self.simulation.query_dispatcher(),
786                scoped_fn,
787            )
788        }
789    }
790}
791
792mod rigidbody_set {
793    use std::collections::HashMap;
794
795    use super::*;
796    pub use rapier::prelude::RigidBodyHandle;
797
798    impl RapierContext<'_> {
799        /// Shortcut to [`RapierRigidBodySet::entity2body`].
800        pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
801            self.rigidbody_set.entity2body()
802        }
803
804        /// Shortcut to [`RapierRigidBodySet::rigid_body_entity`].
805        pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
806            self.rigidbody_set.rigid_body_entity(handle)
807        }
808
809        /// Shortcut to [`RapierRigidBodySet::impulse_revolute_joint_angle`].
810        pub fn impulse_revolute_joint_angle(&self, entity: Entity) -> Option<f32> {
811            self.rigidbody_set
812                .impulse_revolute_joint_angle(self.joints, entity)
813        }
814    }
815
816    impl RapierContextMut<'_> {
817        /// Shortcut to [`RapierRigidBodySet::propagate_modified_body_positions_to_colliders`].
818        pub fn propagate_modified_body_positions_to_colliders(&mut self) {
819            self.rigidbody_set
820                .propagate_modified_body_positions_to_colliders(&mut self.colliders)
821        }
822
823        /// Shortcut to [`RapierRigidBodySet::entity2body`].
824        pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
825            self.rigidbody_set.entity2body()
826        }
827
828        /// Shortcut to [`RapierRigidBodySet::rigid_body_entity`].
829        pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
830            self.rigidbody_set.rigid_body_entity(handle)
831        }
832
833        /// Shortcut to [`RapierRigidBodySet::impulse_revolute_joint_angle`].
834        pub fn impulse_revolute_joint_angle(&self, entity: Entity) -> Option<f32> {
835            self.rigidbody_set
836                .impulse_revolute_joint_angle(&self.joints, entity)
837        }
838    }
839}
840
841mod soft_bodies {
842    use super::*;
843    use crate::dynamics::RapierSoftBody;
844    use crate::pipeline::SoftBodyTearResult;
845    use rapier::dynamics::{RigidBodyHandle, SoftBodyHandle};
846    use std::collections::HashMap;
847
848    impl RapierContext<'_> {
849        /// Shortcut to [`RapierRigidBodySet::entity2soft_body`].
850        pub fn entity2soft_body(&self) -> &HashMap<Entity, SoftBodyHandle> {
851            self.rigidbody_set.entity2soft_body()
852        }
853
854        /// Shortcut to [`RapierRigidBodySet::soft_body_entity`].
855        pub fn soft_body_entity(&self, handle: SoftBodyHandle) -> Option<Entity> {
856            self.rigidbody_set.soft_body_entity(handle)
857        }
858
859        /// Shortcut to [`RapierRigidBodySet::soft_body`].
860        pub fn soft_body(&self, entity: Entity) -> Option<&RapierSoftBody> {
861            self.rigidbody_set.soft_body(entity)
862        }
863
864        /// Shortcut to [`RapierRigidBodySet::soft_body_particle_positions`].
865        pub fn soft_body_particle_positions(
866            &self,
867            entity: Entity,
868        ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
869            self.rigidbody_set.soft_body_particle_positions(entity)
870        }
871
872        /// Shortcut to [`RapierRigidBodySet::soft_body_particle_velocities`].
873        pub fn soft_body_particle_velocities(
874            &self,
875            entity: Entity,
876        ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
877            self.rigidbody_set.soft_body_particle_velocities(entity)
878        }
879
880        /// Shortcut to [`RapierRigidBodySet::soft_body_volume`].
881        pub fn soft_body_volume(&self, entity: Entity) -> Option<Real> {
882            self.rigidbody_set.soft_body_volume(entity)
883        }
884
885        /// Shortcut to [`RapierRigidBodySet::soft_body_mass`].
886        pub fn soft_body_mass(&self, entity: Entity) -> Option<Real> {
887            self.rigidbody_set.soft_body_mass(entity)
888        }
889
890        /// Shortcut to [`RapierRigidBodySet::soft_body_center_of_mass`].
891        pub fn soft_body_center_of_mass(&self, entity: Entity) -> Option<Vect> {
892            self.rigidbody_set.soft_body_center_of_mass(entity)
893        }
894
895        /// Shortcut to [`RapierRigidBodySet::is_soft_body_sleeping`].
896        pub fn is_soft_body_sleeping(&self, entity: Entity) -> Option<bool> {
897            self.rigidbody_set.is_soft_body_sleeping(entity)
898        }
899
900        /// Shortcut to [`RapierRigidBodySet::soft_body_whole_proxy`].
901        pub fn soft_body_whole_proxy(&self, entity: Entity) -> Option<RigidBodyHandle> {
902            self.rigidbody_set.soft_body_whole_proxy(entity)
903        }
904
905        /// Shortcut to [`RapierRigidBodySet::soft_body_cluster_index`].
906        pub fn soft_body_cluster_index(&self, entity: Entity) -> Option<(SoftBodyHandle, u32)> {
907            self.rigidbody_set.soft_body_cluster_index(entity)
908        }
909    }
910
911    impl RapierContextMut<'_> {
912        /// Shortcut to [`RapierRigidBodySet::entity2soft_body`].
913        pub fn entity2soft_body(&self) -> &HashMap<Entity, SoftBodyHandle> {
914            self.rigidbody_set.entity2soft_body()
915        }
916
917        /// Shortcut to [`RapierRigidBodySet::soft_body_entity`].
918        pub fn soft_body_entity(&self, handle: SoftBodyHandle) -> Option<Entity> {
919            self.rigidbody_set.soft_body_entity(handle)
920        }
921
922        /// Shortcut to [`RapierRigidBodySet::soft_body`].
923        pub fn soft_body(&self, entity: Entity) -> Option<&RapierSoftBody> {
924            self.rigidbody_set.soft_body(entity)
925        }
926
927        /// Shortcut to [`RapierRigidBodySet::soft_body_particle_positions`].
928        pub fn soft_body_particle_positions(
929            &self,
930            entity: Entity,
931        ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
932            self.rigidbody_set.soft_body_particle_positions(entity)
933        }
934
935        /// Shortcut to [`RapierRigidBodySet::soft_body_particle_velocities`].
936        pub fn soft_body_particle_velocities(
937            &self,
938            entity: Entity,
939        ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
940            self.rigidbody_set.soft_body_particle_velocities(entity)
941        }
942
943        /// Shortcut to [`RapierRigidBodySet::soft_body_volume`].
944        pub fn soft_body_volume(&self, entity: Entity) -> Option<Real> {
945            self.rigidbody_set.soft_body_volume(entity)
946        }
947
948        /// Shortcut to [`RapierRigidBodySet::soft_body_mass`].
949        pub fn soft_body_mass(&self, entity: Entity) -> Option<Real> {
950            self.rigidbody_set.soft_body_mass(entity)
951        }
952
953        /// Shortcut to [`RapierRigidBodySet::soft_body_center_of_mass`].
954        pub fn soft_body_center_of_mass(&self, entity: Entity) -> Option<Vect> {
955            self.rigidbody_set.soft_body_center_of_mass(entity)
956        }
957
958        /// Shortcut to [`RapierRigidBodySet::is_soft_body_sleeping`].
959        pub fn is_soft_body_sleeping(&self, entity: Entity) -> Option<bool> {
960            self.rigidbody_set.is_soft_body_sleeping(entity)
961        }
962
963        /// Shortcut to [`RapierRigidBodySet::soft_body_whole_proxy`].
964        pub fn soft_body_whole_proxy(&self, entity: Entity) -> Option<RigidBodyHandle> {
965            self.rigidbody_set.soft_body_whole_proxy(entity)
966        }
967
968        /// Shortcut to [`RapierRigidBodySet::soft_body_cluster_index`].
969        pub fn soft_body_cluster_index(&self, entity: Entity) -> Option<(SoftBodyHandle, u32)> {
970            self.rigidbody_set.soft_body_cluster_index(entity)
971        }
972
973        /// Shortcut to [`RapierRigidBodySet::soft_body_mut`].
974        pub fn soft_body_mut(&mut self, entity: Entity) -> Option<&mut RapierSoftBody> {
975            self.rigidbody_set.soft_body_mut(entity)
976        }
977
978        /// Shortcut to [`RapierRigidBodySet::wake_up_soft_body`].
979        pub fn wake_up_soft_body(&mut self, entity: Entity, strong: bool) -> bool {
980            self.rigidbody_set.wake_up_soft_body(entity, strong)
981        }
982
983        /// Shortcut to [`RapierContextSimulation::tear_soft_body`].
984        pub fn tear_soft_body(
985            &mut self,
986            commands: &mut Commands,
987            entity: Entity,
988            edges: &[u32],
989            cells: &[u32],
990        ) -> Option<SoftBodyTearResult> {
991            self.simulation.tear_soft_body(
992                &mut self.colliders,
993                &mut self.joints,
994                &mut self.rigidbody_set,
995                commands,
996                entity,
997                edges,
998                cells,
999            )
1000        }
1001
1002        /// Shortcut to [`RapierContextSimulation::cut_soft_body`].
1003        pub fn cut_soft_body(
1004            &mut self,
1005            commands: &mut Commands,
1006            entity: Entity,
1007            blade: &[Vect; rapier::math::DIM],
1008        ) -> Option<SoftBodyTearResult> {
1009            self.simulation.cut_soft_body(
1010                &mut self.colliders,
1011                &mut self.joints,
1012                &mut self.rigidbody_set,
1013                commands,
1014                entity,
1015                blade,
1016            )
1017        }
1018    }
1019}
1020
1021mod joints {
1022    use super::*;
1023    use crate::dynamics::{ImpulseJointImpulses, InverseKinematicsOption, MultibodyJointState};
1024    use rapier::dynamics::Multibody;
1025    use rapier::math::Jacobian;
1026    use std::ops::Range;
1027
1028    impl RapierContext<'_> {
1029        /// Shortcut to [`RapierContextJoints::impulse_joint_impulses`].
1030        pub fn impulse_joint_impulses(&self, entity: Entity) -> Option<ImpulseJointImpulses> {
1031            self.joints.impulse_joint_impulses(entity)
1032        }
1033
1034        /// Shortcut to [`RapierContextJoints::impulse_joints_between`].
1035        pub fn impulse_joints_between(
1036            &self,
1037            body1: Entity,
1038            body2: Entity,
1039        ) -> impl Iterator<Item = Entity> + '_ {
1040            self.joints
1041                .impulse_joints_between(self.rigidbody_set, body1, body2)
1042        }
1043
1044        /// Shortcut to [`RapierContextJoints::attached_impulse_joints`].
1045        pub fn attached_impulse_joints(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
1046            self.joints
1047                .attached_impulse_joints(self.rigidbody_set, body)
1048        }
1049
1050        /// Shortcut to [`RapierContextJoints::attached_enabled_impulse_joints`].
1051        pub fn attached_enabled_impulse_joints(
1052            &self,
1053            body: Entity,
1054        ) -> impl Iterator<Item = Entity> + '_ {
1055            self.joints
1056                .attached_enabled_impulse_joints(self.rigidbody_set, body)
1057        }
1058
1059        /// Shortcut to [`RapierContextJoints::multibody`].
1060        pub fn multibody(&self, entity: Entity) -> Option<(&Multibody, usize)> {
1061            self.joints.multibody(self.rigidbody_set, entity)
1062        }
1063
1064        /// Shortcut to [`RapierContextJoints::multibody_root`].
1065        pub fn multibody_root(&self, entity: Entity) -> Option<Entity> {
1066            self.joints.multibody_root(self.rigidbody_set, entity)
1067        }
1068
1069        /// Shortcut to [`RapierContextJoints::multibody_links`].
1070        pub fn multibody_links(&self, entity: Entity) -> impl Iterator<Item = Entity> + '_ {
1071            self.joints.multibody_links(self.rigidbody_set, entity)
1072        }
1073
1074        /// Shortcut to [`RapierContextJoints::multibody_parent_link`].
1075        pub fn multibody_parent_link(&self, entity: Entity) -> Option<Entity> {
1076            self.joints
1077                .multibody_parent_link(self.rigidbody_set, entity)
1078        }
1079
1080        /// Shortcut to [`RapierContextJoints::multibody_joint_between`].
1081        pub fn multibody_joint_between(&self, body1: Entity, body2: Entity) -> Option<Entity> {
1082            self.joints
1083                .multibody_joint_between(self.rigidbody_set, body1, body2)
1084        }
1085
1086        /// Shortcut to [`RapierContextJoints::attached_multibody_joints`].
1087        pub fn attached_multibody_joints(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
1088            self.joints
1089                .attached_multibody_joints(self.rigidbody_set, body)
1090        }
1091
1092        /// Shortcut to [`RapierContextJoints::multibody_link_dofs`].
1093        pub fn multibody_link_dofs(&self, entity: Entity) -> Option<Range<usize>> {
1094            self.joints.multibody_link_dofs(self.rigidbody_set, entity)
1095        }
1096
1097        /// Shortcut to [`RapierContextJoints::multibody_generalized_velocity`].
1098        pub fn multibody_generalized_velocity(&self, entity: Entity) -> Option<&[Real]> {
1099            self.joints
1100                .multibody_generalized_velocity(self.rigidbody_set, entity)
1101        }
1102
1103        /// Shortcut to [`RapierContextJoints::multibody_joint_state`].
1104        pub fn multibody_joint_state(&self, entity: Entity) -> Option<MultibodyJointState> {
1105            self.joints.multibody_joint_state(entity)
1106        }
1107
1108        /// Shortcut to [`RapierContextJoints::multibody_body_jacobian`].
1109        pub fn multibody_body_jacobian(&self, entity: Entity) -> Option<&Jacobian<Real>> {
1110            self.joints
1111                .multibody_body_jacobian(self.rigidbody_set, entity)
1112        }
1113
1114        /// Shortcut to [`RapierContextJoints::multibody_inverse_kinematics`].
1115        pub fn multibody_inverse_kinematics(
1116            &self,
1117            link: Entity,
1118            target: Transform,
1119            options: &InverseKinematicsOption,
1120            joint_can_move: impl Fn(Entity) -> bool,
1121            displacements: &mut Vec<Real>,
1122        ) -> bool {
1123            self.joints.multibody_inverse_kinematics(
1124                self.rigidbody_set,
1125                link,
1126                target,
1127                options,
1128                joint_can_move,
1129                displacements,
1130            )
1131        }
1132    }
1133
1134    impl RapierContextMut<'_> {
1135        /// Shortcut to [`RapierContextJoints::impulse_joint_impulses`].
1136        pub fn impulse_joint_impulses(&self, entity: Entity) -> Option<ImpulseJointImpulses> {
1137            self.joints.impulse_joint_impulses(entity)
1138        }
1139
1140        /// Shortcut to [`RapierContextJoints::impulse_joints_between`].
1141        pub fn impulse_joints_between(
1142            &self,
1143            body1: Entity,
1144            body2: Entity,
1145        ) -> impl Iterator<Item = Entity> + '_ {
1146            self.joints
1147                .impulse_joints_between(&self.rigidbody_set, body1, body2)
1148        }
1149
1150        /// Shortcut to [`RapierContextJoints::attached_impulse_joints`].
1151        pub fn attached_impulse_joints(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
1152            self.joints
1153                .attached_impulse_joints(&self.rigidbody_set, body)
1154        }
1155
1156        /// Shortcut to [`RapierContextJoints::attached_enabled_impulse_joints`].
1157        pub fn attached_enabled_impulse_joints(
1158            &self,
1159            body: Entity,
1160        ) -> impl Iterator<Item = Entity> + '_ {
1161            self.joints
1162                .attached_enabled_impulse_joints(&self.rigidbody_set, body)
1163        }
1164
1165        /// Shortcut to [`RapierContextJoints::multibody`].
1166        pub fn multibody(&self, entity: Entity) -> Option<(&Multibody, usize)> {
1167            self.joints.multibody(&self.rigidbody_set, entity)
1168        }
1169
1170        /// Shortcut to [`RapierContextJoints::multibody_root`].
1171        pub fn multibody_root(&self, entity: Entity) -> Option<Entity> {
1172            self.joints.multibody_root(&self.rigidbody_set, entity)
1173        }
1174
1175        /// Shortcut to [`RapierContextJoints::multibody_links`].
1176        pub fn multibody_links(&self, entity: Entity) -> impl Iterator<Item = Entity> + '_ {
1177            self.joints.multibody_links(&self.rigidbody_set, entity)
1178        }
1179
1180        /// Shortcut to [`RapierContextJoints::multibody_parent_link`].
1181        pub fn multibody_parent_link(&self, entity: Entity) -> Option<Entity> {
1182            self.joints
1183                .multibody_parent_link(&self.rigidbody_set, entity)
1184        }
1185
1186        /// Shortcut to [`RapierContextJoints::multibody_joint_between`].
1187        pub fn multibody_joint_between(&self, body1: Entity, body2: Entity) -> Option<Entity> {
1188            self.joints
1189                .multibody_joint_between(&self.rigidbody_set, body1, body2)
1190        }
1191
1192        /// Shortcut to [`RapierContextJoints::attached_multibody_joints`].
1193        pub fn attached_multibody_joints(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
1194            self.joints
1195                .attached_multibody_joints(&self.rigidbody_set, body)
1196        }
1197
1198        /// Shortcut to [`RapierContextJoints::multibody_link_dofs`].
1199        pub fn multibody_link_dofs(&self, entity: Entity) -> Option<Range<usize>> {
1200            self.joints.multibody_link_dofs(&self.rigidbody_set, entity)
1201        }
1202
1203        /// Shortcut to [`RapierContextJoints::multibody_generalized_velocity`].
1204        pub fn multibody_generalized_velocity(&self, entity: Entity) -> Option<&[Real]> {
1205            self.joints
1206                .multibody_generalized_velocity(&self.rigidbody_set, entity)
1207        }
1208
1209        /// Shortcut to [`RapierContextJoints::multibody_joint_state`].
1210        pub fn multibody_joint_state(&self, entity: Entity) -> Option<MultibodyJointState> {
1211            self.joints.multibody_joint_state(entity)
1212        }
1213
1214        /// Shortcut to [`RapierContextJoints::multibody_body_jacobian`].
1215        pub fn multibody_body_jacobian(&self, entity: Entity) -> Option<&Jacobian<Real>> {
1216            self.joints
1217                .multibody_body_jacobian(&self.rigidbody_set, entity)
1218        }
1219
1220        /// Shortcut to [`RapierContextJoints::multibody_inverse_kinematics`].
1221        pub fn multibody_inverse_kinematics(
1222            &self,
1223            link: Entity,
1224            target: Transform,
1225            options: &InverseKinematicsOption,
1226            joint_can_move: impl Fn(Entity) -> bool,
1227            displacements: &mut Vec<Real>,
1228        ) -> bool {
1229            self.joints.multibody_inverse_kinematics(
1230                &self.rigidbody_set,
1231                link,
1232                target,
1233                options,
1234                joint_can_move,
1235                displacements,
1236            )
1237        }
1238
1239        /// Shortcut to [`RapierContextJoints::multibody_mut`].
1240        pub fn multibody_mut(&mut self, entity: Entity) -> Option<(&mut Multibody, usize)> {
1241            self.joints.multibody_mut(&self.rigidbody_set, entity)
1242        }
1243
1244        /// Shortcut to [`RapierContextJoints::multibody_generalized_velocity_mut`].
1245        pub fn multibody_generalized_velocity_mut(
1246            &mut self,
1247            entity: Entity,
1248        ) -> Option<&mut [Real]> {
1249            self.joints
1250                .multibody_generalized_velocity_mut(&mut self.rigidbody_set, entity)
1251        }
1252
1253        /// Shortcut to [`RapierContextJoints::multibody_joint_velocity_mut`].
1254        pub fn multibody_joint_velocity_mut(&mut self, entity: Entity) -> Option<&mut [Real]> {
1255            self.joints
1256                .multibody_joint_velocity_mut(&mut self.rigidbody_set, entity)
1257        }
1258
1259        /// Shortcut to [`RapierContextJoints::multibody_apply_displacements`].
1260        pub fn multibody_apply_displacements(
1261            &mut self,
1262            entity: Entity,
1263            displacements: &[Real],
1264        ) -> bool {
1265            self.joints
1266                .multibody_apply_displacements(&self.rigidbody_set, entity, displacements)
1267        }
1268
1269        /// Shortcut to [`RapierContextJoints::multibody_forward_kinematics`].
1270        pub fn multibody_forward_kinematics(
1271            &mut self,
1272            entity: Entity,
1273            read_root_pose_from_rigid_body: bool,
1274        ) -> bool {
1275            self.joints.multibody_forward_kinematics(
1276                &mut self.rigidbody_set,
1277                entity,
1278                read_root_pose_from_rigid_body,
1279            )
1280        }
1281    }
1282}
1283
1284#[cfg(test)]
1285mod tests {
1286    use super::*;
1287    use crate::geometry::{Collider, NonlinearMotion, ShapeCastOptions, ShapeClosestPoints};
1288    use crate::plugin::{NoUserData, RapierPhysicsPlugin};
1289    use crate::prelude::QueryFilter;
1290    use approx::assert_relative_eq;
1291    use bevy::ecs::system::RunSystemOnce;
1292    use bevy::time::{TimePlugin, TimeUpdateStrategy};
1293
1294    #[cfg(feature = "dim2")]
1295    fn v(x: Real, y: Real) -> Vect {
1296        Vect::new(x, y)
1297    }
1298    #[cfg(feature = "dim3")]
1299    fn v(x: Real, y: Real) -> Vect {
1300        Vect::new(x, y, 0.0)
1301    }
1302
1303    #[cfg(feature = "dim2")]
1304    fn quarter_turn() -> Rot {
1305        std::f32::consts::FRAC_PI_2
1306    }
1307    #[cfg(feature = "dim3")]
1308    fn quarter_turn() -> Rot {
1309        Rot::from_rotation_z(std::f32::consts::FRAC_PI_2)
1310    }
1311
1312    #[cfg(feature = "dim2")]
1313    fn unit_cuboid() -> Collider {
1314        Collider::cuboid(0.5, 0.5)
1315    }
1316    #[cfg(feature = "dim3")]
1317    fn unit_cuboid() -> Collider {
1318        Collider::cuboid(0.5, 0.5, 0.5)
1319    }
1320
1321    fn transform_at(x: Real, y: Real) -> Transform {
1322        Transform::from_xyz(x, y, 0.0)
1323    }
1324
1325    fn test_app() -> App {
1326        let mut app = App::new();
1327        app.add_plugins((
1328            TransformPlugin,
1329            TimePlugin,
1330            RapierPhysicsPlugin::<NoUserData>::default(),
1331        ))
1332        .insert_resource(TimeUpdateStrategy::ManualDuration(
1333            std::time::Duration::from_secs_f32(1.0 / 60.0),
1334        ));
1335        app.finish();
1336        app.update();
1337        app
1338    }
1339
1340    /// Spawns a fixed ball at the origin and a fixed cuboid at `x = 10`, then steps the world.
1341    fn ball_and_cuboid_app() -> (App, Entity, Entity) {
1342        let mut app = test_app();
1343        let ball = app
1344            .world_mut()
1345            .spawn((transform_at(0.0, 0.0), Collider::ball(0.5)))
1346            .id();
1347        let cuboid = app
1348            .world_mut()
1349            .spawn((transform_at(10.0, 0.0), unit_cuboid()))
1350            .id();
1351        app.update();
1352        app.update();
1353        (app, ball, cuboid)
1354    }
1355
1356    fn run<T: Send + 'static>(
1357        app: &mut App,
1358        f: impl Fn(RapierContext) -> T + Send + Sync + 'static,
1359    ) -> T {
1360        app.world_mut()
1361            .run_system_once(move |context: ReadRapierContext| f(context.single().unwrap()))
1362            .unwrap()
1363    }
1364
1365    #[test]
1366    fn cast_shape_nonlinear_hits_the_first_collider() {
1367        let (mut app, ball, _) = ball_and_cuboid_app();
1368        let (from_context, from_pipeline) = run(&mut app, |context| {
1369            let shape = Collider::ball(0.5);
1370            let mut motion = NonlinearMotion::constant_position(v(-5.0, 0.0), Rot::default());
1371            motion.linear_velocity = v(1.0, 0.0);
1372            let from_context = context.cast_shape_nonlinear(
1373                &motion,
1374                &*shape.raw,
1375                0.0,
1376                10.0,
1377                true,
1378                QueryFilter::default(),
1379            );
1380            let from_pipeline = context.with_query_pipeline(QueryFilter::default(), |pipeline| {
1381                pipeline.cast_shape_nonlinear(&motion, &*shape.raw, 0.0, 2.0, true)
1382            });
1383            (from_context, from_pipeline)
1384        });
1385
1386        let (entity, hit) = from_context.unwrap();
1387        assert_eq!(entity, ball);
1388        assert_relative_eq!(hit.time_of_impact, 4.0, epsilon = 1.0e-2);
1389        // The end time is too short to reach the ball.
1390        assert!(from_pipeline.is_none());
1391    }
1392
1393    #[test]
1394    fn scene_queries_report_subshapes() {
1395        let mut app = test_app();
1396        let compound = app
1397            .world_mut()
1398            .spawn((
1399                transform_at(0.0, 0.0),
1400                Collider::compound(vec![
1401                    (v(0.0, 0.0), Rot::default(), Collider::ball(0.5)),
1402                    (v(3.0, 0.0), Rot::default(), Collider::ball(0.5)),
1403                ]),
1404            ))
1405            .id();
1406        app.update();
1407        app.update();
1408
1409        run(&mut app, move |context| {
1410            let filter = QueryFilter::default();
1411            let (e, inter) = context
1412                .cast_ray_and_get_normal(v(3.0, 5.0), v(0.0, -1.0), Real::MAX, true, filter)
1413                .unwrap();
1414            assert_eq!(e, compound);
1415            assert_eq!(inter.subshape, 1);
1416
1417            let mut ray_subshapes = vec![];
1418            context.intersect_ray(
1419                v(3.0, 5.0),
1420                v(0.0, -1.0),
1421                Real::MAX,
1422                true,
1423                filter,
1424                |_, _, i| {
1425                    ray_subshapes.push(i.subshape);
1426                    true
1427                },
1428            );
1429            assert_eq!(ray_subshapes, vec![1]);
1430
1431            let (_, proj) = context
1432                .project_point(v(3.0, 2.0), Real::MAX, true, filter)
1433                .unwrap();
1434            assert_eq!(proj.subshape, 1);
1435            let (_, proj) = context
1436                .project_point(v(0.0, 2.0), Real::MAX, true, filter)
1437                .unwrap();
1438            assert_eq!(proj.subshape, 0);
1439
1440            let shape = Collider::ball(0.25);
1441            let (_, hit) = context
1442                .cast_shape(
1443                    v(3.0, 5.0),
1444                    Rot::default(),
1445                    v(0.0, -1.0),
1446                    &*shape.raw,
1447                    ShapeCastOptions::default(),
1448                    filter,
1449                )
1450                .unwrap();
1451            assert_eq!(hit.subshape1, 1);
1452            assert_eq!(hit.subshape2, 0);
1453
1454            let (_, contact) = context
1455                .contact_with_shape(v(3.0, 1.0), Rot::default(), &*shape.raw, 1.0, filter)
1456                .unwrap();
1457            assert_eq!(contact.subshape1, 1);
1458        });
1459    }
1460
1461    #[test]
1462    fn project_point_and_get_feature_honors_max_dist() {
1463        let (mut app, ball, _) = ball_and_cuboid_app();
1464        run(&mut app, move |context| {
1465            let filter = QueryFilter::default();
1466            assert!(context
1467                .project_point_and_get_feature(v(0.0, 2.5), 1.0, filter)
1468                .is_none());
1469            let (e, proj, _) = context
1470                .project_point_and_get_feature(v(0.0, 2.5), 3.0, filter)
1471                .unwrap();
1472            assert_eq!(e, ball);
1473            assert_relative_eq!(proj.point, v(0.0, 0.5), epsilon = 1.0e-4);
1474
1475            let from_pipeline = context.with_query_pipeline(filter, |pipeline| {
1476                pipeline.project_point_and_get_feature(v(0.0, 2.5), 1.0)
1477            });
1478            assert!(from_pipeline.is_none());
1479        });
1480    }
1481
1482    #[test]
1483    fn predicate_receives_the_collider() {
1484        let (mut app, ball, cuboid) = ball_and_cuboid_app();
1485        run(&mut app, move |context| {
1486            let hits = |filter: QueryFilter| {
1487                let mut hits = vec![];
1488                context.intersect_ray(
1489                    v(-5.0, 0.0),
1490                    v(1.0, 0.0),
1491                    Real::MAX,
1492                    true,
1493                    filter,
1494                    |e, _, _| {
1495                        hits.push(e);
1496                        true
1497                    },
1498                );
1499                hits.sort();
1500                hits
1501            };
1502            let mut all = vec![ball, cuboid];
1503            all.sort();
1504
1505            let is_cuboid =
1506                |_: Entity, co: &rapier::geometry::Collider| co.shape().as_cuboid().is_some();
1507            let not_cuboid = |e: Entity, _: &rapier::geometry::Collider| e != cuboid;
1508
1509            assert_eq!(hits(QueryFilter::default()), all);
1510            assert_eq!(
1511                hits(QueryFilter::default().predicate(&is_cuboid)),
1512                vec![cuboid]
1513            );
1514            assert_eq!(
1515                hits(QueryFilter::default().predicate(&not_cuboid)),
1516                vec![ball]
1517            );
1518        });
1519    }
1520
1521    #[test]
1522    fn intersect_queries_yield_colliders() {
1523        let (mut app, ball, cuboid) = ball_and_cuboid_app();
1524        run(&mut app, move |context| {
1525            let filter = QueryFilter::default();
1526            let is_ball = |co: &rapier::geometry::Collider| co.shape().as_ball().is_some();
1527
1528            let mut point_hits = vec![];
1529            context.intersect_point(v(0.0, 0.0), filter, |e, co| {
1530                point_hits.push((e, is_ball(co)));
1531                true
1532            });
1533            assert_eq!(point_hits, vec![(ball, true)]);
1534
1535            let mut ray_hits = vec![];
1536            context.intersect_ray(
1537                v(10.0, 5.0),
1538                v(0.0, -1.0),
1539                Real::MAX,
1540                true,
1541                filter,
1542                |e, co, inter| {
1543                    ray_hits.push((e, is_ball(co), inter.time_of_impact));
1544                    true
1545                },
1546            );
1547            assert_eq!(ray_hits.len(), 1);
1548            assert_eq!(ray_hits[0].0, cuboid);
1549            assert!(!ray_hits[0].1);
1550            assert_relative_eq!(ray_hits[0].2, 4.5, epsilon = 1.0e-4);
1551
1552            let shape = Collider::ball(1.0);
1553            let mut shape_hits = vec![];
1554            context.intersect_shape(
1555                v(10.0, 1.0),
1556                Rot::default(),
1557                &*shape.raw,
1558                filter,
1559                |e, co| {
1560                    shape_hits.push((e, is_ball(co)));
1561                    true
1562                },
1563            );
1564            assert_eq!(shape_hits, vec![(cuboid, false)]);
1565
1566            let aabb = Collider::ball(0.1).aabb(v(0.0, 0.0), Rot::default());
1567            let mut aabb_hits = vec![];
1568            context.intersect_aabb_conservative(aabb, filter, |e, co| {
1569                aabb_hits.push((e, is_ball(co)));
1570                true
1571            });
1572            assert_eq!(aabb_hits, vec![(ball, true)]);
1573
1574            // The pipeline iterators yield the same colliders.
1575            context.with_query_pipeline(filter, |pipeline| {
1576                let hits: Vec<_> = pipeline
1577                    .intersect_point(v(10.0, 0.0))
1578                    .map(|(e, co)| (e, is_ball(co)))
1579                    .collect();
1580                assert_eq!(hits, vec![(cuboid, false)]);
1581            });
1582        });
1583    }
1584
1585    #[test]
1586    fn distance_closest_points_and_contact_with_scene() {
1587        let (mut app, ball, cuboid) = ball_and_cuboid_app();
1588        run(&mut app, move |context| {
1589            let filter = QueryFilter::default();
1590            let shape = Collider::ball(0.5);
1591            // The shape is rotated to check that its local results are moved to world-space.
1592            let rot = quarter_turn();
1593
1594            let (e, dist) = context
1595                .distance_to_shape(v(2.0, 0.0), rot, &*shape.raw, filter)
1596                .unwrap();
1597            assert_eq!(e, ball);
1598            assert_relative_eq!(dist, 1.0, epsilon = 1.0e-4);
1599
1600            let (e, dist) = context
1601                .distance_to_shape(
1602                    v(2.0, 0.0),
1603                    rot,
1604                    &*shape.raw,
1605                    QueryFilter::default().exclude_collider(ball),
1606                )
1607                .unwrap();
1608            assert_eq!(e, cuboid);
1609            assert_relative_eq!(dist, 7.0, epsilon = 1.0e-4);
1610
1611            assert!(context
1612                .closest_points_to_shape(v(2.0, 0.0), rot, &*shape.raw, 0.5, filter)
1613                .is_none());
1614            let (e, pts) = context
1615                .closest_points_to_shape(v(2.0, 0.0), rot, &*shape.raw, 2.0, filter)
1616                .unwrap();
1617            assert_eq!(e, ball);
1618            let ShapeClosestPoints::WithinMargin(p1, p2) = pts else {
1619                panic!("unexpected closest points: {pts:?}");
1620            };
1621            assert_relative_eq!(p1, v(0.5, 0.0), epsilon = 1.0e-4);
1622            assert_relative_eq!(p2, v(1.5, 0.0), epsilon = 1.0e-4);
1623            let (_, pts) = context
1624                .closest_points_to_shape(v(0.5, 0.0), rot, &*shape.raw, 2.0, filter)
1625                .unwrap();
1626            assert_eq!(pts, ShapeClosestPoints::Intersecting);
1627
1628            assert!(context
1629                .contact_with_shape(v(2.0, 0.0), rot, &*shape.raw, 0.5, filter)
1630                .is_none());
1631            let (e, contact) = context
1632                .contact_with_shape(v(2.0, 0.0), rot, &*shape.raw, 2.0, filter)
1633                .unwrap();
1634            assert_eq!(e, ball);
1635            assert_relative_eq!(contact.distance, 1.0, epsilon = 1.0e-4);
1636            assert_relative_eq!(contact.point1, v(0.5, 0.0), epsilon = 1.0e-4);
1637            assert_relative_eq!(contact.point2, v(1.5, 0.0), epsilon = 1.0e-4);
1638            assert_relative_eq!(contact.normal1, v(1.0, 0.0), epsilon = 1.0e-4);
1639            assert_relative_eq!(contact.normal2, v(-1.0, 0.0), epsilon = 1.0e-4);
1640        });
1641    }
1642
1643    #[test]
1644    fn bvh_leaves_resolve_to_filtered_colliders() {
1645        let (mut app, ball, cuboid) = ball_and_cuboid_app();
1646        run(&mut app, move |context| {
1647            let leaves: Vec<u32> = context.bvh().leaves(|_| true).collect();
1648            assert_eq!(leaves.len(), 2);
1649
1650            let filter = QueryFilter::default().exclude_collider(cuboid);
1651            context.with_query_pipeline(filter, |pipeline| {
1652                let mut resolved: Vec<_> = leaves
1653                    .iter()
1654                    .filter_map(|leaf| pipeline.bvh_leaf_collider(*leaf))
1655                    .map(|(e, _)| e)
1656                    .collect();
1657                resolved.sort();
1658                assert_eq!(resolved, vec![ball]);
1659                assert_eq!(pipeline.bvh().leaves(|_| true).count(), 2);
1660            });
1661        });
1662    }
1663
1664    #[test]
1665    fn scene_shape_casts_report_world_space_collider_witnesses() {
1666        let (mut app, _, cuboid) = ball_and_cuboid_app();
1667        let (entity, hit) = run(&mut app, |context| {
1668            let shape = Collider::ball(0.5);
1669            context.cast_shape(
1670                v(5.0, 0.0),
1671                quarter_turn(),
1672                v(1.0, 0.0),
1673                &shape,
1674                ShapeCastOptions::default(),
1675                QueryFilter::default(),
1676            )
1677        })
1678        .unwrap();
1679        assert_eq!(entity, cuboid);
1680        let details = hit.details.unwrap();
1681        // The collider hit (the first shape) is in world-space.
1682        assert_relative_eq!(details.witness1, v(9.5, 0.0), epsilon = 1.0e-2);
1683        assert_relative_eq!(details.normal1, v(-1.0, 0.0), epsilon = 1.0e-2);
1684        // The cast shape (the second shape), rotated by a quarter turn, is in its local-space.
1685        assert_relative_eq!(details.witness2, v(0.0, -0.5), epsilon = 1.0e-2);
1686        assert_relative_eq!(details.normal2, v(0.0, -1.0), epsilon = 1.0e-2);
1687    }
1688
1689    #[test]
1690    fn move_shape_from_a_user_system() {
1691        let (mut app, ball, _) = ball_and_cuboid_app();
1692        let (output, hits) = app
1693            .world_mut()
1694            .run_system_once(|mut context: WriteRapierContext| {
1695                let mut context = context.single_mut().unwrap();
1696                let options = crate::control::MoveShapeOptions {
1697                    snap_to_ground: None,
1698                    ..Default::default()
1699                };
1700                let mut hits = vec![];
1701                let output = context.move_shape(
1702                    v(8.0, 0.0),
1703                    &rapier::parry::shape::Ball::new(0.5),
1704                    v(-5.0, 0.0),
1705                    Rot::default(),
1706                    1.0,
1707                    &options,
1708                    QueryFilter::default(),
1709                    |collision| hits.push(collision.entity),
1710                );
1711                (output, hits)
1712            })
1713            .unwrap();
1714
1715        // The ball at the origin stops the shape before it reaches `x = -1`.
1716        assert!(output.effective_translation.x < 4.0);
1717        assert!(output.effective_translation.x > 3.5);
1718        assert!(hits.contains(&ball));
1719    }
1720
1721    /// A query dispatcher counting its shape-casts before forwarding every query to the default
1722    /// dispatcher.
1723    struct CountingDispatcher(std::sync::Arc<std::sync::atomic::AtomicUsize>);
1724
1725    mod counting_dispatcher {
1726        use super::CountingDispatcher;
1727        use rapier::geometry::{ContactData, ContactManifoldData};
1728        use rapier::math::{Pose, Real, Vector};
1729        use rapier::parry::query::{
1730            details::NormalConstraints, ClosestPoints, Contact, ContactManifold,
1731            ContactManifoldsWorkspace, DefaultQueryDispatcher, NonlinearRigidMotion,
1732            PersistentQueryDispatcher, QueryDispatcher, ShapeCastHit, ShapeCastOptions,
1733            ShapeDistance, ShapeIntersection, Unsupported,
1734        };
1735        use rapier::parry::shape::Shape;
1736        use std::sync::atomic::Ordering;
1737
1738        impl QueryDispatcher for CountingDispatcher {
1739            fn intersection_test(
1740                &self,
1741                pos12: &Pose,
1742                g1: &dyn Shape,
1743                g2: &dyn Shape,
1744            ) -> Result<ShapeIntersection, Unsupported> {
1745                DefaultQueryDispatcher.intersection_test(pos12, g1, g2)
1746            }
1747
1748            fn distance(
1749                &self,
1750                pos12: &Pose,
1751                g1: &dyn Shape,
1752                g2: &dyn Shape,
1753            ) -> Result<ShapeDistance, Unsupported> {
1754                DefaultQueryDispatcher.distance(pos12, g1, g2)
1755            }
1756
1757            fn contact(
1758                &self,
1759                pos12: &Pose,
1760                g1: &dyn Shape,
1761                g2: &dyn Shape,
1762                prediction: Real,
1763            ) -> Result<Option<Contact>, Unsupported> {
1764                DefaultQueryDispatcher.contact(pos12, g1, g2, prediction)
1765            }
1766
1767            fn closest_points(
1768                &self,
1769                pos12: &Pose,
1770                g1: &dyn Shape,
1771                g2: &dyn Shape,
1772                max_dist: Real,
1773            ) -> Result<ClosestPoints, Unsupported> {
1774                DefaultQueryDispatcher.closest_points(pos12, g1, g2, max_dist)
1775            }
1776
1777            fn cast_shapes(
1778                &self,
1779                pos12: &Pose,
1780                local_vel12: Vector,
1781                g1: &dyn Shape,
1782                g2: &dyn Shape,
1783                options: ShapeCastOptions,
1784            ) -> Result<Option<ShapeCastHit>, Unsupported> {
1785                self.0.fetch_add(1, Ordering::Relaxed);
1786                DefaultQueryDispatcher.cast_shapes(pos12, local_vel12, g1, g2, options)
1787            }
1788
1789            fn cast_shapes_nonlinear(
1790                &self,
1791                motion1: &NonlinearRigidMotion,
1792                g1: &dyn Shape,
1793                motion2: &NonlinearRigidMotion,
1794                g2: &dyn Shape,
1795                start_time: Real,
1796                end_time: Real,
1797                stop_at_penetration: bool,
1798            ) -> Result<Option<ShapeCastHit>, Unsupported> {
1799                DefaultQueryDispatcher.cast_shapes_nonlinear(
1800                    motion1,
1801                    g1,
1802                    motion2,
1803                    g2,
1804                    start_time,
1805                    end_time,
1806                    stop_at_penetration,
1807                )
1808            }
1809        }
1810
1811        impl PersistentQueryDispatcher<ContactManifoldData, ContactData> for CountingDispatcher {
1812            fn contact_manifolds(
1813                &self,
1814                pos12: &Pose,
1815                g1: &dyn Shape,
1816                g2: &dyn Shape,
1817                prediction: Real,
1818                manifolds: &mut Vec<ContactManifold<ContactManifoldData, ContactData>>,
1819                workspace: &mut Option<ContactManifoldsWorkspace>,
1820            ) -> Result<(), Unsupported> {
1821                DefaultQueryDispatcher
1822                    .contact_manifolds(pos12, g1, g2, prediction, manifolds, workspace)
1823            }
1824
1825            fn contact_manifold_convex_convex(
1826                &self,
1827                pos12: &Pose,
1828                g1: &dyn Shape,
1829                g2: &dyn Shape,
1830                normal_constraints1: Option<&dyn NormalConstraints>,
1831                normal_constraints2: Option<&dyn NormalConstraints>,
1832                prediction: Real,
1833                manifold: &mut ContactManifold<ContactManifoldData, ContactData>,
1834            ) -> Result<(), Unsupported> {
1835                DefaultQueryDispatcher.contact_manifold_convex_convex(
1836                    pos12,
1837                    g1,
1838                    g2,
1839                    normal_constraints1,
1840                    normal_constraints2,
1841                    prediction,
1842                    manifold,
1843                )
1844            }
1845        }
1846    }
1847
1848    #[test]
1849    fn scene_queries_use_the_context_query_dispatcher() {
1850        let mut app = test_app();
1851        let casts = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
1852        let dispatcher = CountingDispatcher(casts.clone());
1853        app.world_mut()
1854            .run_system_once(move |mut context: WriteRapierContext| {
1855                context
1856                    .single_mut()
1857                    .unwrap()
1858                    .simulation
1859                    .set_query_dispatcher(CountingDispatcher(dispatcher.0.clone()));
1860            })
1861            .unwrap();
1862        let ball = app
1863            .world_mut()
1864            .spawn((transform_at(0.0, 0.0), Collider::ball(0.5)))
1865            .id();
1866        app.update();
1867        app.update();
1868
1869        let casts_before = casts.load(std::sync::atomic::Ordering::Relaxed);
1870        let hit = run(&mut app, |context| {
1871            let shape = Collider::ball(0.5);
1872            context.cast_shape(
1873                v(-5.0, 0.0),
1874                Rot::default(),
1875                v(1.0, 0.0),
1876                &*shape.raw,
1877                ShapeCastOptions::default(),
1878                QueryFilter::default(),
1879            )
1880        });
1881        assert_eq!(hit.map(|(e, _)| e), Some(ball));
1882        assert!(casts.load(std::sync::atomic::Ordering::Relaxed) > casts_before);
1883
1884        // The character-controller shortcut goes through the same dispatcher.
1885        let casts_before = casts.load(std::sync::atomic::Ordering::Relaxed);
1886        app.world_mut()
1887            .run_system_once(|mut context: WriteRapierContext| {
1888                let shape = Collider::ball(0.5);
1889                context.single_mut().unwrap().move_shape(
1890                    v(8.0, 0.0),
1891                    &*shape.raw,
1892                    v(-5.0, 0.0),
1893                    Rot::default(),
1894                    1.0,
1895                    &Default::default(),
1896                    QueryFilter::default(),
1897                    |_| {},
1898                );
1899            })
1900            .unwrap();
1901        assert!(casts.load(std::sync::atomic::Ordering::Relaxed) > casts_before);
1902    }
1903}