diff options
Diffstat (limited to 'tests/euler.rs')
-rw-r--r-- | tests/euler.rs | 106 |
1 files changed, 57 insertions, 49 deletions
diff --git a/tests/euler.rs b/tests/euler.rs index dc4de53..b2d0726 100644 --- a/tests/euler.rs +++ b/tests/euler.rs @@ -1,47 +1,53 @@ #[macro_use] mod support; -/// Helper to calculate the inner angle in the range [0, 2*PI) -trait AngleDiff { - type Output; - fn angle_diff(self, other: Self) -> Self::Output; +/// 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; } -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) +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), } } } }; } -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 - ); - }}; + +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; } macro_rules! impl_3axis_test { @@ -49,9 +55,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 (-176..=176).step_by(44) { - for v in (-88..=88).step_by(44) { - for w in (-176..=176).step_by(44) { + for u in (-180..=180).step_by(15) { + for v in (-180..=180).step_by(15) { + for w in (-180..=180).step_by(15) { let u1 = (u as $t).to_radians(); let v1 = (v as $t).to_radians(); let w1 = (w as $t).to_radians(); @@ -63,19 +69,21 @@ 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, q2, 1e-5); + assert_approx_eq!(q1.canonical(), q2.canonical(), <$t>::Q_EPS); - // Test angle reconstruction - let (u2, v2, w2) = q1.to_euler(euler); + // Test quat reconstruction from angles + let (u2, v2, w2) = q2.to_euler(euler); let q3 = $quat::from_euler(euler, u2, v2, w2).normalize(); - - 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); + assert_approx_eq!( + q2.canonical(), + q3.canonical(), + <$t>::E_EPS, + format!( + "angles {:?} -> {:?}", + (u, v, w), + (u2.to_degrees(), v2.to_degrees(), w2.to_degrees()) + ) + ); } } } @@ -95,7 +103,7 @@ macro_rules! impl_all_quat_tests_three_axis { } mod euler { - use super::AngleDiff; + use super::{CanonicalQuat, EulerEpsilon}; use glam::*; type ER = EulerRot; |