diff options
| author | Sébastien Crozet <developer@crozet.re> | 2020-08-31 17:58:14 +0200 |
|---|---|---|
| committer | Sébastien Crozet <developer@crozet.re> | 2020-08-31 19:05:14 +0200 |
| commit | df2156ffd02ea1b8c86e86f1d68c5e4e915e6d98 (patch) | |
| tree | 53c08d57b3fdbfcb94654a61fd68fcec5d66bffd /src/dynamics | |
| parent | cc05bad0410128b163e81e9f703ccb841f6a9a08 (diff) | |
| download | rapier-df2156ffd02ea1b8c86e86f1d68c5e4e915e6d98.tar.gz rapier-df2156ffd02ea1b8c86e86f1d68c5e4e915e6d98.tar.bz2 rapier-df2156ffd02ea1b8c86e86f1d68c5e4e915e6d98.zip | |
Allow the removal of a collider.
Diffstat (limited to 'src/dynamics')
| -rw-r--r-- | src/dynamics/mass_properties.rs | 165 | ||||
| -rw-r--r-- | src/dynamics/rigid_body.rs | 20 |
2 files changed, 183 insertions, 2 deletions
diff --git a/src/dynamics/mass_properties.rs b/src/dynamics/mass_properties.rs index cc2979c..8affe0a 100644 --- a/src/dynamics/mass_properties.rs +++ b/src/dynamics/mass_properties.rs @@ -1,7 +1,7 @@ use crate::math::{AngVector, AngularInertia, Isometry, Point, Rotation, Vector}; use crate::utils; use num::Zero; -use std::ops::{Add, AddAssign}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; #[cfg(feature = "dim3")] use {na::Matrix3, std::ops::MulAssign}; @@ -24,6 +24,59 @@ pub struct MassProperties { pub principal_inertia_local_frame: Rotation<f32>, } +impl approx::AbsDiffEq for MassProperties { + type Epsilon = f32; + fn default_epsilon() -> Self::Epsilon { + f32::default_epsilon() + } + + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + self.local_com.abs_diff_eq(&other.local_com, epsilon) + && self.inv_mass.abs_diff_eq(&other.inv_mass, epsilon) + && self + .inv_principal_inertia_sqrt + .abs_diff_eq(&other.inv_principal_inertia_sqrt, epsilon) + // && self + // .principal_inertia_local_frame + // .abs_diff_eq(&other.principal_inertia_local_frame, epsilon) + } +} + +impl approx::RelativeEq for MassProperties { + fn default_max_relative() -> Self::Epsilon { + f32::default_max_relative() + } + + fn relative_eq( + &self, + other: &Self, + epsilon: Self::Epsilon, + max_relative: Self::Epsilon, + ) -> bool { + #[cfg(feature = "dim2")] + let inertia_is_ok = self.inv_principal_inertia_sqrt.relative_eq( + &other.inv_principal_inertia_sqrt, + epsilon, + max_relative, + ); + + #[cfg(feature = "dim3")] + let inertia_is_ok = self.reconstruct_inverse_inertia_matrix().relative_eq( + &other.reconstruct_inverse_inertia_matrix(), + epsilon, + max_relative, + ); + + inertia_is_ok + && self + .local_com + .relative_eq(&other.local_com, epsilon, max_relative) + && self + .inv_mass + .relative_eq(&other.inv_mass, epsilon, max_relative) + } +} + impl MassProperties { #[cfg(feature = "dim2")] pub(crate) fn new(local_com: Point<f32>, mass: f32, principal_inertia: f32) -> Self { @@ -91,6 +144,18 @@ impl MassProperties { } #[cfg(feature = "dim3")] + /// Reconstructs the inverse angular inertia tensor of the rigid body from its principal inertia values and axii. + pub fn reconstruct_inverse_inertia_matrix(&self) -> Matrix3<f32> { + let inv_principal_inertia = self.inv_principal_inertia_sqrt.map(|e| e * e); + self.principal_inertia_local_frame.to_rotation_matrix() + * Matrix3::from_diagonal(&inv_principal_inertia) + * self + .principal_inertia_local_frame + .inverse() + .to_rotation_matrix() + } + + #[cfg(feature = "dim3")] /// Reconstructs the angular inertia tensor of the rigid body from its principal inertia values and axii. pub fn reconstruct_inertia_matrix(&self) -> Matrix3<f32> { let principal_inertia = self.inv_principal_inertia_sqrt.map(|e| utils::inv(e * e)); @@ -143,6 +208,67 @@ impl Zero for MassProperties { } } +impl Sub<MassProperties> for MassProperties { + type Output = Self; + + #[cfg(feature = "dim2")] + fn sub(self, other: MassProperties) -> Self { + if self.is_zero() || other.is_zero() { + return self; + } + + let m1 = utils::inv(self.inv_mass); + let m2 = utils::inv(other.inv_mass); + let inv_mass = utils::inv(m1 - m2); + + let local_com = (self.local_com * m1 - other.local_com.coords * m2) * inv_mass; + let i1 = self.construct_shifted_inertia_matrix(local_com - self.local_com); + let i2 = other.construct_shifted_inertia_matrix(local_com - other.local_com); + let inertia = i1 - i2; + // NOTE: we drop the negative eigenvalues that may result from subtraction rounding errors. + let inv_principal_inertia_sqrt = utils::inv(inertia.max(0.0).sqrt()); + + Self { + local_com, + inv_mass, + inv_principal_inertia_sqrt, + } + } + + #[cfg(feature = "dim3")] + fn sub(self, other: MassProperties) -> Self { + if self.is_zero() || other.is_zero() { + return self; + } + + let m1 = utils::inv(self.inv_mass); + let m2 = utils::inv(other.inv_mass); + let inv_mass = utils::inv(m1 - m2); + let local_com = (self.local_com * m1 - other.local_com.coords * m2) * inv_mass; + let i1 = self.construct_shifted_inertia_matrix(local_com - self.local_com); + let i2 = other.construct_shifted_inertia_matrix(local_com - other.local_com); + let inertia = i1 - i2; + let eigen = inertia.symmetric_eigen(); + let principal_inertia_local_frame = Rotation::from_matrix(&eigen.eigenvectors); + let principal_inertia = eigen.eigenvalues; + // NOTE: we drop the negative eigenvalues that may result from subtraction rounding errors. + let inv_principal_inertia_sqrt = principal_inertia.map(|e| utils::inv(e.max(0.0).sqrt())); + + Self { + local_com, + inv_mass, + inv_principal_inertia_sqrt, + principal_inertia_local_frame, + } + } +} + +impl SubAssign<MassProperties> for MassProperties { + fn sub_assign(&mut self, rhs: MassProperties) { + *self = *self - rhs + } +} + impl Add<MassProperties> for MassProperties { type Output = Self; @@ -204,3 +330,40 @@ impl AddAssign<MassProperties> for MassProperties { *self = *self + rhs } } + +#[cfg(test)] +mod test { + use super::MassProperties; + use crate::geometry::ColliderBuilder; + use approx::assert_relative_eq; + use num::Zero; + + #[test] + fn mass_properties_add_sub() { + // Check that addition and subtraction of mass properties behave as expected. + let c1 = ColliderBuilder::capsule_x(1.0, 2.0).build(); + let c2 = ColliderBuilder::capsule_y(3.0, 4.0).build(); + let c3 = ColliderBuilder::ball(5.0).build(); + + let m1 = c1.mass_properties(); + let m2 = c2.mass_properties(); + let m3 = c3.mass_properties(); + let m1m2m3 = m1 + m2 + m3; + + assert_relative_eq!(m1 + m2, m2 + m1, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m1, m2 + m3, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m2, m1 + m3, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m3, m1 + m2, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - (m1 + m2), m3, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - (m1 + m3), m2, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - (m2 + m3), m1, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m1 - m2, m3, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m1 - m3, m2, epsilon = 1.0e-6); + assert_relative_eq!(m1m2m3 - m2 - m3, m1, epsilon = 1.0e-6); + assert_relative_eq!( + m1m2m3 - m1 - m2 - m3, + MassProperties::zero(), + epsilon = 1.0e-6 + ); + } +} diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index 584dc96..d9cddd1 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -1,5 +1,5 @@ use crate::dynamics::MassProperties; -use crate::geometry::{ColliderHandle, InteractionGraph, RigidBodyGraphIndex}; +use crate::geometry::{Collider, ColliderHandle, InteractionGraph, RigidBodyGraphIndex}; use crate::math::{AngVector, AngularInertia, Isometry, Point, Rotation, Translation, Vector}; use crate::utils::{WCross, WDot}; use num::Zero; @@ -137,6 +137,24 @@ impl RigidBody { crate::utils::inv(self.mass_properties.inv_mass) } + /// Adds a collider to this rigid-body. + pub(crate) fn add_collider_internal(&mut self, handle: ColliderHandle, coll: &Collider) { + let mass_properties = coll.mass_properties(); + self.colliders.push(handle); + self.mass_properties += mass_properties; + self.update_world_mass_properties(); + } + + /// Removes a collider from this rigid-body. + pub(crate) fn remove_collider_internal(&mut self, handle: ColliderHandle, coll: &Collider) { + if let Some(i) = self.colliders.iter().position(|e| *e == handle) { + self.colliders.swap_remove(i); + let mass_properties = coll.mass_properties(); + self.mass_properties -= mass_properties; + self.update_world_mass_properties(); + } + } + /// Put this rigid body to sleep. /// /// A sleeping body no longer moves and is no longer simulated by the physics engine unless |
