avian2d/dynamics/rigid_body/mass_properties/components/
computed.rs

1#![allow(clippy::unnecessary_cast)]
2
3use crate::prelude::*;
4use bevy::prelude::*;
5use derive_more::From;
6
7use super::{AngularInertia, AngularInertiaError, CenterOfMass, Mass, MassError};
8
9/// The total [mass] computed for a dynamic [rigid body], taking into account
10/// colliders and descendants. Represents resistance to linear acceleration.
11///
12/// The total mass is computed as the sum of the masses of all attached colliders
13/// and the mass of the rigid body entity itself. The mass of an entity is determined
14/// by its [`Mass`] component, or if it is not present, from an attached [`Collider`]
15/// based on its shape and [`ColliderDensity`].
16///
17/// A total mass of zero is a special case, and is interpreted as infinite mass, meaning the rigid body
18/// will not be affected by any forces.
19///
20/// [mass]: https://en.wikipedia.org/wiki/Mass
21/// [rigid body]: RigidBody
22///
23/// # Representation
24///
25/// Internally, the total mass is actually stored as the inverse mass `1.0 / mass`.
26/// This is because most physics calculations operate on the inverse mass, and storing it directly
27/// allows for fewer divisions and guards against division by zero.
28///
29/// When using [`ComputedMass`], you shouldn't need to worry about this internal representation.
30/// The provided constructors and getters abstract away the implementation details.
31///
32/// In terms of performance, the main thing to keep in mind is that [`inverse`](Self::inverse) is a no-op
33/// and [`value`](Self::value) contains a division. When dividing by the mass, it's better to use
34/// `foo * mass.inverse()` than `foo / mass.value()`.
35///
36/// # Related Types
37///
38/// - [`Mass`] can be used to set the mass associated with an individual entity.
39/// - [`ComputedAngularInertia`] stores the total angular inertia of a rigid body, taking into account colliders and descendants.
40/// - [`ComputedCenterOfMass`] stores the total center of mass of a rigid body, taking into account colliders and descendants.
41/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
42///
43/// [`SystemParam`]: bevy::ecs::system::SystemParam
44#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
45#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
47#[reflect(Debug, Component, Default, PartialEq)]
48pub struct ComputedMass {
49    /// The inverse mass.
50    ///
51    /// This is stored as an inverse because most physics calculations
52    /// operate on the inverse mass, and storing it directly allows for
53    /// fewer divisions and guards against division by zero.
54    inverse: Scalar,
55}
56
57impl ComputedMass {
58    /// Infinite mass.
59    pub const INFINITY: Self = Self { inverse: 0.0 };
60
61    /// Creates a new [`ComputedMass`] from the given mass.
62    ///
63    /// # Panics
64    ///
65    /// Panics if the mass is negative or NaN when `debug_assertions` are enabled.
66    #[inline]
67    pub fn new(mass: Scalar) -> Self {
68        Self::from_inverse(mass.recip_or_zero())
69    }
70
71    /// Tries to create a new [`ComputedMass`] from the given mass.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`Err(MassError)`](MassError) if the mass is negative or NaN.
76    #[inline]
77    pub fn try_new(mass: Scalar) -> Result<Self, MassError> {
78        if mass.is_nan() {
79            Err(MassError::NaN)
80        } else if mass < 0.0 {
81            Err(MassError::Negative)
82        } else {
83            Ok(Self::from_inverse(mass.recip_or_zero()))
84        }
85    }
86
87    /// Creates a new [`ComputedMass`] from the given inverse mass.
88    ///
89    /// # Panics
90    ///
91    /// Panics if the inverse mass is negative or NaN when `debug_assertions` are enabled.
92    #[inline]
93    pub fn from_inverse(inverse_mass: Scalar) -> Self {
94        debug_assert!(
95            inverse_mass >= 0.0 && !inverse_mass.is_nan(),
96            "mass must be positive or zero"
97        );
98
99        Self {
100            inverse: inverse_mass,
101        }
102    }
103
104    /// Tries to create a new [`ComputedMass`] from the given inverse mass.
105    ///
106    /// # Errors
107    ///
108    /// Returns [`Err(MassError)`](MassError) if the inverse mass is negative or NaN.
109    pub fn try_from_inverse(inverse_mass: Scalar) -> Result<Self, MassError> {
110        if inverse_mass.is_nan() {
111            Err(MassError::NaN)
112        } else if inverse_mass < 0.0 {
113            Err(MassError::Negative)
114        } else {
115            Ok(Self {
116                inverse: inverse_mass,
117            })
118        }
119    }
120
121    /// Returns the mass. If it is infinite, returns zero.
122    ///
123    /// Note that this involves a division because [`ComputedMass`] internally stores the inverse mass.
124    /// If dividing by the mass, consider using `foo * mass.inverse()` instead of `foo / mass.value()`.
125    #[inline]
126    pub fn value(self) -> Scalar {
127        self.inverse.recip_or_zero()
128    }
129
130    /// Returns the inverse mass.
131    ///
132    /// This is a no-op because [`ComputedMass`] internally stores the inverse mass.
133    #[inline]
134    pub fn inverse(self) -> Scalar {
135        self.inverse
136    }
137
138    /// Sets the mass.
139    #[inline]
140    pub fn set(&mut self, mass: impl Into<ComputedMass>) {
141        *self = mass.into();
142    }
143
144    /// Returns `true` if the mass is neither infinite nor NaN.
145    #[inline]
146    pub fn is_finite(self) -> bool {
147        !self.is_infinite() && !self.is_nan()
148    }
149
150    /// Returns `true` if the mass is positive infinity or negative infinity.
151    #[inline]
152    pub fn is_infinite(self) -> bool {
153        self == Self::INFINITY
154    }
155
156    /// Returns `true` if the mass is NaN.
157    #[inline]
158    pub fn is_nan(self) -> bool {
159        self.inverse.is_nan()
160    }
161}
162
163impl From<Scalar> for ComputedMass {
164    fn from(mass: Scalar) -> Self {
165        Self::new(mass)
166    }
167}
168
169impl From<Mass> for ComputedMass {
170    fn from(mass: Mass) -> Self {
171        ComputedMass::new(mass.0 as Scalar)
172    }
173}
174
175impl From<ComputedMass> for Mass {
176    fn from(mass: ComputedMass) -> Self {
177        Self(mass.value() as f32)
178    }
179}
180
181/// The total [angular inertia] computed for a dynamic [rigid body], taking into account
182/// colliders and descendants. Represents resistance to angular acceleration.
183///
184/// The total angular inertia is computed as the sum of the inertias of all attached colliders
185/// and the angular inertia of the rigid body entity itself. The angular inertia of an entity is determined
186/// by its [`AngularInertia`] component, or if it is not present, from an attached [`Collider`]
187/// based on its shape and mass.
188///
189/// A total angular inertia of zero is a special case, and is interpreted as infinite angular inertia,
190/// meaning the rigid body will not be affected by any torque.
191///
192/// [angular inertia]: https://en.wikipedia.org/wiki/Moment_of_inertia
193/// [rigid body]: RigidBody
194///
195/// # Representation
196///
197/// Internally, the angular inertia is actually stored as the inverse angular inertia `1.0 / angular_inertia`.
198/// This is because most physics calculations operate on the inverse angular inertia, and storing it directly
199/// allows for fewer divisions and guards against division by zero.
200///
201/// When using [`ComputedAngularInertia`], you shouldn't need to worry about this internal representation.
202/// The provided constructors and getters abstract away the implementation details.
203///
204/// In terms of performance, the main thing to keep in mind is that [`inverse`](Self::inverse) is a no-op
205/// and [`value`](Self::value) contains a division. When dividing by the angular inertia, it's better to use
206/// `foo * angular_inertia.inverse()` than `foo / angular_inertia.value()`.
207///
208/// # Related Types
209///
210/// - [`AngularInertia`] can be used to set the angular inertia associated with an individual entity.
211/// - [`ComputedMass`] stores the total mass of a rigid body, taking into account colliders and descendants.
212/// - [`ComputedCenterOfMass`] stores the total center of mass of a rigid body, taking into account colliders and descendants.
213/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
214///
215/// [`SystemParam`]: bevy::ecs::system::SystemParam
216#[cfg(feature = "2d")]
217#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
218#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
219#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
220#[reflect(Debug, Component, Default, PartialEq)]
221#[doc(alias = "ComputedMomentOfInertia")]
222pub struct ComputedAngularInertia {
223    /// The inverse angular inertia.
224    ///
225    /// This is stored as an inverse to minimize the number of divisions
226    /// and to guard against division by zero. Most physics calculations
227    /// use the inverse angular inertia.
228    inverse: Scalar,
229}
230
231#[cfg(feature = "2d")]
232impl ComputedAngularInertia {
233    /// Infinite angular inertia.
234    pub const INFINITY: Self = Self { inverse: 0.0 };
235
236    /// Creates a new [`ComputedAngularInertia`] from the given angular inertia.
237    ///
238    /// # Panics
239    ///
240    /// Panics if the angular inertia is negative or NaN when `debug_assertions` are enabled.
241    #[inline]
242    pub fn new(angular_inertia: Scalar) -> Self {
243        Self::from_inverse(angular_inertia.recip_or_zero())
244    }
245
246    /// Tries to create a new [`ComputedAngularInertia`] from the given angular inertia.
247    ///
248    /// # Errors
249    ///
250    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if the angular inertia is negative or NaN.
251    #[inline]
252    pub fn try_new(angular_inertia: Scalar) -> Result<Self, AngularInertiaError> {
253        if angular_inertia.is_nan() {
254            Err(AngularInertiaError::NaN)
255        } else if angular_inertia < 0.0 {
256            Err(AngularInertiaError::Negative)
257        } else {
258            Ok(Self::from_inverse(angular_inertia.recip_or_zero()))
259        }
260    }
261
262    /// Creates a new [`ComputedAngularInertia`] from the given inverse angular inertia.
263    ///
264    /// # Panics
265    ///
266    /// Panics if the inverse angular inertia is negative or NaN when `debug_assertions` are enabled.
267    #[inline]
268    pub fn from_inverse(inverse_angular_inertia: Scalar) -> Self {
269        debug_assert!(
270            inverse_angular_inertia >= 0.0 && !inverse_angular_inertia.is_nan(),
271            "angular inertia must be positive or zero"
272        );
273
274        Self {
275            inverse: inverse_angular_inertia,
276        }
277    }
278
279    /// Tries to create a new [`ComputedAngularInertia`] from the given inverse angular inertia.
280    ///
281    /// # Errors
282    ///
283    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if the inverse angular inertia is negative or NaN.
284    #[inline]
285    pub fn try_from_inverse(inverse_angular_inertia: Scalar) -> Result<Self, AngularInertiaError> {
286        if inverse_angular_inertia.is_nan() {
287            Err(AngularInertiaError::NaN)
288        } else if inverse_angular_inertia < 0.0 {
289            Err(AngularInertiaError::Negative)
290        } else {
291            Ok(Self {
292                inverse: inverse_angular_inertia,
293            })
294        }
295    }
296
297    /// Returns the angular inertia. If it is infinite, returns zero.
298    ///
299    /// Note that this involves a division because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
300    /// If dividing by the angular inertia, consider using `foo * angular_inertia.inverse()` instead of `foo / angular_inertia.value()`.
301    #[inline]
302    pub fn value(self) -> Scalar {
303        self.inverse.recip_or_zero()
304    }
305
306    /// Returns the inverse angular inertia.
307    ///
308    /// This is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
309    #[inline]
310    pub fn inverse(self) -> Scalar {
311        self.inverse
312    }
313
314    /// Returns a mutable reference to the inverse of the angular inertia.
315    ///
316    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
317    #[inline]
318    pub fn inverse_mut(&mut self) -> &mut Scalar {
319        &mut self.inverse
320    }
321
322    /// Sets the angular inertia.
323    #[inline]
324    pub fn set(&mut self, angular_inertia: impl Into<ComputedAngularInertia>) {
325        *self = angular_inertia.into();
326    }
327
328    /// Computes the angular inertia shifted by the given offset, taking into account the given mass.
329    #[inline]
330    pub fn shifted(&self, mass: Scalar, offset: Vector) -> Scalar {
331        AngularInertia::from(*self).shifted(mass as f32, offset.f32()) as Scalar
332    }
333
334    /// Computes the angular inertia shifted by the given offset, taking into account the given mass.
335    #[inline]
336    pub fn shifted_inverse(&self, mass: Scalar, offset: Vector) -> Scalar {
337        self.shifted(mass, offset).recip_or_zero()
338    }
339
340    /// Returns `true` if the angular inertia is neither infinite nor NaN.
341    #[inline]
342    pub fn is_finite(self) -> bool {
343        !self.is_infinite() && !self.is_nan()
344    }
345
346    /// Returns `true` if the angular inertia is positive infinity or negative infinity.
347    #[inline]
348    pub fn is_infinite(self) -> bool {
349        self == Self::INFINITY
350    }
351
352    /// Returns `true` if the angular inertia is NaN.
353    #[inline]
354    pub fn is_nan(self) -> bool {
355        self.inverse.is_nan()
356    }
357}
358
359#[cfg(feature = "2d")]
360impl From<Scalar> for ComputedAngularInertia {
361    fn from(angular_inertia: Scalar) -> Self {
362        Self::new(angular_inertia)
363    }
364}
365
366#[cfg(feature = "2d")]
367impl From<AngularInertia> for ComputedAngularInertia {
368    fn from(inertia: AngularInertia) -> Self {
369        ComputedAngularInertia::new(inertia.0 as Scalar)
370    }
371}
372
373#[cfg(feature = "2d")]
374impl From<ComputedAngularInertia> for AngularInertia {
375    fn from(inertia: ComputedAngularInertia) -> Self {
376        Self(inertia.value() as f32)
377    }
378}
379
380/// The total local [angular inertia] computed for a dynamic [rigid body] as a 3x3 [tensor] matrix,
381/// taking into account colliders and descendants. Represents resistance to angular acceleration.
382///
383/// The total angular inertia is computed as the sum of the inertias of all attached colliders
384/// and the angular inertia of the rigid body entity itself, taking into account offsets from the center of mass.
385/// The angular inertia of an entity is determined by its [`AngularInertia`] component, or if it is not present,
386/// from an attached [`Collider`] based on its shape and mass.
387///
388/// This is computed in local space, so the object's orientation is not taken into account.
389/// The world-space version can be computed using the associated [`rotated`](Self::rotated) method.
390///
391/// To manually compute the world-space version that takes the body's rotation into account,
392/// use the associated [`rotated`](Self::rotated) method.
393///
394/// A total angular inertia of zero is a special case, and is interpreted as infinite angular inertia,
395/// meaning the rigid body will not be affected by any torques.
396///
397/// [angular inertia]: https://en.wikipedia.org/wiki/Moment_of_inertia
398/// [tensor]: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
399/// [rigid body]: RigidBody
400///
401/// # Representation
402///
403/// Internally, the angular inertia is actually stored as the inverse angular inertia tensor `angular_inertia_matrix.inverse()`.
404/// This is because most physics calculations operate on the inverse angular inertia, and storing it directly
405/// allows for fewer inversions and guards against division by zero.
406///
407/// When using [`ComputedAngularInertia`], you shouldn't need to worry about this internal representation.
408/// The provided constructors and getters abstract away the implementation details.
409///
410/// In terms of performance, the main thing to keep in mind is that [`inverse`](Self::inverse) is a no-op
411/// and [`value`](Self::value) contains an inversion. When multiplying by the inverse angular inertia, it's better to use
412/// `angular_inertia.inverse() * foo` than `angular_inertia.value().inverse() * foo`.
413///
414/// # Related Types
415///
416/// - [`AngularInertia`] can be used to set the local angular inertia associated with an individual entity.
417/// - [`ComputedMass`] stores the total mass of a rigid body, taking into account colliders and descendants.
418/// - [`ComputedCenterOfMass`] stores the total center of mass of a rigid body, taking into account colliders and descendants.
419/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
420///
421/// [`SystemParam`]: bevy::ecs::system::SystemParam
422#[cfg(feature = "3d")]
423#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
424#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
425#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
426#[reflect(Debug, Component, PartialEq)]
427#[doc(alias = "ComputedMomentOfInertia")]
428pub struct ComputedAngularInertia {
429    // TODO: The matrix should be symmetric and positive definite.
430    //       We could add a custom `SymmetricMat3` type to enforce symmetricity and reduce memory usage.
431    inverse: SymmetricMatrix,
432}
433
434impl Default for ComputedAngularInertia {
435    fn default() -> Self {
436        Self::INFINITY
437    }
438}
439
440#[cfg(feature = "3d")]
441impl ComputedAngularInertia {
442    /// Infinite angular inertia.
443    pub const INFINITY: Self = Self {
444        inverse: SymmetricMatrix::ZERO,
445    };
446
447    /// Creates a new [`ComputedAngularInertia`] from the given principal angular inertia.
448    ///
449    /// The principal angular inertia represents the torque needed for a desired angular acceleration
450    /// about the local coordinate axes.
451    ///
452    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
453    ///
454    /// To specify the orientation of the local inertial frame, consider using [`ComputedAngularInertia::new_with_local_frame`].
455    ///
456    /// # Panics
457    ///
458    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
459    #[inline]
460    #[doc(alias = "from_principal_angular_inertia")]
461    pub fn new(principal_angular_inertia: Vector) -> Self {
462        debug_assert!(
463            principal_angular_inertia.cmpge(Vector::ZERO).all()
464                && !principal_angular_inertia.is_nan(),
465            "principal angular inertia must be positive or zero for all axes"
466        );
467
468        Self::from_inverse_tensor(SymmetricMatrix::from_diagonal(
469            principal_angular_inertia.recip_or_zero(),
470        ))
471    }
472
473    /// Tries to create a new [`ComputedAngularInertia`] from the given principal angular inertia.
474    ///
475    /// The principal angular inertia represents the torque needed for a desired angular acceleration
476    /// about the local coordinate axes. To specify the orientation of the local inertial frame,
477    /// consider using [`ComputedAngularInertia::try_new_with_local_frame`].
478    ///
479    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
480    ///
481    /// # Errors
482    ///
483    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
484    #[inline]
485    pub fn try_new(principal_angular_inertia: Vector) -> Result<Self, AngularInertiaError> {
486        if principal_angular_inertia.is_nan() {
487            Err(AngularInertiaError::NaN)
488        } else if !principal_angular_inertia.cmpge(Vector::ZERO).all() {
489            Err(AngularInertiaError::Negative)
490        } else {
491            Ok(Self::from_inverse_tensor(SymmetricMatrix::from_diagonal(
492                principal_angular_inertia.recip_or_zero(),
493            )))
494        }
495    }
496
497    /// Creates a new [`ComputedAngularInertia`] from the given principal angular inertia
498    /// and the orientation of the local inertial frame.
499    ///
500    /// The principal angular inertia represents the torque needed for a desired angular acceleration
501    /// about the local coordinate axes defined by the given `orientation`.
502    ///
503    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
504    ///
505    /// # Panics
506    ///
507    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
508    #[inline]
509    #[doc(alias = "from_principal_angular_inertia_with_local_frame")]
510    pub fn new_with_local_frame(
511        principal_angular_inertia: Vector,
512        orientation: Quaternion,
513    ) -> Self {
514        debug_assert!(
515            principal_angular_inertia.cmpge(Vector::ZERO).all()
516                && !principal_angular_inertia.is_nan(),
517            "principal angular inertia must be positive or zero for all axes"
518        );
519
520        Self::from_inverse_tensor(SymmetricMatrix::from_mat3_unchecked(
521            Matrix::from_quat(orientation)
522                * Matrix::from_diagonal(principal_angular_inertia.recip_or_zero())
523                * Matrix::from_quat(orientation.inverse()),
524        ))
525    }
526
527    /// Tries to create a new [`ComputedAngularInertia`] from the given principal angular inertia
528    /// and the orientation of the local inertial frame.
529    ///
530    /// The principal angular inertia represents the torque needed for a desired angular acceleration
531    /// about the local coordinate axes defined by the given `orientation`.
532    ///
533    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
534    ///
535    /// # Errors
536    ///
537    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
538    #[inline]
539    pub fn try_new_with_local_frame(
540        principal_angular_inertia: Vector,
541        orientation: Quaternion,
542    ) -> Result<Self, AngularInertiaError> {
543        if principal_angular_inertia.is_nan() {
544            Err(AngularInertiaError::NaN)
545        } else if !principal_angular_inertia.cmpge(Vector::ZERO).all() {
546            Err(AngularInertiaError::Negative)
547        } else {
548            Ok(Self::from_inverse_tensor(
549                SymmetricMatrix::from_mat3_unchecked(
550                    Matrix::from_quat(orientation)
551                        * Matrix::from_diagonal(principal_angular_inertia.recip_or_zero())
552                        * Matrix::from_quat(orientation.inverse()),
553                ),
554            ))
555        }
556    }
557
558    /// Creates a new [`ComputedAngularInertia`] from the given angular inertia tensor.
559    ///
560    /// The tensor should be symmetric and positive definite.
561    ///
562    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
563    #[inline]
564    #[doc(alias = "from_mat3")]
565    pub fn from_tensor(tensor: SymmetricMatrix) -> Self {
566        Self::from_inverse_tensor(tensor.inverse_or_zero())
567    }
568
569    /// Creates a new [`ComputedAngularInertia`] from the given inverse angular inertia tensor.
570    ///
571    /// The tensor should be symmetric and positive definite.
572    #[inline]
573    #[doc(alias = "from_inverse_mat3")]
574    pub fn from_inverse_tensor(inverse_tensor: SymmetricMatrix) -> Self {
575        Self {
576            inverse: inverse_tensor,
577        }
578    }
579
580    /// Returns the angular inertia tensor. If it is infinite, returns zero.
581    ///
582    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
583    /// If multiplying by the inverse angular inertia, consider using `angular_inertia.inverse() * foo`
584    /// instead of `angular_inertia.value().inverse() * foo`.
585    ///
586    /// Equivalent to [`ComputedAngularInertia::tensor`].
587    #[inline]
588    pub fn value(self) -> SymmetricMatrix {
589        self.tensor()
590    }
591
592    /// Returns the inverse of the angular inertia tensor.
593    ///
594    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
595    ///
596    /// Equivalent to [`ComputedAngularInertia::inverse_tensor`].
597    #[inline]
598    pub fn inverse(self) -> SymmetricMatrix {
599        self.inverse_tensor()
600    }
601
602    /// Returns a mutable reference to the inverse of the angular inertia tensor.
603    ///
604    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
605    #[inline]
606    pub(crate) fn inverse_mut(&mut self) -> &mut SymmetricMatrix {
607        self.inverse_tensor_mut()
608    }
609
610    /// Returns the angular inertia tensor.
611    ///
612    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
613    /// If multiplying by the inverse angular inertia, consider using `angular_inertia.inverse() * foo`
614    /// instead of `angular_inertia.value().inverse() * foo`.
615    #[inline]
616    #[doc(alias = "as_mat3")]
617    pub fn tensor(self) -> SymmetricMatrix {
618        self.inverse.inverse_or_zero()
619    }
620
621    /// Returns the inverse of the angular inertia tensor.
622    ///
623    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
624    #[inline]
625    #[doc(alias = "as_inverse_mat3")]
626    pub fn inverse_tensor(self) -> SymmetricMatrix {
627        self.inverse
628    }
629
630    /// Returns a mutable reference to the inverse of the angular inertia tensor.
631    ///
632    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
633    #[inline]
634    #[doc(alias = "as_inverse_mat3_mut")]
635    pub fn inverse_tensor_mut(&mut self) -> &mut SymmetricMatrix {
636        &mut self.inverse
637    }
638
639    /// Sets the angular inertia tensor.
640    #[inline]
641    pub fn set(&mut self, angular_inertia: impl Into<ComputedAngularInertia>) {
642        *self = angular_inertia.into();
643    }
644
645    /// Computes the principal angular inertia and local inertial frame
646    /// by diagonalizing the 3x3 tensor matrix.
647    ///
648    /// The principal angular inertia represents the torque needed for a desired angular acceleration
649    /// about the local coordinate axes defined by the local inertial frame.
650    #[doc(alias = "diagonalize")]
651    pub fn principal_angular_inertia_with_local_frame(&self) -> (Vector, Quaternion) {
652        let angular_inertia = AngularInertia::from_tensor(self.tensor().f32());
653        (
654            angular_inertia.principal.adjust_precision(),
655            angular_inertia.local_frame.adjust_precision(),
656        )
657    }
658
659    /// Computes the angular inertia tensor with the given rotation.
660    ///
661    /// This can be used to transform local angular inertia to world space.
662    #[inline]
663    pub fn rotated(self, rotation: Quaternion) -> Self {
664        let rot_mat3 = Matrix::from_quat(rotation);
665        Self::from_inverse_tensor(SymmetricMatrix::from_mat3_unchecked(
666            (rot_mat3 * self.inverse) * rot_mat3.transpose(),
667        ))
668    }
669
670    /// Computes the angular inertia tensor shifted by the given offset, taking into account the given mass.
671    #[inline]
672    pub fn shifted_tensor(&self, mass: Scalar, offset: Vector) -> SymmetricMatrix3 {
673        if mass > 0.0 && mass.is_finite() && offset != Vector::ZERO {
674            let diagonal_element = offset.length_squared();
675            let diagonal_mat = Matrix3::from_diagonal(Vector::splat(diagonal_element));
676            let offset_outer_product =
677                Matrix3::from_cols(offset * offset.x, offset * offset.y, offset * offset.z);
678            self.tensor()
679                + SymmetricMatrix::from_mat3_unchecked((diagonal_mat + offset_outer_product) * mass)
680        } else {
681            self.tensor()
682        }
683    }
684
685    /// Computes the inverse angular inertia tensor shifted by the given offset, taking into account the given mass.
686    #[inline]
687    pub fn shifted_inverse_tensor(&self, mass: Scalar, offset: Vector) -> SymmetricMatrix3 {
688        self.shifted_tensor(mass, offset).inverse_or_zero()
689    }
690
691    /// Returns `true` if the angular inertia is neither infinite nor NaN.
692    #[inline]
693    pub fn is_finite(self) -> bool {
694        !self.is_infinite() && !self.is_nan()
695    }
696
697    /// Returns `true` if the angular inertia is positive infinity or negative infinity.
698    #[inline]
699    pub fn is_infinite(self) -> bool {
700        self == Self::INFINITY
701    }
702
703    /// Returns `true` if the angular inertia is NaN.
704    #[inline]
705    pub fn is_nan(self) -> bool {
706        self.inverse.is_nan()
707    }
708}
709
710#[cfg(feature = "3d")]
711impl From<SymmetricMatrix> for ComputedAngularInertia {
712    fn from(tensor: SymmetricMatrix) -> Self {
713        Self::from_tensor(tensor)
714    }
715}
716
717#[cfg(feature = "3d")]
718impl From<AngularInertia> for ComputedAngularInertia {
719    fn from(inertia: AngularInertia) -> Self {
720        ComputedAngularInertia::new_with_local_frame(
721            inertia.principal.adjust_precision(),
722            inertia.local_frame.adjust_precision(),
723        )
724    }
725}
726
727#[cfg(feature = "3d")]
728impl From<ComputedAngularInertia> for AngularInertia {
729    fn from(inertia: ComputedAngularInertia) -> Self {
730        Self::from_tensor(inertia.tensor().f32())
731    }
732}
733
734#[cfg(feature = "2d")]
735impl core::ops::Mul<Scalar> for ComputedAngularInertia {
736    type Output = Scalar;
737
738    #[inline]
739    fn mul(self, rhs: Scalar) -> Scalar {
740        self.value() * rhs
741    }
742}
743
744impl core::ops::Mul<Vector> for ComputedAngularInertia {
745    type Output = Vector;
746
747    #[inline]
748    fn mul(self, rhs: Vector) -> Vector {
749        self.value() * rhs
750    }
751}
752
753/// The local [center of mass] computed for a dynamic [rigid body], taking into account
754/// colliders and descendants. Represents the average position of mass in the body.
755///
756/// The total center of mass is computed as the weighted average of the centers of mass
757/// of all attached colliders and the center of mass of the rigid body entity itself.
758/// The center of mass of an entity is determined by its [`CenterOfMass`] component,
759/// or if it is not present, from an attached [`Collider`] based on its shape.
760///
761/// [center of mass]: https://en.wikipedia.org/wiki/Center_of_mass
762/// [rigid body]: RigidBody
763///
764/// # Related Types
765///
766/// - [`CenterOfMass`] can be used to set the local center of mass associated with an individual entity.
767/// - [`ComputedMass`] stores the total mass of a rigid body, taking into account colliders and descendants.
768/// - [`ComputedAngularInertia`] stores the total angular inertia of a rigid body, taking into account colliders and descendants.
769/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
770///
771/// [`SystemParam`]: bevy::ecs::system::SystemParam
772#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
773#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
774#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
775#[reflect(Debug, Component, Default, PartialEq)]
776pub struct ComputedCenterOfMass(pub Vector);
777
778impl ComputedCenterOfMass {
779    /// A center of mass set at the local origin.
780    pub const ZERO: Self = Self(Vector::ZERO);
781
782    /// Creates a new [`ComputedCenterOfMass`] at the given local position.
783    #[inline]
784    #[cfg(feature = "2d")]
785    pub const fn new(x: Scalar, y: Scalar) -> Self {
786        Self(Vector::new(x, y))
787    }
788
789    /// Creates a new [`ComputedCenterOfMass`] at the given local position.
790    #[inline]
791    #[cfg(feature = "3d")]
792    pub const fn new(x: Scalar, y: Scalar, z: Scalar) -> Self {
793        Self(Vector::new(x, y, z))
794    }
795}
796
797impl From<CenterOfMass> for ComputedCenterOfMass {
798    fn from(center_of_mass: CenterOfMass) -> Self {
799        Self(center_of_mass.adjust_precision())
800    }
801}
802
803impl From<ComputedCenterOfMass> for CenterOfMass {
804    fn from(center_of_mass: ComputedCenterOfMass) -> Self {
805        Self(center_of_mass.f32())
806    }
807}
808
809#[cfg(test)]
810mod tests {
811    use super::*;
812    #[cfg(feature = "3d")]
813    use approx::assert_relative_eq;
814
815    #[test]
816    fn mass_creation() {
817        let mass = ComputedMass::new(10.0);
818        assert_eq!(mass, ComputedMass::from_inverse(0.1));
819        assert_eq!(mass.value(), 10.0);
820        assert_eq!(mass.inverse(), 0.1);
821    }
822
823    #[test]
824    fn zero_mass() {
825        // Zero mass should be equivalent to infinite mass.
826        let mass = ComputedMass::new(0.0);
827        assert_eq!(mass, ComputedMass::new(Scalar::INFINITY));
828        assert_eq!(mass, ComputedMass::from_inverse(0.0));
829        assert_eq!(mass.value(), 0.0);
830        assert_eq!(mass.inverse(), 0.0);
831        assert!(mass.is_infinite());
832        assert!(!mass.is_finite());
833        assert!(!mass.is_nan());
834    }
835
836    #[test]
837    fn infinite_mass() {
838        let mass = ComputedMass::INFINITY;
839        assert_eq!(mass, ComputedMass::new(Scalar::INFINITY));
840        assert_eq!(mass, ComputedMass::from_inverse(0.0));
841        assert_eq!(mass.value(), 0.0);
842        assert_eq!(mass.inverse(), 0.0);
843        assert!(mass.is_infinite());
844        assert!(!mass.is_finite());
845        assert!(!mass.is_nan());
846    }
847
848    #[test]
849    #[should_panic]
850    fn negative_mass_panics() {
851        ComputedMass::new(-1.0);
852    }
853
854    #[test]
855    fn negative_mass_error() {
856        assert_eq!(
857            ComputedMass::try_new(-1.0),
858            Err(MassError::Negative),
859            "negative mass should return an error"
860        );
861    }
862
863    #[test]
864    fn nan_mass_error() {
865        assert_eq!(
866            ComputedMass::try_new(Scalar::NAN),
867            Err(MassError::NaN),
868            "NaN mass should return an error"
869        );
870    }
871
872    #[test]
873    #[cfg(feature = "2d")]
874    fn angular_inertia_creation() {
875        let angular_inertia = ComputedAngularInertia::new(10.0);
876        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.1));
877        assert_eq!(angular_inertia.value(), 10.0);
878        assert_eq!(angular_inertia.inverse(), 0.1);
879    }
880
881    #[test]
882    #[cfg(feature = "2d")]
883    fn zero_angular_inertia() {
884        // Zero angular inertia should be equivalent to infinite angular inertia.
885        let angular_inertia = ComputedAngularInertia::new(0.0);
886        assert_eq!(
887            angular_inertia,
888            ComputedAngularInertia::new(Scalar::INFINITY)
889        );
890        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.0));
891        assert_eq!(angular_inertia.value(), 0.0);
892        assert_eq!(angular_inertia.inverse(), 0.0);
893        assert!(angular_inertia.is_infinite());
894        assert!(!angular_inertia.is_finite());
895        assert!(!angular_inertia.is_nan());
896    }
897
898    #[test]
899    #[cfg(feature = "2d")]
900    fn infinite_angular_inertia() {
901        let angular_inertia = ComputedAngularInertia::INFINITY;
902        assert_eq!(
903            angular_inertia,
904            ComputedAngularInertia::new(Scalar::INFINITY)
905        );
906        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.0));
907        assert_eq!(angular_inertia.value(), 0.0);
908        assert_eq!(angular_inertia.inverse(), 0.0);
909        assert!(angular_inertia.is_infinite());
910        assert!(!angular_inertia.is_finite());
911        assert!(!angular_inertia.is_nan());
912    }
913
914    #[test]
915    #[should_panic]
916    #[cfg(feature = "2d")]
917    fn negative_angular_inertia_panics() {
918        ComputedAngularInertia::new(-1.0);
919    }
920
921    #[test]
922    #[cfg(feature = "2d")]
923    fn negative_angular_inertia_error() {
924        assert_eq!(
925            ComputedAngularInertia::try_new(-1.0),
926            Err(AngularInertiaError::Negative),
927            "negative angular inertia should return an error"
928        );
929    }
930
931    #[test]
932    #[cfg(feature = "2d")]
933    fn nan_angular_inertia_error() {
934        assert_eq!(
935            ComputedAngularInertia::try_new(Scalar::NAN),
936            Err(AngularInertiaError::NaN),
937            "NaN angular inertia should return an error"
938        );
939    }
940
941    #[test]
942    #[cfg(feature = "3d")]
943    fn angular_inertia_creation() {
944        let angular_inertia = ComputedAngularInertia::new(Vector::new(10.0, 20.0, 30.0));
945        assert_relative_eq!(
946            angular_inertia.inverse_tensor(),
947            ComputedAngularInertia::from_inverse_tensor(SymmetricMatrix::from_diagonal(
948                Vector::new(0.1, 0.05, 1.0 / 30.0)
949            ))
950            .inverse_tensor()
951        );
952        assert_relative_eq!(
953            angular_inertia.tensor(),
954            SymmetricMatrix::from_diagonal(Vector::new(10.0, 20.0, 30.0))
955        );
956        assert_relative_eq!(
957            angular_inertia.inverse_tensor(),
958            SymmetricMatrix::from_diagonal(Vector::new(0.1, 0.05, 1.0 / 30.0))
959        );
960    }
961
962    #[test]
963    #[cfg(feature = "3d")]
964    fn zero_angular_inertia() {
965        let angular_inertia = ComputedAngularInertia::new(Vector::ZERO);
966        assert_eq!(
967            angular_inertia,
968            ComputedAngularInertia::new(Vector::INFINITY)
969        );
970        assert_eq!(
971            angular_inertia,
972            ComputedAngularInertia::from_inverse_tensor(SymmetricMatrix::from_diagonal(
973                Vector::ZERO
974            ))
975        );
976        assert_relative_eq!(angular_inertia.tensor(), SymmetricMatrix::ZERO);
977        assert_relative_eq!(angular_inertia.inverse_tensor(), SymmetricMatrix::ZERO);
978        assert!(angular_inertia.is_infinite());
979        assert!(!angular_inertia.is_finite());
980        assert!(!angular_inertia.is_nan());
981    }
982
983    #[test]
984    #[cfg(feature = "3d")]
985    fn infinite_angular_inertia() {
986        let angular_inertia = ComputedAngularInertia::INFINITY;
987        assert_eq!(
988            angular_inertia,
989            ComputedAngularInertia::new(Vector::INFINITY)
990        );
991        assert_eq!(
992            angular_inertia,
993            ComputedAngularInertia::from_inverse_tensor(SymmetricMatrix::ZERO)
994        );
995        assert_relative_eq!(angular_inertia.tensor(), SymmetricMatrix::ZERO);
996        assert_relative_eq!(angular_inertia.inverse_tensor(), SymmetricMatrix::ZERO);
997        assert!(angular_inertia.is_infinite());
998        assert!(!angular_inertia.is_finite());
999        assert!(!angular_inertia.is_nan());
1000    }
1001
1002    #[test]
1003    #[should_panic]
1004    #[cfg(feature = "3d")]
1005    fn negative_angular_inertia_panics() {
1006        ComputedAngularInertia::new(Vector::new(-1.0, 2.0, 3.0));
1007    }
1008
1009    #[test]
1010    #[cfg(feature = "3d")]
1011    fn negative_angular_inertia_error() {
1012        assert_eq!(
1013            ComputedAngularInertia::try_new(Vector::new(-1.0, 2.0, 3.0)),
1014            Err(AngularInertiaError::Negative),
1015            "negative angular inertia should return an error"
1016        );
1017    }
1018
1019    #[test]
1020    #[cfg(feature = "3d")]
1021    fn nan_angular_inertia_error() {
1022        assert_eq!(
1023            ComputedAngularInertia::try_new(Vector::new(Scalar::NAN, 2.0, 3.0)),
1024            Err(AngularInertiaError::NaN),
1025            "NaN angular inertia should return an error"
1026        );
1027    }
1028}