use crate::error::SpatialError;
use crate::linear_algebra::{Matrix, Matrix3D, Vector3D};
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SpatialInertia<T: Numeric = f64> {
mass: T,
center_of_mass: Vector3D<T>,
rotational_inertia: Matrix3D<T>,
}
impl<T: Numeric> SpatialInertia<T> {
pub fn new(
mass: T,
center_of_mass: Vector3D<T>,
rotational_inertia: Matrix3D<T>,
) -> Result<Self, SpatialError> {
if !mass.is_finite() || !center_of_mass.is_finite() || !rotational_inertia.is_finite() {
return Err(SpatialError::NonFinite);
}
if mass <= T::ZERO {
return Err(SpatialError::NonPositiveMass);
}
if !rotational_inertia.is_symmetric() {
return Err(SpatialError::NotSymmetric);
}
for index in 0..3 {
if rotational_inertia[(index, index)] <= T::ZERO {
return Err(SpatialError::NonPositiveInertia);
}
}
Ok(SpatialInertia {
mass,
center_of_mass,
rotational_inertia,
})
}
pub fn from_diagonal_inertia(
mass: T,
center_of_mass: Vector3D<T>,
diagonal: Vector3D<T>,
) -> Result<Self, SpatialError> {
Self::new(
mass,
center_of_mass,
Matrix::from_diagonal(diagonal.into_array()),
)
}
#[inline]
#[must_use]
pub fn mass(self) -> T {
self.mass
}
#[inline]
pub fn center_of_mass(self) -> Vector3D<T> {
self.center_of_mass
}
#[inline]
pub fn rotational_inertia(self) -> Matrix3D<T> {
self.rotational_inertia
}
pub fn inertia_about(self, point: Vector3D<T>) -> Matrix3D<T> {
let offset = point - self.center_of_mass;
let distance_squared = offset.dot(offset);
Matrix::from_fn(|row, col| {
let spread = if row == col {
distance_squared
} else {
T::ZERO
};
self.rotational_inertia[(row, col)] + self.mass * (spread - offset[row] * offset[col])
})
}
#[inline]
#[must_use]
pub fn is_finite(self) -> bool {
self.mass.is_finite()
&& self.center_of_mass.is_finite()
&& self.rotational_inertia.is_finite()
}
}