//! Miscellaneous utilities. // FIXME: move to linalg/traits use na::SimdRealField; use num::Zero; use simba::simd::SimdValue; use std::fmt::Debug; use std::ops::{Add, AddAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign}; use parry::utils::SdpMatrix3; use simba::scalar::{ClosedAdd, ClosedSub}; use {crate::math::*, na::SimdPartialOrd, num::One}; /// The trait for real numbers used by Rapier. /// /// This includes `f32`, `f64` and their related SIMD types. pub trait SimdRealCopy: SimdRealField + Copy + Default { type Vector: Copy + Debug + Default + SimdSign + SimdBasis + SimdVec + SimdCrossMatrix + SimdCross + SimdDot + ClosedAdd + ClosedSub + Mul + MulAssign; #[cfg(feature = "dim3")] type Vector2: Copy + Default + Debug + From<[Self; 2]> + ClosedSub + SimdCapMagnitude + Index; type AngVector: Copy + Default + Debug + SimdDot + Add + Sub + AddAssign + SubAssign + Mul + MulAssign; type Point: Copy + Default + Debug + SimdVec; type Isometry: Copy + IsometryOps; type Matrix: Copy; type Rotation: Copy + SimdQuat; } impl SimdRealCopy for Real { type Vector = Vector; #[cfg(feature = "dim3")] type Vector2 = Vector2; type AngVector = AngVector; type Point = Point; type Isometry = Isometry; type Matrix = Matrix; type Rotation = Rotation; } impl SimdRealCopy for SimdReal { type Vector = SimdVector; #[cfg(feature = "dim3")] type Vector2 = SimdVector2; type AngVector = SimdAngVector; type Point = SimdPoint; type Isometry = SimdIsometry; type Matrix = SimdMatrix; type Rotation = SimdRotation; } const INV_EPSILON: Real = 1.0e-20; pub(crate) fn inv(val: Real) -> Real { if (-INV_EPSILON..=INV_EPSILON).contains(&val) { 0.0 } else { 1.0 / val } } pub(crate) fn simd_inv(val: N) -> N { let eps = N::splat(INV_EPSILON); N::zero().select(val.simd_gt(-eps) & val.simd_lt(eps), N::one() / val) } /// Trait to copy the sign of each component of one scalar/vector/matrix to another. pub trait SimdSign: Sized { // See SIMD implementations of copy_sign there: https://stackoverflow.com/a/57872652 /// Copy the sign of each component of `self` to the corresponding component of `to`. fn copy_sign_to(self, to: Rhs) -> Rhs; } impl SimdSign for Real { fn copy_sign_to(self, to: Self) -> Self { const MINUS_ZERO: Real = -0.0; let signbit = MINUS_ZERO.to_bits(); Real::from_bits((signbit & self.to_bits()) | ((!signbit) & to.to_bits())) } } pub(crate) trait SimdComponent: Sized { type Element; fn min_component(self) -> Self::Element; fn max_component(self) -> Self::Element; } /// Trait to compute the orthonormal basis of a vector. pub trait SimdBasis: Sized { /// The type of the array of orthonormal vectors. type Basis; /// Computes the vectors which, when combined with `self`, form an orthonormal basis. fn orthonormal_basis(self) -> Self::Basis; /// Computes a vector orthogonal to `self` with a unit length (if `self` has a unit length). fn orthonormal_vector(self) -> Self; } pub(crate) trait SimdVec: Sized { type Element; fn horizontal_inf(&self) -> Self::Element; fn horizontal_sup(&self) -> Self::Element; fn component_mul_simd(&self, rhs: &Self) -> Self; } pub(crate) trait SimdCapMagnitude: Sized { fn simd_cap_magnitude(&self, limit: N) -> Self; } pub(crate) trait SimdCrossMatrix: Sized { type CrossMat; type CrossMatTr; fn gcross_matrix(self) -> Self::CrossMat; fn gcross_matrix_tr(self) -> Self::CrossMatTr; } pub(crate) trait SimdCross: Sized { type Result; fn gcross(&self, rhs: Rhs) -> Self::Result; // This method is here only to get the correct generic return // type for the 3D cross product when used in generic code. #[cfg(feature = "dim3")] fn cross_(&self, rhs: &Self) -> Self; } pub(crate) trait SimdDot: Sized { type Result; fn gdot(&self, rhs: Rhs) -> Self::Result; } /// Trait implemented by quaternions. pub trait SimdQuat { /// The result of quaternion differentiation. type Result; /// Compute the differential of `inv(q1) * q2`. fn diff_conj1_2(&self, rhs: &Self) -> Self::Result; } pub(crate) trait SimdAngularInertia { type AngVector; type LinVector; type AngMatrix; fn inverse(&self) -> Self; fn transform_lin_vector(&self, pt: Self::LinVector) -> Self::LinVector; fn transform_vector(&self, pt: Self::AngVector) -> Self::AngVector; fn squared(&self) -> Self; fn transform_matrix(&self, mat: &Self::AngMatrix) -> Self::AngMatrix; fn into_matrix(self) -> Self::AngMatrix; } #[cfg(feature = "dim3")] impl SimdAngularInertia for SdpMatrix3 { type AngVector = Vector; type LinVector = Vector; type AngMatrix = Matrix; fn inverse(&self) -> Self { let minor_m12_m23 = self.m22 * self.m33 - self.m23 * self.m23; let minor_m11_m23 = self.m12 * self.m33 - self.m13 * self.m23; let minor_m11_m22 = self.m12 * self.m23 - self.m13 * self.m22; let determinant = self.m11 * minor_m12_m23 - self.m12 * minor_m11_m23 + self.m13 * minor_m11_m22; if determinant.is_zero() { Self::zero() } else { SdpMatrix3 { m11: minor_m12_m23 / determinant, m12: -minor_m11_m23 / determinant, m13: minor_m11_m22 / determinant, m22: (self.m11 * self.m33 - self.m13 * self.m13) / determinant, m23: (self.m13 * self.m12 - self.m23 * self.m11) / determinant, m33: (self.m11 * self.m22 - self.m12 * self.m12) / determinant, } } } fn squared(&self) -> Self { SdpMatrix3 { m11: self.m11 * self.m11 + self.m12 * self.m12 + self.m13 * self.m13, m12: self.m11 * self.m12 + self.m12 * self.m22 + self.m13 * self.m23, m13: self.m11 * self.m13 + self.m12 * self.m23 + self.m13 * self.m33, m22: self.m12 * self.m12 + self.m22 * self.m22 + self.m23 * self.m23, m23: self.m12 * self.m13 + self.m22 * self.m23 + self.m23 * self.m33, m33: self.m13 * self.m13 + self.m23 * self.m23 + self.m33 * self.m33, } } fn transform_lin_vector(&self, v: Vector) -> Vector { self.transform_vector(v) } fn transform_vector(&self, v: Vector) -> Vector { let x = self.m11 * v.x + self.m12 * v.y + self.m13 * v.z; let y = self.m12 * v.x + self.m22 * v.y + self.m23 * v.z; let z = self.m13 * v.x + self.m23 * v.y + self.m33 * v.z; Vector::new(x, y, z) } #[rustfmt::skip] fn into_matrix(self) -> Matrix { Matrix::new( self.m11, self.m12, self.m13, self.m12, self.m22, self.m23, self.m13, self.m23, self.m33, ) } #[rustfmt::skip] fn transform_matrix(&self, m: &Matrix) -> Matrix { *self * *m } } #[cfg(feature = "dim3")] impl SimdAngularInertia for SdpMatrix3 { type AngVector = SimdVector; type LinVector = SimdVector; type AngMatrix = SimdMatrix; fn inverse(&self) -> Self { let minor_m12_m23 = self.m22 * self.m33 - self.m23 * self.m23; let minor_m11_m23 = self.m12 * self.m33 - self.m13 * self.m23; let minor_m11_m22 = self.m12 * self.m23 - self.m13 * self.m22; let determinant = self.m11 * minor_m12_m23 - self.m12 * minor_m11_m23 + self.m13 * minor_m11_m22; let zero = ::zero(); let is_zero = determinant.simd_eq(zero); let inv_det = (::one() / determinant).select(is_zero, zero); SdpMatrix3 { m11: minor_m12_m23 * inv_det, m12: -minor_m11_m23 * inv_det, m13: minor_m11_m22 * inv_det, m22: (self.m11 * self.m33 - self.m13 * self.m13) * inv_det, m23: (self.m13 * self.m12 - self.m23 * self.m11) * inv_det, m33: (self.m11 * self.m22 - self.m12 * self.m12) * inv_det, } } fn transform_lin_vector(&self, v: SimdVector) -> SimdVector { self.transform_vector(v) } fn transform_vector(&self, v: SimdVector) -> SimdVector { let x = self.m11 * v.x + self.m12 * v.y + self.m13 * v.z; let y = self.m12 * v.x + self.m22 * v.y + self.m23 * v.z; let z = self.m13 * v.x + self.m23 * v.y + self.m33 * v.z; SimdVector::new(x, y, z) } fn squared(&self) -> Self { SdpMatrix3 { m11: self.m11 * self.m11 + self.m12 * self.m12 + self.m13 * self.m13, m12: self.m11 * self.m12 + self.m12 * self.m22 + self.m13 * self.m23, m13: self.m11 * self.m13 + self.m12 * self.m23 + self.m13 * self.m33, m22: self.m12 * self.m12 + self.m22 * self.m22 + self.m23 * self.m23, m23: self.m12 * self.m13 + self.m22 * self.m23 + self.m23 * self.m33, m33: self.m13 * self.m13 + self.m23 * self.m23 + self.m33 * self.m33, } } #[rustfmt::skip] fn into_matrix(self) -> SimdMatrix { SimdMatrix::new( self.m11, self.m12, self.m13, self.m12, self.m22, self.m23, self.m13, self.m23, self.m33, ) } #[rustfmt::skip] #[cfg(feature = "linalg-nalgebra")] fn transform_matrix(&self, m: &SimdMatrix) -> SimdMatrix { let x0 = self.m11 * m.m11 + self.m12 * m.m21 + self.m13 * m.m31; let y0 = self.m12 * m.m11 + self.m22 * m.m21 + self.m23 * m.m31; let z0 = self.m13 * m.m11 + self.m23 * m.m21 + self.m33 * m.m31; let x1 = self.m11 * m.m12 + self.m12 * m.m22 + self.m13 * m.m32; let y1 = self.m12 * m.m12 + self.m22 * m.m22 + self.m23 * m.m32; let z1 = self.m13 * m.m12 + self.m23 * m.m22 + self.m33 * m.m32; let x2 = self.m11 * m.m13 + self.m12 * m.m23 + self.m13 * m.m33; let y2 = self.m12 * m.m13 + self.m22 * m.m23 + self.m23 * m.m33; let z2 = self.m13 * m.m13 + self.m23 * m.m23 + self.m33 * m.m33; SimdMatrix::new( x0, x1, x2, y0, y1, y2, z0, z1, z2, ) } #[rustfmt::skip] #[cfg(feature = "linalg-glam")] fn transform_matrix(&self, m: &SimdMatrix) -> SimdMatrix { let x0 = self.m11 * m[(0, 0)] + self.m12 * m[(1, 0)] + self.m13 * m[(2, 0)]; let y0 = self.m12 * m[(0, 0)] + self.m22 * m[(1, 0)] + self.m23 * m[(2, 0)]; let z0 = self.m13 * m[(0, 0)] + self.m23 * m[(1, 0)] + self.m33 * m[(2, 0)]; let x1 = self.m11 * m[(0, 1)] + self.m12 * m[(1, 1)] + self.m13 * m[(2, 1)]; let y1 = self.m12 * m[(0, 1)] + self.m22 * m[(1, 1)] + self.m23 * m[(2, 1)]; let z1 = self.m13 * m[(0, 1)] + self.m23 * m[(1, 1)] + self.m33 * m[(2, 1)]; let x2 = self.m11 * m[(0, 2)] + self.m12 * m[(1, 2)] + self.m13 * m[(2, 2)]; let y2 = self.m12 * m[(0, 2)] + self.m22 * m[(1, 2)] + self.m23 * m[(2, 2)]; let z2 = self.m13 * m[(0, 2)] + self.m23 * m[(1, 2)] + self.m33 * m[(2, 2)]; SimdMatrix::new( x0, x1, x2, y0, y1, y2, z0, z1, z2, ) } } // This is an RAII structure that enables flushing denormal numbers // to zero, and automatically reseting previous flags once it is dropped. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct FlushToZeroDenormalsAreZeroFlags { original_flags: u32, } impl FlushToZeroDenormalsAreZeroFlags { #[cfg(not(all( not(feature = "enhanced-determinism"), any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse" )))] pub fn flush_denormal_to_zero() -> Self { Self { original_flags: 0 } } #[cfg(all( not(feature = "enhanced-determinism"), any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse" ))] #[allow(deprecated)] // will address that later. pub fn flush_denormal_to_zero() -> Self { unsafe { #[cfg(target_arch = "x86")] use std::arch::x86::{_mm_getcsr, _mm_setcsr, _MM_FLUSH_ZERO_ON}; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::{_mm_getcsr, _mm_setcsr, _MM_FLUSH_ZERO_ON}; // Flush denormals & underflows to zero as this as a significant impact on the solver's performances. // To enable this we need to set the bit 15 (given by _MM_FLUSH_ZERO_ON) and the bit 6 (for denormals-are-zero). // See https://software.intel.com/content/www/us/en/develop/articles/x87-and-sse-floating-point-assists-in-ia-32-flush-to-zero-ftz-and-denormals-are-zero-daz.html let original_flags = _mm_getcsr(); _mm_setcsr(original_flags | _MM_FLUSH_ZERO_ON | (1 << 6)); Self { original_flags } } } } #[cfg(all( not(feature = "enhanced-determinism"), any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse" ))] impl Drop for FlushToZeroDenormalsAreZeroFlags { #[allow(deprecated)] // will address that later. fn drop(&mut self) { #[cfg(target_arch = "x86")] unsafe { std::arch::x86::_mm_setcsr(self.original_flags) } #[cfg(target_arch = "x86_64")] unsafe { std::arch::x86_64::_mm_setcsr(self.original_flags) } } } /// This is an RAII structure that disables floating point exceptions while /// it is alive, so that operations which generate NaNs and infinite values /// intentionally will not trip an exception when debugging problematic /// code that is generating NaNs and infinite values erroneously. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct DisableFloatingPointExceptionsFlags { #[cfg(feature = "debug-disable-legitimate-fe-exceptions")] // We can't get a precise size for this, because it's of type // `fenv_t`, which is a definition that doesn't exist in rust // (not even in the libc crate, as of the time of writing.) // But since the state is intended to be stored on the stack, // 256 bytes should be more than enough. original_flags: [u8; 256], } #[cfg(feature = "debug-disable-legitimate-fe-exceptions")] extern "C" { fn feholdexcept(env: *mut std::ffi::c_void); fn fesetenv(env: *const std::ffi::c_void); } impl DisableFloatingPointExceptionsFlags { #[cfg(not(feature = "debug-disable-legitimate-fe-exceptions"))] #[allow(dead_code)] /// Disables floating point exceptions as long as this object is not dropped. pub fn disable_floating_point_exceptions() -> Self { Self {} } #[cfg(feature = "debug-disable-legitimate-fe-exceptions")] /// Disables floating point exceptions as long as this object is not dropped. pub fn disable_floating_point_exceptions() -> Self { unsafe { let mut original_flags = [0; 256]; feholdexcept(original_flags.as_mut_ptr() as *mut _); Self { original_flags } } } } #[cfg(feature = "debug-disable-legitimate-fe-exceptions")] impl Drop for DisableFloatingPointExceptionsFlags { fn drop(&mut self) { unsafe { fesetenv(self.original_flags.as_ptr() as *const _); } } } pub(crate) fn select_other(pair: (T, T), elt: T) -> T { if pair.0 == elt { pair.1 } else { pair.0 } } /// Methods for simultaneously indexing a container with two distinct indices. pub trait IndexMut2: IndexMut { /// Gets mutable references to two distinct elements of the container. /// /// Panics if `i == j`. fn index_mut2(&mut self, i: usize, j: usize) -> (&mut Self::Output, &mut Self::Output); /// Gets a mutable reference to one element, and immutable reference to a second one. /// /// Panics if `i == j`. #[inline] fn index_mut_const(&mut self, i: usize, j: usize) -> (&mut Self::Output, &Self::Output) { let (a, b) = self.index_mut2(i, j); (a, &*b) } } impl IndexMut2 for Vec { #[inline] fn index_mut2(&mut self, i: usize, j: usize) -> (&mut T, &mut T) { assert!(i != j, "Unable to index the same element twice."); assert!(i < self.len() && j < self.len(), "Index out of bounds."); unsafe { let a = &mut *(self.get_unchecked_mut(i) as *mut _); let b = &mut *(self.get_unchecked_mut(j) as *mut _); (a, b) } } } impl IndexMut2 for [T] { #[inline] fn index_mut2(&mut self, i: usize, j: usize) -> (&mut T, &mut T) { assert!(i != j, "Unable to index the same element twice."); assert!(i < self.len() && j < self.len(), "Index out of bounds."); unsafe { let a = &mut *(self.get_unchecked_mut(i) as *mut _); let b = &mut *(self.get_unchecked_mut(j) as *mut _); (a, b) } } } /// Calculate the difference with smallest absolute value between the two given values. pub fn smallest_abs_diff_between_sin_angles(a: N, b: N) -> N { // Select the smallest path among the two angles to reach the target. let s_err = a - b; let sgn = s_err.simd_signum(); let s_err_complement = s_err - sgn * N::splat(2.0); let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs()); s_err.select(s_err_is_smallest, s_err_complement) } /// Calculate the difference with smallest absolute value between the two given angles. pub fn smallest_abs_diff_between_angles(a: N, b: N) -> N { // Select the smallest path among the two angles to reach the target. let s_err = a - b; let sgn = s_err.simd_signum(); let s_err_complement = s_err - sgn * N::simd_two_pi(); let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs()); s_err.select(s_err_is_smallest, s_err_complement) }