Skip to main content

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