diff options
Diffstat (limited to 'tests/euler.rs')
-rw-r--r-- | tests/euler.rs | 106 |
1 files changed, 49 insertions, 57 deletions
diff --git a/tests/euler.rs b/tests/euler.rs index b2d0726..dc4de53 100644 --- a/tests/euler.rs +++ b/tests/euler.rs @@ -1,53 +1,47 @@ #[macro_use] mod support; -/// Helper to get the 'canonical' version of a `Quat`. We define the canonical of quat `q` as: -/// * `q`, if q.w > epsilon -/// * `-q`, if q.w < -epsilon -/// * `(0, 0, 0, 1)` otherwise -/// The rationale is that q and -q represent the same rotation, and any (_, _, _, 0) respresent no rotation at all. -trait CanonicalQuat: Copy { - fn canonical(self) -> Self; +/// Helper to calculate the inner angle in the range [0, 2*PI) +trait AngleDiff { + type Output; + fn angle_diff(self, other: Self) -> Self::Output; } -macro_rules! impl_canonical_quat { - ($t:ty) => { - impl CanonicalQuat for $t { - fn canonical(self) -> Self { - match self { - _ if self.w >= 1e-5 => self, - _ if self.w <= -1e-5 => -self, - _ => <$t>::from_xyzw(0.0, 0.0, 0.0, 1.0), +macro_rules! impl_angle_diff { + ($t:ty, $pi:expr) => { + impl AngleDiff for $t { + type Output = $t; + fn angle_diff(self, other: $t) -> $t { + const PI2: $t = $pi + $pi; + let s = self.rem_euclid(PI2); + let o = other.rem_euclid(PI2); + if s > o { + (s - o).min(PI2 + o - s) + } else { + (o - s).min(PI2 + s - o) } } } }; } - -impl_canonical_quat!(glam::Quat); -impl_canonical_quat!(glam::DQuat); - -/// Helper to set some alternative epsilons based on the floating point type used -trait EulerEpsilon { - /// epsilon for comparing quaterions built from eulers and axis-angles - const Q_EPS: f32; - - /// epsilon for comparing quaternion round-tripped through eulers (quat -> euler -> quat) - const E_EPS: f32; -} -impl EulerEpsilon for f32 { - const Q_EPS: f32 = 1e-5; - - // The scalar-math and wasm paths seems to use a particularly bad implementation of the trig functions - #[cfg(any(feature = "scalar-math", target_arch = "wasm32"))] - const E_EPS: f32 = 2e-4; - - #[cfg(not(any(feature = "scalar-math", target_arch = "wasm32")))] - const E_EPS: f32 = 1e-5; -} -impl EulerEpsilon for f64 { - const Q_EPS: f32 = 1e-8; - const E_EPS: f32 = 1e-8; +impl_angle_diff!(f32, std::f32::consts::PI); +impl_angle_diff!(f64, std::f64::consts::PI); + +macro_rules! assert_approx_angle { + ($a:expr, $b:expr, $eps:expr) => {{ + let (a, b) = ($a, $b); + let eps = $eps; + let diff = a.angle_diff(b); + assert!( + diff < $eps, + "assertion failed: `(left !== right)` \ + (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)", + a, + b, + eps, + diff + ); + }}; } macro_rules! impl_3axis_test { @@ -55,9 +49,9 @@ macro_rules! impl_3axis_test { glam_test!($name, { let euler = $euler; assert!($U != $W); // First and last axis must be different for three axis - for u in (-180..=180).step_by(15) { - for v in (-180..=180).step_by(15) { - for w in (-180..=180).step_by(15) { + for u in (-176..=176).step_by(44) { + for v in (-88..=88).step_by(44) { + for w in (-176..=176).step_by(44) { let u1 = (u as $t).to_radians(); let v1 = (v as $t).to_radians(); let w1 = (w as $t).to_radians(); @@ -69,21 +63,19 @@ macro_rules! impl_3axis_test { // Test if the rotation is the expected let q2: $quat = $quat::from_euler(euler, u1, v1, w1).normalize(); - assert_approx_eq!(q1.canonical(), q2.canonical(), <$t>::Q_EPS); + assert_approx_eq!(q1, q2, 1e-5); - // Test quat reconstruction from angles - let (u2, v2, w2) = q2.to_euler(euler); + // Test angle reconstruction + let (u2, v2, w2) = q1.to_euler(euler); let q3 = $quat::from_euler(euler, u2, v2, w2).normalize(); - assert_approx_eq!( - q2.canonical(), - q3.canonical(), - <$t>::E_EPS, - format!( - "angles {:?} -> {:?}", - (u, v, w), - (u2.to_degrees(), v2.to_degrees(), w2.to_degrees()) - ) - ); + + assert_approx_angle!(u1, u2, 1e-4 as $t); + assert_approx_angle!(v1, v2, 1e-4 as $t); + assert_approx_angle!(w1, w2, 1e-4 as $t); + + assert_approx_eq!(q1 * $vec::X, q3 * $vec::X, 1e-4); + assert_approx_eq!(q1 * $vec::Y, q3 * $vec::Y, 1e-4); + assert_approx_eq!(q1 * $vec::Z, q3 * $vec::Z, 1e-4); } } } @@ -103,7 +95,7 @@ macro_rules! impl_all_quat_tests_three_axis { } mod euler { - use super::{CanonicalQuat, EulerEpsilon}; + use super::AngleDiff; use glam::*; type ER = EulerRot; |