use core::ops::*;
use bevy_math::{Mat3, Quat, Vec3};
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use glam_matrix_extras::{MatConversionError, SymmetricEigen3, SymmetricMat3};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AngularInertiaTensorError {
Negative,
Nan,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "bevy_reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
)]
#[doc(alias = "MomentOfInertiaTensor")]
pub struct AngularInertiaTensor(SymmetricMat3);
impl Deref for AngularInertiaTensor {
type Target = SymmetricMat3;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AngularInertiaTensor {
pub const ZERO: Self = Self(SymmetricMat3::ZERO);
pub const IDENTITY: Self = Self(SymmetricMat3::IDENTITY);
pub const INFINITY: Self = Self(SymmetricMat3::from_diagonal(Vec3::INFINITY));
#[inline]
#[doc(alias = "from_principal_angular_inertia")]
pub fn new(principal_angular_inertia: Vec3) -> Self {
debug_assert!(
principal_angular_inertia.cmpge(Vec3::ZERO).all(),
"principal angular inertia must be positive or zero for all axes"
);
Self(SymmetricMat3::from_diagonal(principal_angular_inertia))
}
#[inline]
pub fn try_new(principal_angular_inertia: Vec3) -> Result<Self, AngularInertiaTensorError> {
if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
Err(AngularInertiaTensorError::Negative)
} else if principal_angular_inertia.is_nan() {
Err(AngularInertiaTensorError::Nan)
} else {
Ok(Self(SymmetricMat3::from_diagonal(
principal_angular_inertia,
)))
}
}
#[inline]
#[doc(alias = "from_principal_angular_inertia_with_local_frame")]
pub fn new_with_local_frame(principal_angular_inertia: Vec3, orientation: Quat) -> Self {
debug_assert!(
principal_angular_inertia.cmpge(Vec3::ZERO).all(),
"principal angular inertia must be positive or zero for all axes"
);
Self(SymmetricMat3::from_mat3_unchecked(
Mat3::from_quat(orientation)
* Mat3::from_diagonal(principal_angular_inertia)
* Mat3::from_quat(orientation.inverse()),
))
}
#[inline]
pub fn try_new_with_local_frame(
principal_angular_inertia: Vec3,
orientation: Quat,
) -> Result<Self, AngularInertiaTensorError> {
if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
Err(AngularInertiaTensorError::Negative)
} else if principal_angular_inertia.is_nan() {
Err(AngularInertiaTensorError::Nan)
} else {
Ok(Self(SymmetricMat3::from_mat3_unchecked(
Mat3::from_quat(orientation)
* Mat3::from_diagonal(principal_angular_inertia)
* Mat3::from_quat(orientation.inverse()),
)))
}
}
#[inline]
#[must_use]
#[doc(alias = "from_tensor")]
pub const fn from_symmetric_mat3(mat: SymmetricMat3) -> Self {
Self(mat)
}
#[inline]
pub fn try_from_mat3(mat: Mat3) -> Result<Self, MatConversionError> {
SymmetricMat3::try_from_mat3(mat).map(Self)
}
#[inline]
#[must_use]
pub const fn from_mat3_unchecked(mat: Mat3) -> Self {
Self(SymmetricMat3::from_mat3_unchecked(mat))
}
#[inline]
#[doc(alias = "as_tensor")]
pub fn as_symmetric_mat3(&self) -> SymmetricMat3 {
self.0
}
#[inline]
#[doc(alias = "as_tensor_mut")]
pub fn as_symmetric_mat3_mut(&mut self) -> &mut SymmetricMat3 {
&mut self.0
}
#[inline]
pub fn value(self) -> SymmetricMat3 {
self.0
}
#[inline]
pub fn value_mut(&mut self) -> &mut SymmetricMat3 {
&mut self.0
}
#[inline]
#[doc(alias = "to_tensor")]
pub fn to_mat3(&self) -> Mat3 {
self.0.to_mat3()
}
#[inline]
pub fn inverse(self) -> Self {
Self(self.inverse_or_zero())
}
#[inline]
pub fn set(&mut self, angular_inertia: impl Into<AngularInertiaTensor>) {
*self = angular_inertia.into();
}
#[doc(alias = "diagonalize")]
pub fn principal_angular_inertia_with_local_frame(&self) -> (Vec3, Quat) {
let mut eigen = SymmetricEigen3::new(self.0).reverse();
if eigen.eigenvectors.determinant() < 0.0 {
core::mem::swap(
&mut eigen.eigenvectors.y_axis,
&mut eigen.eigenvectors.z_axis,
);
core::mem::swap(&mut eigen.eigenvalues.y, &mut eigen.eigenvalues.z);
}
let mut local_inertial_frame = Quat::from_mat3(&eigen.eigenvectors).normalize();
if !local_inertial_frame.is_finite() {
local_inertial_frame = Quat::IDENTITY;
}
let principal_angular_inertia = eigen.eigenvalues.max(Vec3::ZERO);
(principal_angular_inertia, local_inertial_frame)
}
#[inline]
pub fn rotated(self, rotation: Quat) -> Self {
let rot_mat3 = Mat3::from_quat(rotation);
Self::from_mat3_unchecked((rot_mat3 * self.0) * rot_mat3.transpose())
}
#[inline]
pub fn shifted(self, mass: f32, offset: Vec3) -> Self {
if offset != Vec3::ZERO {
let diagonal_element = offset.length_squared();
let diagonal_mat = Mat3::from_diagonal(Vec3::splat(diagonal_element));
let offset_outer_product =
Mat3::from_cols(offset * offset.x, offset * offset.y, offset * offset.z);
Self::from_mat3_unchecked(self.0 + mass * (diagonal_mat - offset_outer_product))
} else {
self
}
}
}
impl From<SymmetricMat3> for AngularInertiaTensor {
#[inline]
fn from(angular_inertia: SymmetricMat3) -> Self {
Self::from_symmetric_mat3(angular_inertia)
}
}
impl TryFrom<Mat3> for AngularInertiaTensor {
type Error = MatConversionError;
#[inline]
fn try_from(angular_inertia: Mat3) -> Result<Self, Self::Error> {
Self::try_from_mat3(angular_inertia)
}
}
impl From<AngularInertiaTensor> for SymmetricMat3 {
#[inline]
fn from(angular_inertia: AngularInertiaTensor) -> Self {
angular_inertia.0
}
}
impl From<AngularInertiaTensor> for Mat3 {
#[inline]
fn from(angular_inertia: AngularInertiaTensor) -> Self {
angular_inertia.0.to_mat3()
}
}
impl TryFrom<Vec3> for AngularInertiaTensor {
type Error = AngularInertiaTensorError;
#[inline]
fn try_from(principal_angular_inertia: Vec3) -> Result<Self, Self::Error> {
Self::try_new(principal_angular_inertia)
}
}
impl Add<AngularInertiaTensor> for AngularInertiaTensor {
type Output = Self;
#[inline]
fn add(self, rhs: AngularInertiaTensor) -> Self {
Self(self.0 + rhs.0)
}
}
impl AddAssign<AngularInertiaTensor> for AngularInertiaTensor {
#[inline]
fn add_assign(&mut self, rhs: AngularInertiaTensor) {
self.0 += rhs.0;
}
}
impl Mul<AngularInertiaTensor> for f32 {
type Output = AngularInertiaTensor;
#[inline]
fn mul(self, rhs: AngularInertiaTensor) -> AngularInertiaTensor {
AngularInertiaTensor(self * rhs.0)
}
}
impl MulAssign<f32> for AngularInertiaTensor {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.0 *= rhs;
}
}
impl Div<f32> for AngularInertiaTensor {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self {
Self(self.0 / rhs)
}
}
impl DivAssign<f32> for AngularInertiaTensor {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.0 /= rhs;
}
}
impl Mul<AngularInertiaTensor> for Quat {
type Output = AngularInertiaTensor;
#[inline]
fn mul(self, angular_inertia: AngularInertiaTensor) -> AngularInertiaTensor {
angular_inertia.rotated(self)
}
}
impl Mul<Vec3> for AngularInertiaTensor {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Vec3 {
self.0 * rhs
}
}
#[cfg(any(feature = "approx", test))]
impl approx::AbsDiffEq for AngularInertiaTensor {
type Epsilon = f32;
fn default_epsilon() -> f32 {
f32::EPSILON
}
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
self.0.abs_diff_eq(&other.0, epsilon)
}
}
#[cfg(any(feature = "approx", test))]
impl approx::RelativeEq for AngularInertiaTensor {
fn default_max_relative() -> f32 {
f32::EPSILON
}
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
self.0.relative_eq(&other.0, epsilon, max_relative)
}
}
#[cfg(any(feature = "approx", test))]
impl approx::UlpsEq for AngularInertiaTensor {
fn default_max_ulps() -> u32 {
4
}
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
self.0.ulps_eq(&other.0, epsilon, max_ulps)
}
}