114 lines
3.8 KiB
Rust
114 lines
3.8 KiB
Rust
#[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;
|
|
}
|
|
|
|
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_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 {
|
|
($name:ident, $t:ty, $quat:ident, $euler:path, $U:path, $V:path, $W:path, $vec:ident) => {
|
|
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) {
|
|
let u1 = (u as $t).to_radians();
|
|
let v1 = (v as $t).to_radians();
|
|
let w1 = (w as $t).to_radians();
|
|
|
|
let q1: $quat = ($quat::from_axis_angle($U, u1)
|
|
* $quat::from_axis_angle($V, v1)
|
|
* $quat::from_axis_angle($W, w1))
|
|
.normalize();
|
|
|
|
// 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);
|
|
|
|
// Test angle reconstruction
|
|
let (u2, v2, w2) = q1.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);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
macro_rules! impl_all_quat_tests_three_axis {
|
|
($t:ty, $q:ident, $v:ident) => {
|
|
impl_3axis_test!(test_euler_zyx, $t, $q, ER::ZYX, $v::Z, $v::Y, $v::X, $v);
|
|
impl_3axis_test!(test_euler_zxy, $t, $q, ER::ZXY, $v::Z, $v::X, $v::Y, $v);
|
|
impl_3axis_test!(test_euler_yxz, $t, $q, ER::YXZ, $v::Y, $v::X, $v::Z, $v);
|
|
impl_3axis_test!(test_euler_yzx, $t, $q, ER::YZX, $v::Y, $v::Z, $v::X, $v);
|
|
impl_3axis_test!(test_euler_xyz, $t, $q, ER::XYZ, $v::X, $v::Y, $v::Z, $v);
|
|
impl_3axis_test!(test_euler_xzy, $t, $q, ER::XZY, $v::X, $v::Z, $v::Y, $v);
|
|
};
|
|
}
|
|
|
|
mod euler {
|
|
use super::AngleDiff;
|
|
use glam::*;
|
|
type ER = EulerRot;
|
|
|
|
mod quat {
|
|
use super::*;
|
|
|
|
impl_all_quat_tests_three_axis!(f32, Quat, Vec3);
|
|
}
|
|
|
|
mod dquat {
|
|
use super::*;
|
|
|
|
impl_all_quat_tests_three_axis!(f64, DQuat, DVec3);
|
|
}
|
|
}
|