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}