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

1#![expect(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 is stored in [`GlobalAngularInertia`], which is automatically recomputed
390/// whenever the local angular inertia or rotation is changed.
391///
392/// To manually compute the world-space version that takes the body's rotation into account,
393/// use the associated [`rotated`](Self::rotated) method.
394///
395/// A total angular inertia of zero is a special case, and is interpreted as infinite angular inertia,
396/// meaning the rigid body will not be affected by any torques.
397///
398/// [angular inertia]: https://en.wikipedia.org/wiki/Moment_of_inertia
399/// [tensor]: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
400/// [rigid body]: RigidBody
401///
402/// # Representation
403///
404/// Internally, the angular inertia is actually stored as the inverse angular inertia tensor `angular_inertia_matrix.inverse()`.
405/// This is because most physics calculations operate on the inverse angular inertia, and storing it directly
406/// allows for fewer inversions and guards against division by zero.
407///
408/// When using [`ComputedAngularInertia`], you shouldn't need to worry about this internal representation.
409/// The provided constructors and getters abstract away the implementation details.
410///
411/// In terms of performance, the main thing to keep in mind is that [`inverse`](Self::inverse) is a no-op
412/// and [`value`](Self::value) contains an inversion. When multiplying by the inverse angular inertia, it's better to use
413/// `angular_inertia.inverse() * foo` than `angular_inertia.value().inverse() * foo`.
414///
415/// # Related Types
416///
417/// - [`AngularInertia`] can be used to set the local angular inertia associated with an individual entity.
418/// - [`GlobalAngularInertia`] stores the world-space angular inertia tensor, which is automatically recomputed
419///   whenever the local angular inertia or rotation is changed.
420/// - [`ComputedMass`] stores the total mass of a rigid body, taking into account colliders and descendants.
421/// - [`ComputedCenterOfMass`] stores the total center of mass of a rigid body, taking into account colliders and descendants.
422/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
423///
424/// [`SystemParam`]: bevy::ecs::system::SystemParam
425#[cfg(feature = "3d")]
426#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
427#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
428#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
429#[reflect(Debug, Component, PartialEq)]
430#[doc(alias = "ComputedMomentOfInertia")]
431pub struct ComputedAngularInertia {
432    // TODO: The matrix should be symmetric and positive definite.
433    //       We could add a custom `SymmetricMat3` type to enforce symmetricity and reduce memory usage.
434    inverse: Matrix,
435}
436
437impl Default for ComputedAngularInertia {
438    fn default() -> Self {
439        Self::INFINITY
440    }
441}
442
443#[cfg(feature = "3d")]
444impl ComputedAngularInertia {
445    /// Infinite angular inertia.
446    pub const INFINITY: Self = Self {
447        inverse: Matrix::ZERO,
448    };
449
450    /// Creates a new [`ComputedAngularInertia`] from the given principal angular inertia.
451    ///
452    /// The principal angular inertia represents the torque needed for a desired angular acceleration
453    /// about the local coordinate axes.
454    ///
455    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
456    ///
457    /// To specify the orientation of the local inertial frame, consider using [`ComputedAngularInertia::new_with_local_frame`].
458    ///
459    /// # Panics
460    ///
461    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
462    #[inline]
463    #[doc(alias = "from_principal_angular_inertia")]
464    pub fn new(principal_angular_inertia: Vector) -> Self {
465        debug_assert!(
466            principal_angular_inertia.cmpge(Vector::ZERO).all()
467                && !principal_angular_inertia.is_nan(),
468            "principal angular inertia must be positive or zero for all axes"
469        );
470
471        Self::from_inverse_tensor(Matrix::from_diagonal(
472            principal_angular_inertia.recip_or_zero(),
473        ))
474    }
475
476    /// Tries to create a new [`ComputedAngularInertia`] from the given principal angular inertia.
477    ///
478    /// The principal angular inertia represents the torque needed for a desired angular acceleration
479    /// about the local coordinate axes. To specify the orientation of the local inertial frame,
480    /// consider using [`ComputedAngularInertia::try_new_with_local_frame`].
481    ///
482    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
483    ///
484    /// # Errors
485    ///
486    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
487    #[inline]
488    pub fn try_new(principal_angular_inertia: Vector) -> Result<Self, AngularInertiaError> {
489        if principal_angular_inertia.is_nan() {
490            Err(AngularInertiaError::NaN)
491        } else if !principal_angular_inertia.cmpge(Vector::ZERO).all() {
492            Err(AngularInertiaError::Negative)
493        } else {
494            Ok(Self::from_inverse_tensor(Matrix::from_diagonal(
495                principal_angular_inertia.recip_or_zero(),
496            )))
497        }
498    }
499
500    /// Creates a new [`ComputedAngularInertia`] from the given principal angular inertia
501    /// and the orientation of the local inertial frame.
502    ///
503    /// The principal angular inertia represents the torque needed for a desired angular acceleration
504    /// about the local coordinate axes defined by the given `orientation`.
505    ///
506    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
507    ///
508    /// # Panics
509    ///
510    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
511    #[inline]
512    #[doc(alias = "from_principal_angular_inertia_with_local_frame")]
513    pub fn new_with_local_frame(
514        principal_angular_inertia: Vector,
515        orientation: Quaternion,
516    ) -> Self {
517        debug_assert!(
518            principal_angular_inertia.cmpge(Vector::ZERO).all()
519                && !principal_angular_inertia.is_nan(),
520            "principal angular inertia must be positive or zero for all axes"
521        );
522
523        Self::from_inverse_tensor(
524            Matrix::from_quat(orientation)
525                * Matrix::from_diagonal(principal_angular_inertia.recip_or_zero())
526                * Matrix::from_quat(orientation.inverse()),
527        )
528    }
529
530    /// Tries to create a new [`ComputedAngularInertia`] from the given principal angular inertia
531    /// and the orientation of the local inertial frame.
532    ///
533    /// The principal angular inertia represents the torque needed for a desired angular acceleration
534    /// about the local coordinate axes defined by the given `orientation`.
535    ///
536    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
537    ///
538    /// # Errors
539    ///
540    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
541    #[inline]
542    pub fn try_new_with_local_frame(
543        principal_angular_inertia: Vector,
544        orientation: Quaternion,
545    ) -> Result<Self, AngularInertiaError> {
546        if principal_angular_inertia.is_nan() {
547            Err(AngularInertiaError::NaN)
548        } else if !principal_angular_inertia.cmpge(Vector::ZERO).all() {
549            Err(AngularInertiaError::Negative)
550        } else {
551            Ok(Self::from_inverse_tensor(
552                Matrix::from_quat(orientation)
553                    * Matrix::from_diagonal(principal_angular_inertia.recip_or_zero())
554                    * Matrix::from_quat(orientation.inverse()),
555            ))
556        }
557    }
558
559    /// Creates a new [`ComputedAngularInertia`] from the given angular inertia tensor.
560    ///
561    /// The tensor should be symmetric and positive definite.
562    ///
563    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
564    #[inline]
565    #[doc(alias = "from_mat3")]
566    pub fn from_tensor(tensor: Matrix) -> Self {
567        Self::from_inverse_tensor(tensor.inverse_or_zero())
568    }
569
570    /// Creates a new [`ComputedAngularInertia`] from the given inverse angular inertia tensor.
571    ///
572    /// The tensor should be symmetric and positive definite.
573    #[inline]
574    #[doc(alias = "from_inverse_mat3")]
575    pub fn from_inverse_tensor(inverse_tensor: Matrix) -> Self {
576        Self {
577            inverse: inverse_tensor,
578        }
579    }
580
581    /// Returns the angular inertia tensor. If it is infinite, returns zero.
582    ///
583    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
584    /// If multiplying by the inverse angular inertia, consider using `angular_inertia.inverse() * foo`
585    /// instead of `angular_inertia.value().inverse() * foo`.
586    ///
587    /// Equivalent to [`ComputedAngularInertia::tensor`].
588    #[inline]
589    pub fn value(self) -> Matrix {
590        self.tensor()
591    }
592
593    /// Returns the inverse of the angular inertia tensor.
594    ///
595    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
596    ///
597    /// Equivalent to [`ComputedAngularInertia::inverse_tensor`].
598    #[inline]
599    pub fn inverse(self) -> Matrix {
600        self.inverse_tensor()
601    }
602
603    /// Returns a mutable reference to the inverse of the angular inertia tensor.
604    ///
605    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
606    #[inline]
607    pub(crate) fn inverse_mut(&mut self) -> &mut Matrix {
608        self.inverse_tensor_mut()
609    }
610
611    /// Returns the angular inertia tensor.
612    ///
613    /// Note that this involves an invertion because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
614    /// If multiplying by the inverse angular inertia, consider using `angular_inertia.inverse() * foo`
615    /// instead of `angular_inertia.value().inverse() * foo`.
616    #[inline]
617    #[doc(alias = "as_mat3")]
618    pub fn tensor(self) -> Matrix {
619        self.inverse.inverse_or_zero()
620    }
621
622    /// Returns the inverse of the angular inertia tensor.
623    ///
624    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
625    #[inline]
626    #[doc(alias = "as_inverse_mat3")]
627    pub fn inverse_tensor(self) -> Matrix {
628        self.inverse
629    }
630
631    /// Returns a mutable reference to the inverse of the angular inertia tensor.
632    ///
633    /// Note that this is a no-op because [`ComputedAngularInertia`] internally stores the inverse angular inertia.
634    #[inline]
635    #[doc(alias = "as_inverse_mat3_mut")]
636    pub fn inverse_tensor_mut(&mut self) -> &mut Matrix {
637        &mut self.inverse
638    }
639
640    /// Sets the angular inertia tensor.
641    #[inline]
642    pub fn set(&mut self, angular_inertia: impl Into<ComputedAngularInertia>) {
643        *self = angular_inertia.into();
644    }
645
646    /// Computes the principal angular inertia and local inertial frame
647    /// by diagonalizing the 3x3 tensor matrix.
648    ///
649    /// The principal angular inertia represents the torque needed for a desired angular acceleration
650    /// about the local coordinate axes defined by the local inertial frame.
651    #[doc(alias = "diagonalize")]
652    pub fn principal_angular_inertia_with_local_frame(&self) -> (Vector, Quaternion) {
653        let angular_inertia = AngularInertia::from_tensor(self.tensor().f32());
654        (
655            angular_inertia.principal.adjust_precision(),
656            angular_inertia.local_frame.adjust_precision(),
657        )
658    }
659
660    /// Computes the angular inertia tensor with the given rotation.
661    ///
662    /// This can be used to transform local angular inertia to world space.
663    #[inline]
664    pub fn rotated(self, rotation: Quaternion) -> Self {
665        let rot_mat3 = Matrix::from_quat(rotation);
666        Self::from_inverse_tensor((rot_mat3 * self.inverse) * rot_mat3.transpose())
667    }
668
669    /// Computes the angular inertia tensor shifted by the given offset, taking into account the given mass.
670    #[inline]
671    pub fn shifted_tensor(&self, mass: Scalar, offset: Vector) -> Matrix3 {
672        if mass > 0.0 && mass.is_finite() && offset != Vector::ZERO {
673            let diagonal_element = offset.length_squared();
674            let diagonal_mat = Matrix3::from_diagonal(Vector::splat(diagonal_element));
675            let offset_outer_product =
676                Matrix3::from_cols(offset * offset.x, offset * offset.y, offset * offset.z);
677            self.tensor() + (diagonal_mat + offset_outer_product) * mass
678        } else {
679            self.tensor()
680        }
681    }
682
683    /// Computes the inverse angular inertia tensor shifted by the given offset, taking into account the given mass.
684    #[inline]
685    pub fn shifted_inverse_tensor(&self, mass: Scalar, offset: Vector) -> Matrix3 {
686        self.shifted_tensor(mass, offset).inverse_or_zero()
687    }
688
689    /// Returns `true` if the angular inertia is neither infinite nor NaN.
690    #[inline]
691    pub fn is_finite(self) -> bool {
692        !self.is_infinite() && !self.is_nan()
693    }
694
695    /// Returns `true` if the angular inertia is positive infinity or negative infinity.
696    #[inline]
697    pub fn is_infinite(self) -> bool {
698        self == Self::INFINITY
699    }
700
701    /// Returns `true` if the angular inertia is NaN.
702    #[inline]
703    pub fn is_nan(self) -> bool {
704        self.inverse.is_nan()
705    }
706}
707
708#[cfg(feature = "3d")]
709impl From<Matrix> for ComputedAngularInertia {
710    fn from(tensor: Matrix) -> Self {
711        Self::from_tensor(tensor)
712    }
713}
714
715#[cfg(feature = "3d")]
716impl From<AngularInertia> for ComputedAngularInertia {
717    fn from(inertia: AngularInertia) -> Self {
718        ComputedAngularInertia::new_with_local_frame(
719            inertia.principal.adjust_precision(),
720            inertia.local_frame.adjust_precision(),
721        )
722    }
723}
724
725#[cfg(feature = "3d")]
726impl From<ComputedAngularInertia> for AngularInertia {
727    fn from(inertia: ComputedAngularInertia) -> Self {
728        Self::from_tensor(inertia.tensor().f32())
729    }
730}
731
732#[cfg(feature = "2d")]
733impl core::ops::Mul<Scalar> for ComputedAngularInertia {
734    type Output = Scalar;
735
736    #[inline]
737    fn mul(self, rhs: Scalar) -> Scalar {
738        self.value() * rhs
739    }
740}
741
742impl core::ops::Mul<Vector> for ComputedAngularInertia {
743    type Output = Vector;
744
745    #[inline]
746    fn mul(self, rhs: Vector) -> Vector {
747        self.value() * rhs
748    }
749}
750
751// In 2D, the global angular inertia is the same as the local angular inertia.
752#[cfg(feature = "2d")]
753pub(crate) type GlobalAngularInertia = ComputedAngularInertia;
754
755/// The total world-space angular inertia computed for a dynamic [rigid body], taking into account
756/// colliders and descendants.
757///
758/// A total angular inertia of zero is a special case, and is interpreted as infinite angular inertia,
759/// meaning the rigid body will not be affected by any torque.
760///
761/// This component is updated automatically whenever the local [`ComputedAngularInertia`] or rotation is changed.
762/// To manually update it, use the associated [`update`](Self::update) method.
763///
764/// [rigid body]: RigidBody
765#[cfg(feature = "3d")]
766#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, PartialEq, From)]
767#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
768#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
769#[reflect(Debug, Component, PartialEq)]
770pub struct GlobalAngularInertia(ComputedAngularInertia);
771
772#[cfg(feature = "3d")]
773impl GlobalAngularInertia {
774    /// Creates a new [`GlobalAngularInertia`] from the given local angular inertia and rotation.
775    pub fn new(
776        local_angular_inertia: impl Into<ComputedAngularInertia>,
777        rotation: impl Into<Quaternion>,
778    ) -> Self {
779        let local_angular_inertia: ComputedAngularInertia = local_angular_inertia.into();
780        Self(local_angular_inertia.rotated(rotation.into()))
781    }
782
783    /// Updates the global angular inertia with the given local angular inertia and rotation.
784    pub fn update(
785        &mut self,
786        local_angular_inertia: impl Into<ComputedAngularInertia>,
787        rotation: impl Into<Quaternion>,
788    ) {
789        *self = Self::new(local_angular_inertia, rotation);
790    }
791}
792
793#[cfg(feature = "3d")]
794impl From<GlobalAngularInertia> for ComputedAngularInertia {
795    fn from(inertia: GlobalAngularInertia) -> Self {
796        inertia.0
797    }
798}
799
800#[cfg(feature = "3d")]
801impl From<Matrix> for GlobalAngularInertia {
802    fn from(tensor: Matrix) -> Self {
803        Self(ComputedAngularInertia::from_tensor(tensor))
804    }
805}
806
807/// The local [center of mass] computed for a dynamic [rigid body], taking into account
808/// colliders and descendants. Represents the average position of mass in the body.
809///
810/// The total center of mass is computed as the weighted average of the centers of mass
811/// of all attached colliders and the center of mass of the rigid body entity itself.
812/// The center of mass of an entity is determined by its [`CenterOfMass`] component,
813/// or if it is not present, from an attached [`Collider`] based on its shape.
814///
815/// [center of mass]: https://en.wikipedia.org/wiki/Center_of_mass
816/// [rigid body]: RigidBody
817///
818/// # Related Types
819///
820/// - [`CenterOfMass`] can be used to set the local center of mass associated with an individual entity.
821/// - [`ComputedMass`] stores the total mass of a rigid body, taking into account colliders and descendants.
822/// - [`ComputedAngularInertia`] stores the total angular inertia of a rigid body, taking into account colliders and descendants.
823/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
824///
825/// [`SystemParam`]: bevy::ecs::system::SystemParam
826#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
827#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
828#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
829#[reflect(Debug, Component, Default, PartialEq)]
830pub struct ComputedCenterOfMass(pub Vector);
831
832impl ComputedCenterOfMass {
833    /// A center of mass set at the local origin.
834    pub const ZERO: Self = Self(Vector::ZERO);
835
836    /// Creates a new [`ComputedCenterOfMass`] at the given local position.
837    #[inline]
838    #[cfg(feature = "2d")]
839    pub const fn new(x: Scalar, y: Scalar) -> Self {
840        Self(Vector::new(x, y))
841    }
842
843    /// Creates a new [`ComputedCenterOfMass`] at the given local position.
844    #[inline]
845    #[cfg(feature = "3d")]
846    pub const fn new(x: Scalar, y: Scalar, z: Scalar) -> Self {
847        Self(Vector::new(x, y, z))
848    }
849}
850
851impl From<CenterOfMass> for ComputedCenterOfMass {
852    fn from(center_of_mass: CenterOfMass) -> Self {
853        Self(center_of_mass.adjust_precision())
854    }
855}
856
857impl From<ComputedCenterOfMass> for CenterOfMass {
858    fn from(center_of_mass: ComputedCenterOfMass) -> Self {
859        Self(center_of_mass.f32())
860    }
861}
862
863#[cfg(test)]
864mod tests {
865    use super::*;
866    #[cfg(feature = "3d")]
867    use approx::assert_relative_eq;
868
869    #[test]
870    fn mass_creation() {
871        let mass = ComputedMass::new(10.0);
872        assert_eq!(mass, ComputedMass::from_inverse(0.1));
873        assert_eq!(mass.value(), 10.0);
874        assert_eq!(mass.inverse(), 0.1);
875    }
876
877    #[test]
878    fn zero_mass() {
879        // Zero mass should be equivalent to infinite mass.
880        let mass = ComputedMass::new(0.0);
881        assert_eq!(mass, ComputedMass::new(Scalar::INFINITY));
882        assert_eq!(mass, ComputedMass::from_inverse(0.0));
883        assert_eq!(mass.value(), 0.0);
884        assert_eq!(mass.inverse(), 0.0);
885        assert!(mass.is_infinite());
886        assert!(!mass.is_finite());
887        assert!(!mass.is_nan());
888    }
889
890    #[test]
891    fn infinite_mass() {
892        let mass = ComputedMass::INFINITY;
893        assert_eq!(mass, ComputedMass::new(Scalar::INFINITY));
894        assert_eq!(mass, ComputedMass::from_inverse(0.0));
895        assert_eq!(mass.value(), 0.0);
896        assert_eq!(mass.inverse(), 0.0);
897        assert!(mass.is_infinite());
898        assert!(!mass.is_finite());
899        assert!(!mass.is_nan());
900    }
901
902    #[test]
903    #[should_panic]
904    fn negative_mass_panics() {
905        ComputedMass::new(-1.0);
906    }
907
908    #[test]
909    fn negative_mass_error() {
910        assert_eq!(
911            ComputedMass::try_new(-1.0),
912            Err(MassError::Negative),
913            "negative mass should return an error"
914        );
915    }
916
917    #[test]
918    fn nan_mass_error() {
919        assert_eq!(
920            ComputedMass::try_new(Scalar::NAN),
921            Err(MassError::NaN),
922            "NaN mass should return an error"
923        );
924    }
925
926    #[test]
927    #[cfg(feature = "2d")]
928    fn angular_inertia_creation() {
929        let angular_inertia = ComputedAngularInertia::new(10.0);
930        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.1));
931        assert_eq!(angular_inertia.value(), 10.0);
932        assert_eq!(angular_inertia.inverse(), 0.1);
933    }
934
935    #[test]
936    #[cfg(feature = "2d")]
937    fn zero_angular_inertia() {
938        // Zero angular inertia should be equivalent to infinite angular inertia.
939        let angular_inertia = ComputedAngularInertia::new(0.0);
940        assert_eq!(
941            angular_inertia,
942            ComputedAngularInertia::new(Scalar::INFINITY)
943        );
944        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.0));
945        assert_eq!(angular_inertia.value(), 0.0);
946        assert_eq!(angular_inertia.inverse(), 0.0);
947        assert!(angular_inertia.is_infinite());
948        assert!(!angular_inertia.is_finite());
949        assert!(!angular_inertia.is_nan());
950    }
951
952    #[test]
953    #[cfg(feature = "2d")]
954    fn infinite_angular_inertia() {
955        let angular_inertia = ComputedAngularInertia::INFINITY;
956        assert_eq!(
957            angular_inertia,
958            ComputedAngularInertia::new(Scalar::INFINITY)
959        );
960        assert_eq!(angular_inertia, ComputedAngularInertia::from_inverse(0.0));
961        assert_eq!(angular_inertia.value(), 0.0);
962        assert_eq!(angular_inertia.inverse(), 0.0);
963        assert!(angular_inertia.is_infinite());
964        assert!(!angular_inertia.is_finite());
965        assert!(!angular_inertia.is_nan());
966    }
967
968    #[test]
969    #[should_panic]
970    #[cfg(feature = "2d")]
971    fn negative_angular_inertia_panics() {
972        ComputedAngularInertia::new(-1.0);
973    }
974
975    #[test]
976    #[cfg(feature = "2d")]
977    fn negative_angular_inertia_error() {
978        assert_eq!(
979            ComputedAngularInertia::try_new(-1.0),
980            Err(AngularInertiaError::Negative),
981            "negative angular inertia should return an error"
982        );
983    }
984
985    #[test]
986    #[cfg(feature = "2d")]
987    fn nan_angular_inertia_error() {
988        assert_eq!(
989            ComputedAngularInertia::try_new(Scalar::NAN),
990            Err(AngularInertiaError::NaN),
991            "NaN angular inertia should return an error"
992        );
993    }
994
995    #[test]
996    #[cfg(feature = "3d")]
997    fn angular_inertia_creation() {
998        let angular_inertia = ComputedAngularInertia::new(Vector::new(10.0, 20.0, 30.0));
999        assert_relative_eq!(
1000            angular_inertia.inverse_tensor(),
1001            ComputedAngularInertia::from_inverse_tensor(Matrix::from_diagonal(Vector::new(
1002                0.1,
1003                0.05,
1004                1.0 / 30.0
1005            )))
1006            .inverse_tensor()
1007        );
1008        assert_relative_eq!(
1009            angular_inertia.tensor(),
1010            Matrix::from_diagonal(Vector::new(10.0, 20.0, 30.0))
1011        );
1012        assert_relative_eq!(
1013            angular_inertia.inverse_tensor(),
1014            Matrix::from_diagonal(Vector::new(0.1, 0.05, 1.0 / 30.0))
1015        );
1016    }
1017
1018    #[test]
1019    #[cfg(feature = "3d")]
1020    fn zero_angular_inertia() {
1021        let angular_inertia = ComputedAngularInertia::new(Vector::ZERO);
1022        assert_eq!(
1023            angular_inertia,
1024            ComputedAngularInertia::new(Vector::INFINITY)
1025        );
1026        assert_eq!(
1027            angular_inertia,
1028            ComputedAngularInertia::from_inverse_tensor(Matrix::from_diagonal(Vector::ZERO))
1029        );
1030        assert_relative_eq!(angular_inertia.tensor(), Matrix::ZERO);
1031        assert_relative_eq!(angular_inertia.inverse_tensor(), Matrix::ZERO);
1032        assert!(angular_inertia.is_infinite());
1033        assert!(!angular_inertia.is_finite());
1034        assert!(!angular_inertia.is_nan());
1035    }
1036
1037    #[test]
1038    #[cfg(feature = "3d")]
1039    fn infinite_angular_inertia() {
1040        let angular_inertia = ComputedAngularInertia::INFINITY;
1041        assert_eq!(
1042            angular_inertia,
1043            ComputedAngularInertia::new(Vector::INFINITY)
1044        );
1045        assert_eq!(
1046            angular_inertia,
1047            ComputedAngularInertia::from_inverse_tensor(Matrix::ZERO)
1048        );
1049        assert_relative_eq!(angular_inertia.tensor(), Matrix::ZERO);
1050        assert_relative_eq!(angular_inertia.inverse_tensor(), Matrix::ZERO);
1051        assert!(angular_inertia.is_infinite());
1052        assert!(!angular_inertia.is_finite());
1053        assert!(!angular_inertia.is_nan());
1054    }
1055
1056    #[test]
1057    #[should_panic]
1058    #[cfg(feature = "3d")]
1059    fn negative_angular_inertia_panics() {
1060        ComputedAngularInertia::new(Vector::new(-1.0, 2.0, 3.0));
1061    }
1062
1063    #[test]
1064    #[cfg(feature = "3d")]
1065    fn negative_angular_inertia_error() {
1066        assert_eq!(
1067            ComputedAngularInertia::try_new(Vector::new(-1.0, 2.0, 3.0)),
1068            Err(AngularInertiaError::Negative),
1069            "negative angular inertia should return an error"
1070        );
1071    }
1072
1073    #[test]
1074    #[cfg(feature = "3d")]
1075    fn nan_angular_inertia_error() {
1076        assert_eq!(
1077            ComputedAngularInertia::try_new(Vector::new(Scalar::NAN, 2.0, 3.0)),
1078            Err(AngularInertiaError::NaN),
1079            "NaN angular inertia should return an error"
1080        );
1081    }
1082}