use brepkit_math::vec::Point3;
#[derive(Debug, Clone)]
pub struct GProps {
pub mass: f64,
pub center: Point3,
pub inertia: [f64; 6],
}
impl GProps {
#[must_use]
pub fn new() -> Self {
Self {
mass: 0.0,
center: Point3::new(0.0, 0.0, 0.0),
inertia: [0.0; 6],
}
}
pub fn add(&mut self, other: &Self) {
let m_total = self.mass + other.mass;
if m_total.abs() < 1e-30 {
return;
}
let cx = (self.mass * self.center.x() + other.mass * other.center.x()) / m_total;
let cy = (self.mass * self.center.y() + other.mass * other.center.y()) / m_total;
let cz = (self.mass * self.center.z() + other.mass * other.center.z()) / m_total;
let new_center = Point3::new(cx, cy, cz);
let i_self = shift_inertia(&self.inertia, self.mass, self.center, new_center);
let i_other = shift_inertia(&other.inertia, other.mass, other.center, new_center);
self.mass = m_total;
self.center = new_center;
for k in 0..6 {
self.inertia[k] = i_self[k] + i_other[k];
}
}
#[must_use]
pub fn matrix_of_inertia(&self) -> [[f64; 3]; 3] {
let [ixx, iyy, izz, ixy, ixz, iyz] = self.inertia;
[[ixx, -ixy, -ixz], [-ixy, iyy, -iyz], [-ixz, -iyz, izz]]
}
}
impl Default for GProps {
fn default() -> Self {
Self::new()
}
}
fn shift_inertia(
inertia: &[f64; 6],
mass: f64,
old_center: Point3,
new_center: Point3,
) -> [f64; 6] {
let dx = old_center.x() - new_center.x();
let dy = old_center.y() - new_center.y();
let dz = old_center.z() - new_center.z();
let d_sq = dx * dx + dy * dy + dz * dz;
[
inertia[0] + mass * (d_sq - dx * dx), inertia[1] + mass * (d_sq - dy * dy), inertia[2] + mass * (d_sq - dz * dz), inertia[3] + mass * dx * dy, inertia[4] + mass * dx * dz, inertia[5] + mass * dy * dz, ]
}