use std::ops::{Add, AddAssign, Mul, Sub};
use super::exts::Vec3Ext;
use super::matrix::SymmetricMat3;
use super::{Mat3, SpatialForceVector, SpatialMotionVector, Vec3};
use crate::{Real, SMatrix, VEC3_ZERO};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug)]
pub struct RigidBodyInertia {
pub mass: Real,
pub h: Vec3,
pub inertia: SymmetricMat3,
}
impl Default for RigidBodyInertia {
fn default() -> Self {
Self::new(1.0, Vec3::zeros(), SymmetricMat3::ONE)
}
}
impl RigidBodyInertia {
pub const ZERO: RigidBodyInertia = RigidBodyInertia {
mass: 0.0,
h: VEC3_ZERO,
inertia: SymmetricMat3::ZERO,
};
#[inline]
pub fn new(mass: Real, center_of_mass: Vec3, inertia_com: SymmetricMat3) -> Self {
assert!(mass > 0.0, "mass must be positive:{mass}");
let h = mass * center_of_mass;
let cx = center_of_mass.into_cross_matrix();
let top_left = inertia_com - SymmetricMat3::from(mass * cx * cx);
Self {
mass,
h,
inertia: top_left,
}
}
#[inline]
#[must_use]
pub fn scale(&self, scalar: Real) -> Self {
Self {
mass: self.mass * scalar,
h: self.h * scalar,
inertia: self.inertia.scale(scalar),
}
}
#[inline]
#[must_use]
pub fn add(&self, rhs: Self) -> Self {
Self {
mass: self.mass + rhs.mass,
h: self.h + rhs.h,
inertia: self.inertia.add(rhs.inertia),
}
}
#[inline]
pub fn mul_vec(&self, motion_vec: SpatialMotionVector) -> SpatialForceVector {
let motion_w = motion_vec.top;
let motion_v = motion_vec.bottom;
let h = self.h;
let m = self.mass;
let inertia = self.inertia;
let force_n = inertia.mul_vec(motion_w) + h.cross(&motion_v);
let force_f = m * motion_v - h.cross(&motion_w);
SpatialForceVector::from_pair(force_n, force_f)
}
pub fn matrix(&self) -> SMatrix<6, 6> {
let mut result = SMatrix::<6, 6>::zeros();
result
.fixed_view_mut::<3, 3>(0, 0)
.copy_from(&self.inertia.mat3());
let h_crm = self.h.into_cross_matrix();
result.fixed_view_mut::<3, 3>(0, 3).copy_from(&h_crm);
result
.fixed_view_mut::<3, 3>(3, 0)
.copy_from(&h_crm.transpose());
let mass = Mat3::identity().scale(self.mass);
result.fixed_view_mut::<3, 3>(3, 3).copy_from(&mass);
result
}
#[inline]
pub fn into_array(self) -> [Real; 10] {
let mut array = [0.0; 10];
array[0] = self.mass;
array[1] = self.h.x;
array[2] = self.h.y;
array[3] = self.h.z;
let inertia_array = self.inertia.into_array();
array[4] = inertia_array[0];
array[5] = inertia_array[1];
array[6] = inertia_array[2];
array[7] = inertia_array[3];
array[8] = inertia_array[4];
array[9] = inertia_array[5];
array
}
}
impl From<RigidBodyInertia> for [Real; 10] {
#[inline]
fn from(val: RigidBodyInertia) -> Self {
val.into_array()
}
}
impl Mul<Real> for RigidBodyInertia {
type Output = Self;
#[inline]
fn mul(self, rhs: Real) -> Self::Output {
self.scale(rhs)
}
}
impl Add<Self> for RigidBodyInertia {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
RigidBodyInertia::add(&self, rhs)
}
}
impl Mul<SpatialMotionVector> for RigidBodyInertia {
type Output = SpatialForceVector;
#[inline]
fn mul(self, rhs: SpatialMotionVector) -> Self::Output {
self.mul_vec(rhs)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default)]
pub struct ArticulatedBodyInertia {
pub top_left: SymmetricMat3,
pub h: SMatrix<3, 3>,
pub mass: SymmetricMat3,
}
impl ArticulatedBodyInertia {
#[inline]
#[must_use]
pub fn add_arti_inertia(&self, rhs: &Self) -> Self {
Self {
top_left: self.top_left.add(rhs.top_left),
h: self.h + rhs.h,
mass: self.mass.add(rhs.mass),
}
}
#[inline]
#[must_use]
pub fn sub_arti_inertia(&self, rhs: &Self) -> Self {
Self {
top_left: self.top_left - rhs.top_left,
h: self.h - rhs.h,
mass: self.mass - rhs.mass,
}
}
#[inline]
#[must_use]
pub fn add_spat_inertia(&self, rhs: &RigidBodyInertia) -> Self {
let mass = self.mass.add(SymmetricMat3::ONE.scale(rhs.mass));
let h = self.h + rhs.h.into_cross_matrix();
let inertia = self.top_left + rhs.inertia;
Self {
top_left: inertia,
h,
mass,
}
}
#[inline]
pub fn mul_motion_vec(&self, motion_vec: SpatialMotionVector) -> SpatialForceVector {
let motion_w = motion_vec.top;
let motion_v = motion_vec.bottom;
let h = self.h;
let n = self.top_left.mul_vec(motion_w) + h * motion_v;
let f = self.mass.mul_vec(motion_v) + h.transpose() * motion_w;
SpatialForceVector::from_pair(n, f)
}
#[inline]
pub fn from_symmertic_matrix(matrix: SMatrix<6, 6>) -> Self {
let inertia = matrix.fixed_view::<3, 3>(0, 0).into();
let h = matrix.fixed_view::<3, 3>(0, 3).into();
let mass = matrix.fixed_view::<3, 3>(3, 3).into();
Self {
top_left: inertia,
h,
mass,
}
}
pub fn matrix(&self) -> SMatrix<6, 6> {
let mut result = SMatrix::<6, 6>::zeros();
result
.fixed_view_mut::<3, 3>(0, 0)
.copy_from(&self.top_left.mat3());
result.fixed_view_mut::<3, 3>(0, 3).copy_from(&self.h);
result
.fixed_view_mut::<3, 3>(3, 0)
.copy_from(&self.h.transpose());
result
.fixed_view_mut::<3, 3>(3, 3)
.copy_from(&self.mass.mat3());
result
}
}
impl Add<Self> for ArticulatedBodyInertia {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
self.add_arti_inertia(&rhs)
}
}
impl Add<Self> for &ArticulatedBodyInertia {
type Output = ArticulatedBodyInertia;
#[inline]
fn add(self, rhs: Self) -> ArticulatedBodyInertia {
self.add_arti_inertia(rhs)
}
}
impl AddAssign<&ArticulatedBodyInertia> for ArticulatedBodyInertia {
#[inline]
fn add_assign(&mut self, rhs: &ArticulatedBodyInertia) {
*self = self.add_arti_inertia(rhs);
}
}
impl Sub<Self> for ArticulatedBodyInertia {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
self.sub_arti_inertia(&rhs)
}
}
impl Sub<ArticulatedBodyInertia> for &ArticulatedBodyInertia {
type Output = ArticulatedBodyInertia;
#[inline]
fn sub(self, rhs: ArticulatedBodyInertia) -> Self::Output {
self.sub_arti_inertia(&rhs)
}
}
impl Add<RigidBodyInertia> for ArticulatedBodyInertia {
type Output = Self;
#[inline]
fn add(self, rhs: RigidBodyInertia) -> Self::Output {
self.add_spat_inertia(&rhs)
}
}
impl From<RigidBodyInertia> for ArticulatedBodyInertia {
#[inline]
fn from(value: RigidBodyInertia) -> Self {
Self {
top_left: value.inertia,
h: value.h.into_cross_matrix(),
mass: SymmetricMat3::ONE.scale(value.mass),
}
}
}
impl Mul<SpatialMotionVector> for ArticulatedBodyInertia {
type Output = SpatialForceVector;
#[inline]
fn mul(self, rhs: SpatialMotionVector) -> Self::Output {
self.mul_motion_vec(rhs)
}
}
impl Mul<SpatialMotionVector> for &ArticulatedBodyInertia {
type Output = SpatialForceVector;
#[inline]
fn mul(self, rhs: SpatialMotionVector) -> Self::Output {
self.mul_motion_vec(rhs)
}
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
use crate::vec3;
#[test]
fn test_rbi_construct() {
let mass = 2.0;
let com = vec3(1.0, 2.0, 3.0);
let inertia_com = SymmetricMat3::from_array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let rbi = RigidBodyInertia::new(mass, com, inertia_com);
let expected_inertia =
inertia_com.mat3() + mass * (com.dot(&com) * Mat3::identity() - com * com.transpose());
assert_relative_eq!(rbi.inertia.mat3(), expected_inertia, epsilon = 1e-6);
}
#[test]
fn test_inertia_mul_motion_vec() {
let mass = 2.0;
let com = vec3(1.0, 2.0, 3.0);
let inertia_com = SymmetricMat3::from_array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let rbi = RigidBodyInertia::new(mass, com, inertia_com);
let v = SpatialMotionVector::from_array([1., 2., 3., 4., 5., 6.]);
let f = rbi * v;
let expected_f = rbi.matrix() * v.into_vec6();
assert_relative_eq!(f.into_vec6(), expected_f, epsilon = 1e-6);
}
}