rapier3d/dynamics/
rigid_body_components.rs

1#[cfg(doc)]
2use super::IntegrationParameters;
3use crate::control::PdErrors;
4#[cfg(doc)]
5use crate::control::PidController;
6use crate::dynamics::MassProperties;
7use crate::geometry::{
8    ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
9    ColliderSet, ColliderShape, ModifiedColliders,
10};
11use crate::math::{
12    AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector,
13};
14use crate::utils::{SimdAngularInertia, SimdCross, SimdDot, SimdRealCopy};
15use num::Zero;
16
17/// The unique handle of a rigid body added to a `RigidBodySet`.
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
19#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
20#[repr(transparent)]
21pub struct RigidBodyHandle(pub crate::data::arena::Index);
22
23impl RigidBodyHandle {
24    /// Converts this handle into its (index, generation) components.
25    pub fn into_raw_parts(self) -> (u32, u32) {
26        self.0.into_raw_parts()
27    }
28
29    /// Reconstructs an handle from its (index, generation) components.
30    pub fn from_raw_parts(id: u32, generation: u32) -> Self {
31        Self(crate::data::arena::Index::from_raw_parts(id, generation))
32    }
33
34    /// An always-invalid rigid-body handle.
35    pub fn invalid() -> Self {
36        Self(crate::data::arena::Index::from_raw_parts(
37            crate::INVALID_U32,
38            crate::INVALID_U32,
39        ))
40    }
41}
42
43/// The type of a body, governing the way it is affected by external forces.
44#[deprecated(note = "renamed as RigidBodyType")]
45pub type BodyStatus = RigidBodyType;
46
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
49/// The type of a rigid body, determining how it responds to forces and movement.
50pub enum RigidBodyType {
51    /// Fully simulated - responds to forces, gravity, and collisions.
52    ///
53    /// Use for: Falling objects, projectiles, physics-based characters, anything that should
54    /// behave realistically under physics simulation.
55    Dynamic = 0,
56
57    /// Never moves - has infinite mass and is unaffected by anything.
58    ///
59    /// Use for: Static level geometry, walls, floors, terrain, buildings.
60    Fixed = 1,
61
62    /// Controlled by setting next position - pushes but isn't pushed.
63    ///
64    /// You control this by setting where it should be next frame. Rapier computes the
65    /// velocity needed to get there. The body can push dynamic bodies but nothing can
66    /// push it back (one-way interaction).
67    ///
68    /// Use for: Animated platforms, objects controlled by external animation systems.
69    KinematicPositionBased = 2,
70
71    /// Controlled by setting velocity - pushes but isn't pushed.
72    ///
73    /// You control this by setting its velocity directly. It moves predictably regardless
74    /// of what it hits. Can push dynamic bodies but nothing can push it back (one-way interaction).
75    ///
76    /// Use for: Moving platforms, elevators, doors, player-controlled characters (when you want
77    /// direct control rather than physics-based movement).
78    KinematicVelocityBased = 3,
79    // Semikinematic, // A kinematic that performs automatic CCD with the fixed environment to avoid traversing it?
80    // Disabled,
81}
82
83impl RigidBodyType {
84    /// Is this rigid-body fixed (i.e. cannot move)?
85    pub fn is_fixed(self) -> bool {
86        self == RigidBodyType::Fixed
87    }
88
89    /// Is this rigid-body dynamic (i.e. can move and be affected by forces)?
90    pub fn is_dynamic(self) -> bool {
91        self == RigidBodyType::Dynamic
92    }
93
94    /// Is this rigid-body kinematic (i.e. can move but is unaffected by forces)?
95    pub fn is_kinematic(self) -> bool {
96        self == RigidBodyType::KinematicPositionBased
97            || self == RigidBodyType::KinematicVelocityBased
98    }
99
100    /// Is this rigid-body a dynamic rigid-body or a kinematic rigid-body?
101    ///
102    /// This method is mostly convenient internally where kinematic and dynamic rigid-body
103    /// are subject to the same behavior.
104    pub fn is_dynamic_or_kinematic(self) -> bool {
105        self != RigidBodyType::Fixed
106    }
107}
108
109bitflags::bitflags! {
110    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
111    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
112    /// Flags describing how the rigid-body has been modified by the user.
113    pub struct RigidBodyChanges: u32 {
114        /// Flag indicating that this rigid-body is in the modified rigid-body set.
115        const IN_MODIFIED_SET = 1 << 0;
116        /// Flag indicating that the `RigidBodyPosition` component of this rigid-body has been modified.
117        const POSITION    = 1 << 1;
118        /// Flag indicating that the `RigidBodyActivation` component of this rigid-body has been modified.
119        const SLEEP       = 1 << 2;
120        /// Flag indicating that the `RigidBodyColliders` component of this rigid-body has been modified.
121        const COLLIDERS   = 1 << 3;
122        /// Flag indicating that the `RigidBodyType` component of this rigid-body has been modified.
123        const TYPE        = 1 << 4;
124        /// Flag indicating that the `RigidBodyDominance` component of this rigid-body has been modified.
125        const DOMINANCE   = 1 << 5;
126        /// Flag indicating that the local mass-properties of this rigid-body must be recomputed.
127        const LOCAL_MASS_PROPERTIES = 1 << 6;
128        /// Flag indicating that the rigid-body was enabled or disabled.
129        const ENABLED_OR_DISABLED = 1 << 7;
130    }
131}
132
133impl Default for RigidBodyChanges {
134    fn default() -> Self {
135        RigidBodyChanges::empty()
136    }
137}
138
139#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
140#[derive(Clone, Debug, Copy, PartialEq)]
141/// The position of this rigid-body.
142pub struct RigidBodyPosition {
143    /// The world-space position of the rigid-body.
144    pub position: Isometry<Real>,
145    /// The next position of the rigid-body.
146    ///
147    /// At the beginning of the timestep, and when the
148    /// timestep is complete we must have position == next_position
149    /// except for position-based kinematic bodies.
150    ///
151    /// The next_position is updated after the velocity and position
152    /// resolution. Then it is either validated (ie. we set position := set_position)
153    /// or clamped by CCD.
154    pub next_position: Isometry<Real>,
155}
156
157impl Default for RigidBodyPosition {
158    fn default() -> Self {
159        Self {
160            position: Isometry::identity(),
161            next_position: Isometry::identity(),
162        }
163    }
164}
165
166impl RigidBodyPosition {
167    /// Computes the velocity need to travel from `self.position` to `self.next_position` in
168    /// a time equal to `1.0 / inv_dt`.
169    #[must_use]
170    pub fn interpolate_velocity(
171        &self,
172        inv_dt: Real,
173        local_com: &Point<Real>,
174    ) -> RigidBodyVelocity<Real> {
175        let pose_err = self.pose_errors(local_com);
176        RigidBodyVelocity {
177            linvel: pose_err.linear * inv_dt,
178            angvel: pose_err.angular * inv_dt,
179        }
180    }
181
182    /// Compute new positions after integrating the given forces and velocities.
183    ///
184    /// This uses a symplectic Euler integration scheme.
185    #[must_use]
186    pub fn integrate_forces_and_velocities(
187        &self,
188        dt: Real,
189        forces: &RigidBodyForces,
190        vels: &RigidBodyVelocity<Real>,
191        mprops: &RigidBodyMassProps,
192    ) -> Isometry<Real> {
193        let new_vels = forces.integrate(dt, vels, mprops);
194        new_vels.integrate(dt, &self.position, &mprops.local_mprops.local_com)
195    }
196
197    /// Computes the difference between [`Self::next_position`] and [`Self::position`].
198    ///
199    /// This error measure can for example be used for interpolating the velocity between two poses,
200    /// or be given to the [`PidController`].
201    ///
202    /// Note that interpolating the velocity can be done more conveniently with
203    /// [`Self::interpolate_velocity`].
204    pub fn pose_errors(&self, local_com: &Point<Real>) -> PdErrors {
205        let com = self.position * local_com;
206        let shift = Translation::from(com.coords);
207        let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
208
209        let angular;
210        #[cfg(feature = "dim2")]
211        {
212            angular = dpos.rotation.angle();
213        }
214        #[cfg(feature = "dim3")]
215        {
216            angular = dpos.rotation.scaled_axis();
217        }
218        let linear = dpos.translation.vector;
219
220        PdErrors { linear, angular }
221    }
222}
223
224impl<T> From<T> for RigidBodyPosition
225where
226    Isometry<Real>: From<T>,
227{
228    fn from(position: T) -> Self {
229        let position = position.into();
230        Self {
231            position,
232            next_position: position,
233        }
234    }
235}
236
237bitflags::bitflags! {
238    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
239    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
240    /// Flags affecting the behavior of the constraints solver for a given contact manifold.
241    pub struct AxesMask: u8 {
242        /// The translational X axis.
243        const LIN_X = 1 << 0;
244        /// The translational Y axis.
245        const LIN_Y = 1 << 1;
246        /// The translational Z axis.
247        #[cfg(feature = "dim3")]
248        const LIN_Z = 1 << 2;
249        /// The rotational X axis.
250        #[cfg(feature = "dim3")]
251        const ANG_X = 1 << 3;
252        /// The rotational Y axis.
253        #[cfg(feature = "dim3")]
254        const ANG_Y = 1 << 4;
255        /// The rotational Z axis.
256        const ANG_Z = 1 << 5;
257    }
258}
259
260impl Default for AxesMask {
261    fn default() -> Self {
262        AxesMask::empty()
263    }
264}
265
266bitflags::bitflags! {
267    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
268    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
269    /// Flags that lock specific movement axes to prevent translation or rotation.
270    ///
271    /// Use this to constrain body movement to specific directions/axes. Common uses:
272    /// - **2D games in 3D**: Lock Z translation and X/Y rotation to keep everything in the XY plane
273    /// - **Upright characters**: Lock rotations to prevent tipping over
274    /// - **Sliding objects**: Lock rotation while allowing translation
275    /// - **Spinning objects**: Lock translation while allowing rotation
276    ///
277    /// # Example
278    /// ```
279    /// # use rapier3d::prelude::*;
280    /// # let mut bodies = RigidBodySet::new();
281    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
282    /// # let body = bodies.get_mut(body_handle).unwrap();
283    /// // Character that can't tip over (rotation locked, but can move)
284    /// body.set_locked_axes(LockedAxes::ROTATION_LOCKED, true);
285    ///
286    /// // Object that slides but doesn't rotate
287    /// body.set_locked_axes(LockedAxes::ROTATION_LOCKED, true);
288    ///
289    /// // 2D game in 3D engine (lock Z movement and X/Y rotation)
290    /// body.set_locked_axes(
291    ///     LockedAxes::TRANSLATION_LOCKED_Z |
292    ///     LockedAxes::ROTATION_LOCKED_X |
293    ///     LockedAxes::ROTATION_LOCKED_Y,
294    ///     true
295    /// );
296    /// ```
297    pub struct LockedAxes: u8 {
298        /// Prevents movement along the X axis.
299        const TRANSLATION_LOCKED_X = 1 << 0;
300        /// Prevents movement along the Y axis.
301        const TRANSLATION_LOCKED_Y = 1 << 1;
302        /// Prevents movement along the Z axis.
303        const TRANSLATION_LOCKED_Z = 1 << 2;
304        /// Prevents all translational movement.
305        const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
306        /// Prevents rotation around the X axis.
307        const ROTATION_LOCKED_X = 1 << 3;
308        /// Prevents rotation around the Y axis.
309        const ROTATION_LOCKED_Y = 1 << 4;
310        /// Prevents rotation around the Z axis.
311        const ROTATION_LOCKED_Z = 1 << 5;
312        /// Prevents all rotational movement.
313        const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
314    }
315}
316
317/// Mass and angular inertia added to a rigid-body on top of its attached colliders’ contributions.
318#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
319#[derive(Copy, Clone, Debug, PartialEq)]
320pub enum RigidBodyAdditionalMassProps {
321    /// Mass properties to be added as-is.
322    MassProps(MassProperties),
323    /// Mass to be added to the rigid-body. This will also automatically scale
324    /// the attached colliders total angular inertia to account for the added mass.
325    Mass(Real),
326}
327
328impl Default for RigidBodyAdditionalMassProps {
329    fn default() -> Self {
330        RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
331    }
332}
333
334#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
335#[derive(Clone, Debug, PartialEq)]
336// #[repr(C)]
337/// The mass properties of a rigid-body.
338pub struct RigidBodyMassProps {
339    /// The world-space center of mass of the rigid-body.
340    pub world_com: Point<Real>,
341    /// The inverse mass taking into account translation locking.
342    pub effective_inv_mass: Vector<Real>,
343    /// The square-root of the world-space inverse angular inertia tensor of the rigid-body,
344    /// taking into account rotation locking.
345    pub effective_world_inv_inertia: AngularInertia<Real>,
346    /// The local mass properties of the rigid-body.
347    pub local_mprops: MassProperties,
348    /// Flags for locking rotation and translation.
349    pub flags: LockedAxes,
350    /// Mass-properties of this rigid-bodies, added to the contributions of its attached colliders.
351    pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
352}
353
354impl Default for RigidBodyMassProps {
355    fn default() -> Self {
356        Self {
357            flags: LockedAxes::empty(),
358            local_mprops: MassProperties::zero(),
359            additional_local_mprops: None,
360            world_com: Point::origin(),
361            effective_inv_mass: Vector::zero(),
362            effective_world_inv_inertia: AngularInertia::zero(),
363        }
364    }
365}
366
367impl From<LockedAxes> for RigidBodyMassProps {
368    fn from(flags: LockedAxes) -> Self {
369        Self {
370            flags,
371            ..Self::default()
372        }
373    }
374}
375
376impl From<MassProperties> for RigidBodyMassProps {
377    fn from(local_mprops: MassProperties) -> Self {
378        Self {
379            local_mprops,
380            ..Default::default()
381        }
382    }
383}
384
385impl RigidBodyMassProps {
386    /// The mass of the rigid-body.
387    #[must_use]
388    pub fn mass(&self) -> Real {
389        crate::utils::inv(self.local_mprops.inv_mass)
390    }
391
392    /// The effective mass (that takes the potential translation locking into account) of
393    /// this rigid-body.
394    #[must_use]
395    pub fn effective_mass(&self) -> Vector<Real> {
396        self.effective_inv_mass.map(crate::utils::inv)
397    }
398
399    /// The square root of the effective world-space angular inertia (that takes the potential rotation locking into account) of
400    /// this rigid-body.
401    #[must_use]
402    pub fn effective_angular_inertia(&self) -> AngularInertia<Real> {
403        #[allow(unused_mut)] // mut needed in 3D.
404        let mut ang_inertia = self.effective_world_inv_inertia;
405
406        // Make the matrix invertible.
407        #[cfg(feature = "dim3")]
408        {
409            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
410                ang_inertia.m11 = 1.0;
411            }
412            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
413                ang_inertia.m22 = 1.0;
414            }
415            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
416                ang_inertia.m33 = 1.0;
417            }
418        }
419
420        #[allow(unused_mut)] // mut needed in 3D.
421        let mut result = ang_inertia.inverse();
422
423        // Remove the locked axes again.
424        #[cfg(feature = "dim3")]
425        {
426            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
427                result.m11 = 0.0;
428            }
429            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
430                result.m22 = 0.0;
431            }
432            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
433                result.m33 = 0.0;
434            }
435        }
436
437        result
438    }
439
440    /// Recompute the mass-properties of this rigid-bodies based on its currently attached colliders.
441    pub fn recompute_mass_properties_from_colliders(
442        &mut self,
443        colliders: &ColliderSet,
444        attached_colliders: &RigidBodyColliders,
445        body_type: RigidBodyType,
446        position: &Isometry<Real>,
447    ) {
448        let added_mprops = self
449            .additional_local_mprops
450            .as_ref()
451            .map(|mprops| **mprops)
452            .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
453
454        self.local_mprops = MassProperties::default();
455
456        for handle in &attached_colliders.0 {
457            if let Some(co) = colliders.get(*handle) {
458                if co.is_enabled() {
459                    if let Some(co_parent) = co.parent {
460                        let to_add = co
461                            .mprops
462                            .mass_properties(&*co.shape)
463                            .transform_by(&co_parent.pos_wrt_parent);
464                        self.local_mprops += to_add;
465                    }
466                }
467            }
468        }
469
470        match added_mprops {
471            RigidBodyAdditionalMassProps::MassProps(mprops) => {
472                self.local_mprops += mprops;
473            }
474            RigidBodyAdditionalMassProps::Mass(mass) => {
475                let new_mass = self.local_mprops.mass() + mass;
476                self.local_mprops.set_mass(new_mass, true);
477            }
478        }
479
480        self.update_world_mass_properties(body_type, position);
481    }
482
483    /// Update the world-space mass properties of `self`, taking into account the new position.
484    pub fn update_world_mass_properties(
485        &mut self,
486        body_type: RigidBodyType,
487        position: &Isometry<Real>,
488    ) {
489        self.world_com = self.local_mprops.world_com(position);
490        self.effective_inv_mass = Vector::repeat(self.local_mprops.inv_mass);
491        self.effective_world_inv_inertia = self.local_mprops.world_inv_inertia(&position.rotation);
492
493        // Take into account translation/rotation locking.
494        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
495            self.effective_inv_mass.x = 0.0;
496        }
497
498        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
499            self.effective_inv_mass.y = 0.0;
500        }
501
502        #[cfg(feature = "dim3")]
503        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
504            self.effective_inv_mass.z = 0.0;
505        }
506
507        #[cfg(feature = "dim2")]
508        {
509            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
510                self.effective_world_inv_inertia = 0.0;
511            }
512        }
513        #[cfg(feature = "dim3")]
514        {
515            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
516                self.effective_world_inv_inertia.m11 = 0.0;
517                self.effective_world_inv_inertia.m12 = 0.0;
518                self.effective_world_inv_inertia.m13 = 0.0;
519            }
520
521            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
522                self.effective_world_inv_inertia.m22 = 0.0;
523                self.effective_world_inv_inertia.m12 = 0.0;
524                self.effective_world_inv_inertia.m23 = 0.0;
525            }
526            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
527                self.effective_world_inv_inertia.m33 = 0.0;
528                self.effective_world_inv_inertia.m13 = 0.0;
529                self.effective_world_inv_inertia.m23 = 0.0;
530            }
531        }
532    }
533}
534
535#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
536#[derive(Clone, Debug, Copy, PartialEq)]
537/// The velocities of this rigid-body.
538pub struct RigidBodyVelocity<T: SimdRealCopy> {
539    /// The linear velocity of the rigid-body.
540    pub linvel: Vector<T>,
541    /// The angular velocity of the rigid-body.
542    pub angvel: AngVector<T>,
543}
544
545impl Default for RigidBodyVelocity<Real> {
546    fn default() -> Self {
547        Self::zero()
548    }
549}
550
551impl RigidBodyVelocity<Real> {
552    /// Create a new rigid-body velocity component.
553    #[must_use]
554    pub fn new(linvel: Vector<Real>, angvel: AngVector<Real>) -> Self {
555        Self { linvel, angvel }
556    }
557
558    /// Converts a slice to a rigid-body velocity.
559    ///
560    /// The slice must contain at least 3 elements: the `slice[0..2]` contains
561    /// the linear velocity and the `slice[2]` contains the angular velocity.
562    #[must_use]
563    #[cfg(feature = "dim2")]
564    pub fn from_slice(slice: &[Real]) -> Self {
565        Self {
566            linvel: Vector::new(slice[0], slice[1]),
567            angvel: slice[2],
568        }
569    }
570
571    /// Converts a slice to a rigid-body velocity.
572    ///
573    /// The slice must contain at least 6 elements: the `slice[0..3]` contains
574    /// the linear velocity and the `slice[3..6]` contains the angular velocity.
575    #[must_use]
576    #[cfg(feature = "dim3")]
577    pub fn from_slice(slice: &[Real]) -> Self {
578        Self {
579            linvel: Vector::new(slice[0], slice[1], slice[2]),
580            angvel: AngVector::new(slice[3], slice[4], slice[5]),
581        }
582    }
583
584    /// Velocities set to zero.
585    #[must_use]
586    pub fn zero() -> Self {
587        Self {
588            linvel: na::zero(),
589            angvel: na::zero(),
590        }
591    }
592
593    /// This velocity seen as a slice.
594    ///
595    /// The linear part is stored first.
596    #[inline]
597    pub fn as_slice(&self) -> &[Real] {
598        self.as_vector().as_slice()
599    }
600
601    /// This velocity seen as a mutable slice.
602    ///
603    /// The linear part is stored first.
604    #[inline]
605    pub fn as_mut_slice(&mut self) -> &mut [Real] {
606        self.as_vector_mut().as_mut_slice()
607    }
608
609    /// This velocity seen as a vector.
610    ///
611    /// The linear part is stored first.
612    #[inline]
613    #[cfg(feature = "dim2")]
614    pub fn as_vector(&self) -> &na::Vector3<Real> {
615        unsafe { std::mem::transmute(self) }
616    }
617
618    /// This velocity seen as a mutable vector.
619    ///
620    /// The linear part is stored first.
621    #[inline]
622    #[cfg(feature = "dim2")]
623    pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
624        unsafe { std::mem::transmute(self) }
625    }
626
627    /// This velocity seen as a vector.
628    ///
629    /// The linear part is stored first.
630    #[inline]
631    #[cfg(feature = "dim3")]
632    pub fn as_vector(&self) -> &na::Vector6<Real> {
633        unsafe { std::mem::transmute(self) }
634    }
635
636    /// This velocity seen as a mutable vector.
637    ///
638    /// The linear part is stored first.
639    #[inline]
640    #[cfg(feature = "dim3")]
641    pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
642        unsafe { std::mem::transmute(self) }
643    }
644
645    /// Return `self` rotated by `rotation`.
646    #[must_use]
647    pub fn transformed(self, rotation: &Rotation<Real>) -> Self {
648        Self {
649            linvel: rotation * self.linvel,
650            #[cfg(feature = "dim2")]
651            angvel: self.angvel,
652            #[cfg(feature = "dim3")]
653            angvel: rotation * self.angvel,
654        }
655    }
656
657    /// The approximate kinetic energy of this rigid-body.
658    ///
659    /// This approximation does not take the rigid-body's mass and angular inertia
660    /// into account. Some physics engines call this the "mass-normalized kinetic
661    /// energy".
662    #[must_use]
663    pub fn pseudo_kinetic_energy(&self) -> Real {
664        0.5 * (self.linvel.norm_squared() + self.angvel.gdot(self.angvel))
665    }
666
667    /// The velocity of the given world-space point on this rigid-body.
668    #[must_use]
669    pub fn velocity_at_point(&self, point: &Point<Real>, world_com: &Point<Real>) -> Vector<Real> {
670        let dpt = point - world_com;
671        self.linvel + self.angvel.gcross(dpt)
672    }
673
674    /// Are these velocities exactly equal to zero?
675    #[must_use]
676    pub fn is_zero(&self) -> bool {
677        self.linvel.is_zero() && self.angvel.is_zero()
678    }
679
680    /// The kinetic energy of this rigid-body.
681    #[must_use]
682    #[profiling::function]
683    pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
684        let mut energy = (rb_mprops.mass() * self.linvel.norm_squared()) / 2.0;
685
686        #[cfg(feature = "dim2")]
687        if !rb_mprops.effective_world_inv_inertia.is_zero() {
688            let inertia = 1.0 / rb_mprops.effective_world_inv_inertia;
689            energy += inertia * self.angvel * self.angvel / 2.0;
690        }
691
692        #[cfg(feature = "dim3")]
693        if !rb_mprops.effective_world_inv_inertia.is_zero() {
694            let inertia = rb_mprops.effective_world_inv_inertia.inverse_unchecked();
695            energy += self.angvel.dot(&(inertia * self.angvel)) / 2.0;
696        }
697
698        energy
699    }
700
701    /// Applies an impulse at the center-of-mass of this rigid-body.
702    /// The impulse is applied right away, changing the linear velocity.
703    /// This does nothing on non-dynamic bodies.
704    pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector<Real>) {
705        self.linvel += impulse.component_mul(&rb_mprops.effective_inv_mass);
706    }
707
708    /// Applies an angular impulse at the center-of-mass of this rigid-body.
709    /// The impulse is applied right away, changing the angular velocity.
710    /// This does nothing on non-dynamic bodies.
711    #[cfg(feature = "dim2")]
712    pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
713        self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
714    }
715
716    /// Applies an angular impulse at the center-of-mass of this rigid-body.
717    /// The impulse is applied right away, changing the angular velocity.
718    /// This does nothing on non-dynamic bodies.
719    #[cfg(feature = "dim3")]
720    pub fn apply_torque_impulse(
721        &mut self,
722        rb_mprops: &RigidBodyMassProps,
723        torque_impulse: Vector<Real>,
724    ) {
725        self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
726    }
727
728    /// Applies an impulse at the given world-space point of this rigid-body.
729    /// The impulse is applied right away, changing the linear and/or angular velocities.
730    /// This does nothing on non-dynamic bodies.
731    pub fn apply_impulse_at_point(
732        &mut self,
733        rb_mprops: &RigidBodyMassProps,
734        impulse: Vector<Real>,
735        point: Point<Real>,
736    ) {
737        let torque_impulse = (point - rb_mprops.world_com).gcross(impulse);
738        self.apply_impulse(rb_mprops, impulse);
739        self.apply_torque_impulse(rb_mprops, torque_impulse);
740    }
741}
742
743impl<T: SimdRealCopy> RigidBodyVelocity<T> {
744    /// Returns the update velocities after applying the given damping.
745    #[must_use]
746    pub fn apply_damping(&self, dt: T, damping: &RigidBodyDamping<T>) -> Self {
747        let one = T::one();
748        RigidBodyVelocity {
749            linvel: self.linvel * (one / (one + dt * damping.linear_damping)),
750            angvel: self.angvel * (one / (one + dt * damping.angular_damping)),
751        }
752    }
753
754    /// Integrate the velocities in `self` to compute obtain new positions when moving from the given
755    /// initial position `init_pos`.
756    #[must_use]
757    #[inline]
758    pub fn integrate(&self, dt: T, init_pos: &Isometry<T>, local_com: &Point<T>) -> Isometry<T> {
759        let com = init_pos * local_com;
760        let shift = Translation::from(com.coords);
761        let mut result =
762            shift * Isometry::new(self.linvel * dt, self.angvel * dt) * shift.inverse() * init_pos;
763        result.rotation.renormalize_fast();
764        result
765    }
766
767    /// Same as [`Self::integrate`] but with a local center-of-mass assumed to be zero.
768    #[must_use]
769    #[inline]
770    pub fn integrate_centered(&self, dt: T, mut pose: Isometry<T>) -> Isometry<T> {
771        pose.translation.vector += self.linvel * dt;
772        pose.rotation = Rotation::new(self.angvel * dt) * pose.rotation;
773        pose.rotation.renormalize_fast();
774        pose
775    }
776
777    /// Same as [`Self::integrate`] but with the angular part linearized and the local
778    /// center-of-mass assumed to be zero.
779    #[inline]
780    #[cfg(feature = "dim2")]
781    pub fn integrate_linearized(&self, dt: T, pose: &mut Isometry<T>) {
782        let dang = self.angvel * dt;
783        let new_cos = pose.rotation.re - dang * pose.rotation.im;
784        let new_sin = pose.rotation.im + dang * pose.rotation.re;
785        pose.rotation = Rotation::from_cos_sin_unchecked(new_cos, new_sin);
786        // NOTE: don’t use renormalize_fast since the linearization might cause more drift.
787        // TODO: check for zeros?
788        pose.rotation.renormalize();
789        pose.translation.vector += self.linvel * dt;
790    }
791
792    /// Same as [`Self::integrate`] but with the angular part linearized and the local
793    /// center-of-mass assumed to be zero.
794    #[inline]
795    #[cfg(feature = "dim3")]
796    pub fn integrate_linearized(&self, dt: T, pose: &mut Isometry<T>) {
797        // Rotations linearization is inspired from
798        // https://ahrs.readthedocs.io/en/latest/filters/angular.html (not using the matrix form).
799        let hang = self.angvel * (dt * T::splat(0.5));
800        // Quaternion identity + `hang` seen as a quaternion.
801        let id_plus_hang = na::Quaternion::new(T::one(), hang.x, hang.y, hang.z);
802        pose.rotation = Rotation::new_unchecked(id_plus_hang * pose.rotation.into_inner());
803        // NOTE: don’t use renormalize_fast since the linearization might cause more drift.
804        // TODO: check for zeros?
805        pose.rotation.renormalize();
806        pose.translation.vector += self.linvel * dt;
807    }
808}
809
810impl std::ops::Mul<Real> for RigidBodyVelocity<Real> {
811    type Output = Self;
812
813    fn mul(self, rhs: Real) -> Self {
814        RigidBodyVelocity {
815            linvel: self.linvel * rhs,
816            angvel: self.angvel * rhs,
817        }
818    }
819}
820
821impl std::ops::Add<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
822    type Output = Self;
823
824    fn add(self, rhs: Self) -> Self {
825        RigidBodyVelocity {
826            linvel: self.linvel + rhs.linvel,
827            angvel: self.angvel + rhs.angvel,
828        }
829    }
830}
831
832impl std::ops::AddAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
833    fn add_assign(&mut self, rhs: Self) {
834        self.linvel += rhs.linvel;
835        self.angvel += rhs.angvel;
836    }
837}
838
839impl std::ops::Sub<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
840    type Output = Self;
841
842    fn sub(self, rhs: Self) -> Self {
843        RigidBodyVelocity {
844            linvel: self.linvel - rhs.linvel,
845            angvel: self.angvel - rhs.angvel,
846        }
847    }
848}
849
850impl std::ops::SubAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
851    fn sub_assign(&mut self, rhs: Self) {
852        self.linvel -= rhs.linvel;
853        self.angvel -= rhs.angvel;
854    }
855}
856
857#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
858#[derive(Clone, Debug, Copy, PartialEq)]
859/// Damping factors to progressively slow down a rigid-body.
860pub struct RigidBodyDamping<T> {
861    /// Damping factor for gradually slowing down the translational motion of the rigid-body.
862    pub linear_damping: T,
863    /// Damping factor for gradually slowing down the angular motion of the rigid-body.
864    pub angular_damping: T,
865}
866
867impl<T: SimdRealCopy> Default for RigidBodyDamping<T> {
868    fn default() -> Self {
869        Self {
870            linear_damping: T::zero(),
871            angular_damping: T::zero(),
872        }
873    }
874}
875
876#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
877#[derive(Clone, Debug, Copy, PartialEq)]
878/// The user-defined external forces applied to this rigid-body.
879pub struct RigidBodyForces {
880    /// Accumulation of external forces (only for dynamic bodies).
881    pub force: Vector<Real>,
882    /// Accumulation of external torques (only for dynamic bodies).
883    pub torque: AngVector<Real>,
884    /// Gravity is multiplied by this scaling factor before it's
885    /// applied to this rigid-body.
886    pub gravity_scale: Real,
887    /// Forces applied by the user.
888    pub user_force: Vector<Real>,
889    /// Torque applied by the user.
890    pub user_torque: AngVector<Real>,
891    /// Are gyroscopic forces enabled for this rigid-body?
892    #[cfg(feature = "dim3")]
893    pub gyroscopic_forces_enabled: bool,
894}
895
896impl Default for RigidBodyForces {
897    fn default() -> Self {
898        Self {
899            force: na::zero(),
900            torque: na::zero(),
901            gravity_scale: 1.0,
902            user_force: na::zero(),
903            user_torque: na::zero(),
904            #[cfg(feature = "dim3")]
905            gyroscopic_forces_enabled: false,
906        }
907    }
908}
909
910impl RigidBodyForces {
911    /// Integrate these forces to compute new velocities.
912    #[must_use]
913    pub fn integrate(
914        &self,
915        dt: Real,
916        init_vels: &RigidBodyVelocity<Real>,
917        mprops: &RigidBodyMassProps,
918    ) -> RigidBodyVelocity<Real> {
919        let linear_acc = self.force.component_mul(&mprops.effective_inv_mass);
920        let angular_acc = mprops.effective_world_inv_inertia * self.torque;
921
922        RigidBodyVelocity {
923            linvel: init_vels.linvel + linear_acc * dt,
924            angvel: init_vels.angvel + angular_acc * dt,
925        }
926    }
927
928    /// Adds to `self` the gravitational force that would result in a gravitational acceleration
929    /// equal to `gravity`.
930    pub fn compute_effective_force_and_torque(
931        &mut self,
932        gravity: &Vector<Real>,
933        mass: &Vector<Real>,
934    ) {
935        self.force = self.user_force + gravity.component_mul(mass) * self.gravity_scale;
936        self.torque = self.user_torque;
937    }
938
939    /// Applies a force at the given world-space point of the rigid-body with the given mass properties.
940    pub fn apply_force_at_point(
941        &mut self,
942        rb_mprops: &RigidBodyMassProps,
943        force: Vector<Real>,
944        point: Point<Real>,
945    ) {
946        self.user_force += force;
947        self.user_torque += (point - rb_mprops.world_com).gcross(force);
948    }
949}
950
951#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
952#[derive(Clone, Debug, Copy, PartialEq)]
953/// Information used for Continuous-Collision-Detection.
954pub struct RigidBodyCcd {
955    /// The distance used by the CCD solver to decide if a movement would
956    /// result in a tunnelling problem.
957    pub ccd_thickness: Real,
958    /// The max distance between this rigid-body's center of mass and its
959    /// furthest collider point.
960    pub ccd_max_dist: Real,
961    /// Is CCD active for this rigid-body?
962    ///
963    /// If `self.ccd_enabled` is `true`, then this is automatically set to
964    /// `true` when the CCD solver detects that the rigid-body is moving fast
965    /// enough to potential cause a tunneling problem.
966    pub ccd_active: bool,
967    /// Is CCD enabled for this rigid-body?
968    pub ccd_enabled: bool,
969    /// The soft-CCD prediction distance for this rigid-body.
970    pub soft_ccd_prediction: Real,
971}
972
973impl Default for RigidBodyCcd {
974    fn default() -> Self {
975        Self {
976            ccd_thickness: Real::MAX,
977            ccd_max_dist: 0.0,
978            ccd_active: false,
979            ccd_enabled: false,
980            soft_ccd_prediction: 0.0,
981        }
982    }
983}
984
985impl RigidBodyCcd {
986    /// The maximum velocity any point of any collider attached to this rigid-body
987    /// moving with the given velocity can have.
988    pub fn max_point_velocity(&self, vels: &RigidBodyVelocity<Real>) -> Real {
989        #[cfg(feature = "dim2")]
990        return vels.linvel.norm() + vels.angvel.abs() * self.ccd_max_dist;
991        #[cfg(feature = "dim3")]
992        return vels.linvel.norm() + vels.angvel.norm() * self.ccd_max_dist;
993    }
994
995    /// Is this rigid-body moving fast enough so that it may cause a tunneling problem?
996    pub fn is_moving_fast(
997        &self,
998        dt: Real,
999        vels: &RigidBodyVelocity<Real>,
1000        forces: Option<&RigidBodyForces>,
1001    ) -> bool {
1002        // NOTE: for the threshold we don't use the exact CCD thickness. Theoretically, we
1003        //       should use `self.rb_ccd.ccd_thickness - smallest_contact_dist` where `smallest_contact_dist`
1004        //       is the deepest contact (the contact with the largest penetration depth, i.e., the
1005        //       negative `dist` with the largest absolute value.
1006        //       However, getting this penetration depth assumes querying the contact graph from
1007        //       the narrow-phase, which can be pretty expensive. So we use the CCD thickness
1008        //       divided by 10 right now. We will see in practice if this value is OK or if we
1009        //       should use a smaller (to be less conservative) or larger divisor (to be more conservative).
1010        let threshold = self.ccd_thickness / 10.0;
1011
1012        if let Some(forces) = forces {
1013            let linear_part = (vels.linvel + forces.force * dt).norm();
1014            #[cfg(feature = "dim2")]
1015            let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
1016            #[cfg(feature = "dim3")]
1017            let angular_part = (vels.angvel + forces.torque * dt).norm() * self.ccd_max_dist;
1018            let vel_with_forces = linear_part + angular_part;
1019            vel_with_forces > threshold
1020        } else {
1021            self.max_point_velocity(vels) * dt > threshold
1022        }
1023    }
1024}
1025
1026#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1027#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
1028/// Internal identifiers used by the physics engine.
1029pub struct RigidBodyIds {
1030    pub(crate) active_island_id: usize,
1031    pub(crate) active_set_id: usize,
1032    pub(crate) active_set_offset: u32,
1033    pub(crate) active_set_timestamp: u32,
1034}
1035
1036impl Default for RigidBodyIds {
1037    fn default() -> Self {
1038        Self {
1039            active_island_id: usize::MAX,
1040            active_set_id: usize::MAX,
1041            active_set_offset: u32::MAX,
1042            active_set_timestamp: 0,
1043        }
1044    }
1045}
1046
1047#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1048#[derive(Default, Clone, Debug, PartialEq, Eq)]
1049/// The set of colliders attached to this rigid-bodies.
1050///
1051/// This should not be modified manually unless you really know what
1052/// you are doing (for example if you are trying to integrate Rapier
1053/// to a game engine using its component-based interface).
1054pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
1055
1056impl RigidBodyColliders {
1057    /// Detach a collider from this rigid-body.
1058    pub fn detach_collider(
1059        &mut self,
1060        rb_changes: &mut RigidBodyChanges,
1061        co_handle: ColliderHandle,
1062    ) {
1063        if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
1064            rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1065            self.0.swap_remove(i);
1066        }
1067    }
1068
1069    /// Attach a collider to this rigid-body.
1070    pub fn attach_collider(
1071        &mut self,
1072        rb_type: RigidBodyType,
1073        rb_changes: &mut RigidBodyChanges,
1074        rb_ccd: &mut RigidBodyCcd,
1075        rb_mprops: &mut RigidBodyMassProps,
1076        rb_pos: &RigidBodyPosition,
1077        co_handle: ColliderHandle,
1078        co_pos: &mut ColliderPosition,
1079        co_parent: &ColliderParent,
1080        co_shape: &ColliderShape,
1081        co_mprops: &ColliderMassProps,
1082    ) {
1083        rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1084
1085        co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
1086        rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
1087
1088        let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
1089        rb_ccd.ccd_max_dist = rb_ccd
1090            .ccd_max_dist
1091            .max(shape_bsphere.center.coords.norm() + shape_bsphere.radius);
1092
1093        let mass_properties = co_mprops
1094            .mass_properties(&**co_shape)
1095            .transform_by(&co_parent.pos_wrt_parent);
1096        self.0.push(co_handle);
1097        rb_mprops.local_mprops += mass_properties;
1098        rb_mprops.update_world_mass_properties(rb_type, &rb_pos.position);
1099    }
1100
1101    /// Update the positions of all the colliders attached to this rigid-body.
1102    pub(crate) fn update_positions(
1103        &self,
1104        colliders: &mut ColliderSet,
1105        modified_colliders: &mut ModifiedColliders,
1106        parent_pos: &Isometry<Real>,
1107    ) {
1108        for handle in &self.0 {
1109            // NOTE: the ColliderParent component must exist if we enter this method.
1110            // NOTE: currently, we are propagating the position even if the collider is disabled.
1111            //       Is that the best behavior?
1112            let co = colliders.index_mut_internal(*handle);
1113            let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1114
1115            // Set the modification flag so we can benefit from the modification-tracking
1116            // when updating the narrow-phase/broad-phase afterwards.
1117            modified_colliders.push_once(*handle, co);
1118
1119            co.changes |= ColliderChanges::POSITION;
1120            co.pos = ColliderPosition(new_pos);
1121        }
1122    }
1123}
1124
1125#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1126#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1127/// The dominance groups of a rigid-body.
1128pub struct RigidBodyDominance(pub i8);
1129
1130impl RigidBodyDominance {
1131    /// The actual dominance group of this rigid-body, after taking into account its type.
1132    pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1133        if status.is_dynamic_or_kinematic() {
1134            self.0 as i16
1135        } else {
1136            i8::MAX as i16 + 1
1137        }
1138    }
1139}
1140
1141/// Controls when a body goes to sleep (becomes inactive to save CPU).
1142///
1143/// ## Sleeping System
1144///
1145/// Bodies automatically sleep when they're at rest, dramatically improving performance
1146/// in scenes with many inactive objects. Sleeping bodies are:
1147/// - Excluded from simulation (no collision detection, no velocity integration)
1148/// - Automatically woken when disturbed (hit by moving object, connected via joint)
1149/// - Woken manually with `body.wake_up()` or `islands.wake_up()`
1150///
1151/// ## How sleeping works
1152///
1153/// A body sleeps after its linear AND angular velocities stay below thresholds for
1154/// `time_until_sleep` seconds (default: 2 seconds). Set thresholds to negative to disable sleeping.
1155///
1156/// ## When to disable sleeping
1157///
1158/// Most bodies should sleep! Only disable if the body needs to stay active despite being still:
1159/// - Bodies you frequently query for raycasts/contacts
1160/// - Bodies with time-based behaviors while stationary
1161///
1162/// Use `RigidBodyBuilder::can_sleep(false)` or `RigidBodyActivation::cannot_sleep()`.
1163#[derive(Copy, Clone, Debug, PartialEq)]
1164#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1165pub struct RigidBodyActivation {
1166    /// Linear velocity threshold for sleeping (scaled by `length_unit`).
1167    ///
1168    /// If negative, body never sleeps. Default: 0.4 (in length units/second).
1169    pub normalized_linear_threshold: Real,
1170
1171    /// Angular velocity threshold for sleeping (radians/second).
1172    ///
1173    /// If negative, body never sleeps. Default: 0.5 rad/s.
1174    pub angular_threshold: Real,
1175
1176    /// How long the body must be still before sleeping (seconds).
1177    ///
1178    /// Default: 2.0 seconds. Must be below both velocity thresholds for this duration.
1179    pub time_until_sleep: Real,
1180
1181    /// Internal timer tracking how long body has been still.
1182    pub time_since_can_sleep: Real,
1183
1184    /// Is this body currently sleeping?
1185    pub sleeping: bool,
1186}
1187
1188impl Default for RigidBodyActivation {
1189    fn default() -> Self {
1190        Self::active()
1191    }
1192}
1193
1194impl RigidBodyActivation {
1195    /// The default linear velocity below which a body can be put to sleep.
1196    pub fn default_normalized_linear_threshold() -> Real {
1197        0.4
1198    }
1199
1200    /// The default angular velocity below which a body can be put to sleep.
1201    pub fn default_angular_threshold() -> Real {
1202        0.5
1203    }
1204
1205    /// The amount of time the rigid-body must remain below it’s linear and angular velocity
1206    /// threshold before falling to sleep.
1207    pub fn default_time_until_sleep() -> Real {
1208        2.0
1209    }
1210
1211    /// Create a new rb_activation status initialised with the default rb_activation threshold and is active.
1212    pub fn active() -> Self {
1213        RigidBodyActivation {
1214            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1215            angular_threshold: Self::default_angular_threshold(),
1216            time_until_sleep: Self::default_time_until_sleep(),
1217            time_since_can_sleep: 0.0,
1218            sleeping: false,
1219        }
1220    }
1221
1222    /// Create a new rb_activation status initialised with the default rb_activation threshold and is inactive.
1223    pub fn inactive() -> Self {
1224        RigidBodyActivation {
1225            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1226            angular_threshold: Self::default_angular_threshold(),
1227            time_until_sleep: Self::default_time_until_sleep(),
1228            time_since_can_sleep: Self::default_time_until_sleep(),
1229            sleeping: true,
1230        }
1231    }
1232
1233    /// Create a new activation status that prevents the rigid-body from sleeping.
1234    pub fn cannot_sleep() -> Self {
1235        RigidBodyActivation {
1236            normalized_linear_threshold: -1.0,
1237            angular_threshold: -1.0,
1238            ..Self::active()
1239        }
1240    }
1241
1242    /// Returns `true` if the body is not asleep.
1243    #[inline]
1244    pub fn is_active(&self) -> bool {
1245        !self.sleeping
1246    }
1247
1248    /// Wakes up this rigid-body.
1249    #[inline]
1250    pub fn wake_up(&mut self, strong: bool) {
1251        self.sleeping = false;
1252        if strong {
1253            self.time_since_can_sleep = 0.0;
1254        }
1255    }
1256
1257    /// Put this rigid-body to sleep.
1258    #[inline]
1259    pub fn sleep(&mut self) {
1260        self.sleeping = true;
1261        self.time_since_can_sleep = self.time_until_sleep;
1262    }
1263}
1264
1265#[cfg(test)]
1266mod tests {
1267    use super::*;
1268    use crate::math::Real;
1269
1270    #[test]
1271    fn test_interpolate_velocity() {
1272        // Interpolate and then integrate the velocity to see if
1273        // the end positions match.
1274        #[cfg(feature = "f32")]
1275        let mut rng = oorandom::Rand32::new(0);
1276        #[cfg(feature = "f64")]
1277        let mut rng = oorandom::Rand64::new(0);
1278
1279        for i in -10..=10 {
1280            let mult = i as Real;
1281            let (local_com, curr_pos, next_pos);
1282            #[cfg(feature = "dim2")]
1283            {
1284                local_com = Point::new(rng.rand_float(), rng.rand_float());
1285                curr_pos = Isometry::new(
1286                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1287                    rng.rand_float(),
1288                );
1289                next_pos = Isometry::new(
1290                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1291                    rng.rand_float(),
1292                );
1293            }
1294            #[cfg(feature = "dim3")]
1295            {
1296                local_com = Point::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1297                curr_pos = Isometry::new(
1298                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1299                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1300                );
1301                next_pos = Isometry::new(
1302                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1303                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1304                );
1305            }
1306
1307            let dt = 0.016;
1308            let rb_pos = RigidBodyPosition {
1309                position: curr_pos,
1310                next_position: next_pos,
1311            };
1312            let vel = rb_pos.interpolate_velocity(1.0 / dt, &local_com);
1313            let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1314            approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1315        }
1316    }
1317}