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}