bevy_heavy/dim3/
angular_inertia.rs1use core::ops::*;
2
3use crate::MatExt;
4use bevy_math::{Mat3, Quat, Vec3};
5#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
6use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
7
8use super::SymmetricEigen3;
9
10#[derive(Clone, Copy, Debug, PartialEq)]
13pub enum AngularInertiaTensorError {
14 Negative,
16 Nan,
18}
19
20#[derive(Clone, Copy, Debug, Default, PartialEq)]
40#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
41#[cfg_attr(feature = "bevy_reflect", reflect(Debug, PartialEq))]
42#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(
44 all(feature = "bevy_reflect", feature = "serialize"),
45 reflect(Serialize, Deserialize)
46)]
47#[doc(alias = "MomentOfInertiaTensor")]
48pub struct AngularInertiaTensor(Mat3);
49
50impl Deref for AngularInertiaTensor {
51 type Target = Mat3;
52
53 #[inline]
54 fn deref(&self) -> &Self::Target {
55 &self.0
56 }
57}
58
59impl AngularInertiaTensor {
60 pub const ZERO: Self = Self(Mat3::ZERO);
62
63 pub const IDENTITY: Self = Self(Mat3::IDENTITY);
65
66 pub const INFINITY: Self = Self(Mat3::from_diagonal(Vec3::INFINITY));
68
69 #[inline]
80 #[doc(alias = "from_principal_angular_inertia")]
81 pub fn new(principal_angular_inertia: Vec3) -> Self {
82 debug_assert!(
83 principal_angular_inertia.cmpge(Vec3::ZERO).all(),
84 "principal angular inertia must be positive or zero for all axes"
85 );
86
87 Self(Mat3::from_diagonal(principal_angular_inertia))
88 }
89
90 #[inline]
101 pub fn try_new(principal_angular_inertia: Vec3) -> Result<Self, AngularInertiaTensorError> {
102 if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
103 Err(AngularInertiaTensorError::Negative)
104 } else if principal_angular_inertia.is_nan() {
105 Err(AngularInertiaTensorError::Nan)
106 } else {
107 Ok(Self(Mat3::from_diagonal(principal_angular_inertia)))
108 }
109 }
110
111 #[inline]
122 #[doc(alias = "from_principal_angular_inertia_with_local_frame")]
123 pub fn new_with_local_frame(principal_angular_inertia: Vec3, orientation: Quat) -> Self {
124 debug_assert!(
125 principal_angular_inertia.cmpge(Vec3::ZERO).all(),
126 "principal angular inertia must be positive or zero for all axes"
127 );
128
129 Self(
130 Mat3::from_quat(orientation)
131 * Mat3::from_diagonal(principal_angular_inertia)
132 * Mat3::from_quat(orientation.inverse()),
133 )
134 }
135
136 #[inline]
147 pub fn try_new_with_local_frame(
148 principal_angular_inertia: Vec3,
149 orientation: Quat,
150 ) -> Result<Self, AngularInertiaTensorError> {
151 if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
152 Err(AngularInertiaTensorError::Negative)
153 } else if principal_angular_inertia.is_nan() {
154 Err(AngularInertiaTensorError::Nan)
155 } else {
156 Ok(Self(
157 Mat3::from_quat(orientation)
158 * Mat3::from_diagonal(principal_angular_inertia)
159 * Mat3::from_quat(orientation.inverse()),
160 ))
161 }
162 }
163
164 #[inline]
172 #[doc(alias = "from_tensor")]
173 pub fn from_mat3(mat: Mat3) -> Self {
174 Self(mat)
175 }
176
177 #[doc(alias = "as_tensor")]
181 #[inline]
182 pub fn as_mat3(&self) -> Mat3 {
183 self.0
184 }
185
186 #[doc(alias = "as_tensor_mut")]
193 #[inline]
194 pub fn as_mat3_mut(&mut self) -> &mut Mat3 {
195 &mut self.0
196 }
197
198 #[inline]
202 pub fn value(self) -> Mat3 {
203 self.0
204 }
205
206 #[inline]
213 pub fn value_mut(&mut self) -> &mut Mat3 {
214 &mut self.0
215 }
216
217 #[inline]
219 pub fn inverse(self) -> Self {
220 Self(self.inverse_or_zero())
221 }
222
223 #[inline]
225 pub fn set(&mut self, angular_inertia: impl Into<AngularInertiaTensor>) {
226 *self = angular_inertia.into();
227 }
228
229 #[doc(alias = "diagonalize")]
235 pub fn principal_angular_inertia_with_local_frame(&self) -> (Vec3, Quat) {
236 let mut eigen = SymmetricEigen3::new(self.0).reverse();
237
238 if eigen.eigenvectors.determinant() < 0.0 {
239 core::mem::swap(
240 &mut eigen.eigenvectors.y_axis,
241 &mut eigen.eigenvectors.z_axis,
242 );
243 core::mem::swap(&mut eigen.eigenvalues.y, &mut eigen.eigenvalues.z);
244 }
245
246 let mut local_inertial_frame = Quat::from_mat3(&eigen.eigenvectors).normalize();
247
248 if !local_inertial_frame.is_finite() {
249 local_inertial_frame = Quat::IDENTITY;
250 }
251
252 let principal_angular_inertia = eigen.eigenvalues.max(Vec3::ZERO);
254
255 (principal_angular_inertia, local_inertial_frame)
256 }
257
258 #[inline]
262 pub fn rotated(self, rotation: Quat) -> Self {
263 let rot_mat3 = Mat3::from_quat(rotation);
264 Self::from_mat3((rot_mat3 * self.0) * rot_mat3.transpose())
265 }
266
267 #[inline]
269 pub fn shifted(self, mass: f32, offset: Vec3) -> Self {
270 if offset != Vec3::ZERO {
271 let diagonal_element = offset.length_squared();
273 let diagonal_mat = Mat3::from_diagonal(Vec3::splat(diagonal_element));
274 let offset_outer_product =
275 Mat3::from_cols(offset * offset.x, offset * offset.y, offset * offset.z);
276 Self::from_mat3(self.0 + mass * (diagonal_mat - offset_outer_product))
277 } else {
278 self
279 }
280 }
281}
282
283impl From<Mat3> for AngularInertiaTensor {
284 #[inline]
285 fn from(angular_inertia: Mat3) -> Self {
286 Self::from_mat3(angular_inertia)
287 }
288}
289
290impl From<AngularInertiaTensor> for Mat3 {
291 #[inline]
292 fn from(angular_inertia: AngularInertiaTensor) -> Self {
293 angular_inertia.0
294 }
295}
296
297impl TryFrom<Vec3> for AngularInertiaTensor {
298 type Error = AngularInertiaTensorError;
299
300 #[inline]
301 fn try_from(principal_angular_inertia: Vec3) -> Result<Self, Self::Error> {
302 Self::try_new(principal_angular_inertia)
303 }
304}
305
306impl Add<AngularInertiaTensor> for AngularInertiaTensor {
307 type Output = Self;
308
309 #[inline]
310 fn add(self, rhs: AngularInertiaTensor) -> Self {
311 Self(self.0 + rhs.0)
312 }
313}
314
315impl AddAssign<AngularInertiaTensor> for AngularInertiaTensor {
316 #[inline]
317 fn add_assign(&mut self, rhs: AngularInertiaTensor) {
318 self.0 += rhs.0;
319 }
320}
321
322impl Mul<AngularInertiaTensor> for f32 {
323 type Output = AngularInertiaTensor;
324
325 #[inline]
326 fn mul(self, rhs: AngularInertiaTensor) -> AngularInertiaTensor {
327 AngularInertiaTensor(self * rhs.0)
328 }
329}
330
331impl MulAssign<f32> for AngularInertiaTensor {
332 #[inline]
333 fn mul_assign(&mut self, rhs: f32) {
334 self.0 *= rhs;
335 }
336}
337
338impl Div<f32> for AngularInertiaTensor {
339 type Output = Self;
340
341 #[inline]
342 fn div(self, rhs: f32) -> Self {
343 Self(self.0 / rhs)
344 }
345}
346
347impl DivAssign<f32> for AngularInertiaTensor {
348 #[inline]
349 fn div_assign(&mut self, rhs: f32) {
350 self.0 /= rhs;
351 }
352}
353
354impl Mul<AngularInertiaTensor> for Quat {
355 type Output = AngularInertiaTensor;
356
357 #[inline]
358 fn mul(self, angular_inertia: AngularInertiaTensor) -> AngularInertiaTensor {
359 angular_inertia.rotated(self)
360 }
361}
362
363impl Mul<Vec3> for AngularInertiaTensor {
364 type Output = Vec3;
365
366 #[inline]
367 fn mul(self, rhs: Vec3) -> Vec3 {
368 self.0 * rhs
369 }
370}
371
372#[cfg(any(feature = "approx", test))]
373impl approx::AbsDiffEq for AngularInertiaTensor {
374 type Epsilon = f32;
375 fn default_epsilon() -> f32 {
376 f32::EPSILON
377 }
378 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
379 self.0.abs_diff_eq(other.0, epsilon)
380 }
381}
382
383#[cfg(any(feature = "approx", test))]
384impl approx::RelativeEq for AngularInertiaTensor {
385 fn default_max_relative() -> f32 {
386 f32::EPSILON
387 }
388 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
389 self.0.relative_eq(&other.0, epsilon, max_relative)
390 }
391}
392
393#[cfg(any(feature = "approx", test))]
394impl approx::UlpsEq for AngularInertiaTensor {
395 fn default_max_ulps() -> u32 {
396 4
397 }
398 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
399 self.0.ulps_eq(&other.0, epsilon, max_ulps)
400 }
401}