bevy_heavy/dim3/
angular_inertia.rs1use core::ops::*;
2
3use bevy_math::{Mat3, Quat, Vec3};
4#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
5use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
6use glam_matrix_extras::{MatConversionError, SymmetricEigen3, SymmetricMat3};
7
8#[derive(Clone, Copy, Debug, PartialEq)]
10pub enum AngularInertiaTensorError {
11 Negative,
13 Nan,
15}
16
17#[derive(Clone, Copy, Debug, Default, PartialEq)]
35#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
36#[cfg_attr(feature = "bevy_reflect", reflect(Debug, PartialEq))]
37#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
38#[cfg_attr(
39 all(feature = "bevy_reflect", feature = "serialize"),
40 reflect(Serialize, Deserialize)
41)]
42#[doc(alias = "MomentOfInertiaTensor")]
43pub struct AngularInertiaTensor(SymmetricMat3);
44
45impl Deref for AngularInertiaTensor {
46 type Target = SymmetricMat3;
47
48 #[inline]
49 fn deref(&self) -> &Self::Target {
50 &self.0
51 }
52}
53
54impl AngularInertiaTensor {
55 pub const ZERO: Self = Self(SymmetricMat3::ZERO);
57
58 pub const IDENTITY: Self = Self(SymmetricMat3::IDENTITY);
60
61 pub const INFINITY: Self = Self(SymmetricMat3::from_diagonal(Vec3::INFINITY));
63
64 #[inline]
75 #[doc(alias = "from_principal_angular_inertia")]
76 pub fn new(principal_angular_inertia: Vec3) -> Self {
77 debug_assert!(
78 principal_angular_inertia.cmpge(Vec3::ZERO).all(),
79 "principal angular inertia must be positive or zero for all axes"
80 );
81
82 Self(SymmetricMat3::from_diagonal(principal_angular_inertia))
83 }
84
85 #[inline]
96 pub fn try_new(principal_angular_inertia: Vec3) -> Result<Self, AngularInertiaTensorError> {
97 if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
98 Err(AngularInertiaTensorError::Negative)
99 } else if principal_angular_inertia.is_nan() {
100 Err(AngularInertiaTensorError::Nan)
101 } else {
102 Ok(Self(SymmetricMat3::from_diagonal(
103 principal_angular_inertia,
104 )))
105 }
106 }
107
108 #[inline]
119 #[doc(alias = "from_principal_angular_inertia_with_local_frame")]
120 pub fn new_with_local_frame(principal_angular_inertia: Vec3, orientation: Quat) -> Self {
121 debug_assert!(
122 principal_angular_inertia.cmpge(Vec3::ZERO).all(),
123 "principal angular inertia must be positive or zero for all axes"
124 );
125
126 Self(SymmetricMat3::from_mat3_unchecked(
127 Mat3::from_quat(orientation)
128 * Mat3::from_diagonal(principal_angular_inertia)
129 * Mat3::from_quat(orientation.inverse()),
130 ))
131 }
132
133 #[inline]
144 pub fn try_new_with_local_frame(
145 principal_angular_inertia: Vec3,
146 orientation: Quat,
147 ) -> Result<Self, AngularInertiaTensorError> {
148 if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
149 Err(AngularInertiaTensorError::Negative)
150 } else if principal_angular_inertia.is_nan() {
151 Err(AngularInertiaTensorError::Nan)
152 } else {
153 Ok(Self(SymmetricMat3::from_mat3_unchecked(
154 Mat3::from_quat(orientation)
155 * Mat3::from_diagonal(principal_angular_inertia)
156 * Mat3::from_quat(orientation.inverse()),
157 )))
158 }
159 }
160
161 #[inline]
169 #[must_use]
170 #[doc(alias = "from_tensor")]
171 pub const fn from_symmetric_mat3(mat: SymmetricMat3) -> Self {
172 Self(mat)
173 }
174
175 #[inline]
187 pub fn try_from_mat3(mat: Mat3) -> Result<Self, MatConversionError> {
188 SymmetricMat3::try_from_mat3(mat).map(Self)
189 }
190
191 #[inline]
197 #[must_use]
198 pub const fn from_mat3_unchecked(mat: Mat3) -> Self {
199 Self(SymmetricMat3::from_mat3_unchecked(mat))
200 }
201
202 #[inline]
206 #[doc(alias = "as_tensor")]
207 pub fn as_symmetric_mat3(&self) -> SymmetricMat3 {
208 self.0
209 }
210
211 #[inline]
215 #[doc(alias = "as_tensor_mut")]
216 pub fn as_symmetric_mat3_mut(&mut self) -> &mut SymmetricMat3 {
217 &mut self.0
218 }
219
220 #[inline]
224 pub fn value(self) -> SymmetricMat3 {
225 self.0
226 }
227
228 #[inline]
232 pub fn value_mut(&mut self) -> &mut SymmetricMat3 {
233 &mut self.0
234 }
235
236 #[inline]
238 #[doc(alias = "to_tensor")]
239 pub fn to_mat3(&self) -> Mat3 {
240 self.0.to_mat3()
241 }
242
243 #[inline]
245 pub fn inverse(self) -> Self {
246 Self(self.inverse_or_zero())
247 }
248
249 #[inline]
251 pub fn set(&mut self, angular_inertia: impl Into<AngularInertiaTensor>) {
252 *self = angular_inertia.into();
253 }
254
255 #[doc(alias = "diagonalize")]
261 pub fn principal_angular_inertia_with_local_frame(&self) -> (Vec3, Quat) {
262 let mut eigen = SymmetricEigen3::new(self.0).reverse();
263
264 if eigen.eigenvectors.determinant() < 0.0 {
265 core::mem::swap(
266 &mut eigen.eigenvectors.y_axis,
267 &mut eigen.eigenvectors.z_axis,
268 );
269 core::mem::swap(&mut eigen.eigenvalues.y, &mut eigen.eigenvalues.z);
270 }
271
272 let mut local_inertial_frame = Quat::from_mat3(&eigen.eigenvectors).normalize();
273
274 if !local_inertial_frame.is_finite() {
275 local_inertial_frame = Quat::IDENTITY;
276 }
277
278 let principal_angular_inertia = eigen.eigenvalues.max(Vec3::ZERO);
280
281 (principal_angular_inertia, local_inertial_frame)
282 }
283
284 #[inline]
288 pub fn rotated(self, rotation: Quat) -> Self {
289 let rot_mat3 = Mat3::from_quat(rotation);
290 Self::from_mat3_unchecked((rot_mat3 * self.0) * rot_mat3.transpose())
291 }
292
293 #[inline]
295 pub fn shifted(self, mass: f32, offset: Vec3) -> Self {
296 if offset != Vec3::ZERO {
297 let diagonal_element = offset.length_squared();
299 let diagonal_mat = Mat3::from_diagonal(Vec3::splat(diagonal_element));
300 let offset_outer_product =
301 Mat3::from_cols(offset * offset.x, offset * offset.y, offset * offset.z);
302 Self::from_mat3_unchecked(self.0 + mass * (diagonal_mat - offset_outer_product))
303 } else {
304 self
305 }
306 }
307}
308
309impl From<SymmetricMat3> for AngularInertiaTensor {
310 #[inline]
311 fn from(angular_inertia: SymmetricMat3) -> Self {
312 Self::from_symmetric_mat3(angular_inertia)
313 }
314}
315
316impl TryFrom<Mat3> for AngularInertiaTensor {
317 type Error = MatConversionError;
318
319 #[inline]
320 fn try_from(angular_inertia: Mat3) -> Result<Self, Self::Error> {
321 Self::try_from_mat3(angular_inertia)
322 }
323}
324
325impl From<AngularInertiaTensor> for SymmetricMat3 {
326 #[inline]
327 fn from(angular_inertia: AngularInertiaTensor) -> Self {
328 angular_inertia.0
329 }
330}
331
332impl From<AngularInertiaTensor> for Mat3 {
333 #[inline]
334 fn from(angular_inertia: AngularInertiaTensor) -> Self {
335 angular_inertia.0.to_mat3()
336 }
337}
338
339impl TryFrom<Vec3> for AngularInertiaTensor {
340 type Error = AngularInertiaTensorError;
341
342 #[inline]
343 fn try_from(principal_angular_inertia: Vec3) -> Result<Self, Self::Error> {
344 Self::try_new(principal_angular_inertia)
345 }
346}
347
348impl Add<AngularInertiaTensor> for AngularInertiaTensor {
349 type Output = Self;
350
351 #[inline]
352 fn add(self, rhs: AngularInertiaTensor) -> Self {
353 Self(self.0 + rhs.0)
354 }
355}
356
357impl AddAssign<AngularInertiaTensor> for AngularInertiaTensor {
358 #[inline]
359 fn add_assign(&mut self, rhs: AngularInertiaTensor) {
360 self.0 += rhs.0;
361 }
362}
363
364impl Mul<AngularInertiaTensor> for f32 {
365 type Output = AngularInertiaTensor;
366
367 #[inline]
368 fn mul(self, rhs: AngularInertiaTensor) -> AngularInertiaTensor {
369 AngularInertiaTensor(self * rhs.0)
370 }
371}
372
373impl MulAssign<f32> for AngularInertiaTensor {
374 #[inline]
375 fn mul_assign(&mut self, rhs: f32) {
376 self.0 *= rhs;
377 }
378}
379
380impl Div<f32> for AngularInertiaTensor {
381 type Output = Self;
382
383 #[inline]
384 fn div(self, rhs: f32) -> Self {
385 Self(self.0 / rhs)
386 }
387}
388
389impl DivAssign<f32> for AngularInertiaTensor {
390 #[inline]
391 fn div_assign(&mut self, rhs: f32) {
392 self.0 /= rhs;
393 }
394}
395
396impl Mul<AngularInertiaTensor> for Quat {
397 type Output = AngularInertiaTensor;
398
399 #[inline]
400 fn mul(self, angular_inertia: AngularInertiaTensor) -> AngularInertiaTensor {
401 angular_inertia.rotated(self)
402 }
403}
404
405impl Mul<Vec3> for AngularInertiaTensor {
406 type Output = Vec3;
407
408 #[inline]
409 fn mul(self, rhs: Vec3) -> Vec3 {
410 self.0 * rhs
411 }
412}
413
414#[cfg(any(feature = "approx", test))]
415impl approx::AbsDiffEq for AngularInertiaTensor {
416 type Epsilon = f32;
417 fn default_epsilon() -> f32 {
418 f32::EPSILON
419 }
420 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
421 self.0.abs_diff_eq(&other.0, epsilon)
422 }
423}
424
425#[cfg(any(feature = "approx", test))]
426impl approx::RelativeEq for AngularInertiaTensor {
427 fn default_max_relative() -> f32 {
428 f32::EPSILON
429 }
430 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
431 self.0.relative_eq(&other.0, epsilon, max_relative)
432 }
433}
434
435#[cfg(any(feature = "approx", test))]
436impl approx::UlpsEq for AngularInertiaTensor {
437 fn default_max_ulps() -> u32 {
438 4
439 }
440 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
441 self.0.ulps_eq(&other.0, epsilon, max_ulps)
442 }
443}