use crate::{ Affine2, Affine3A, DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4, }; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; macro_rules! impl_approx_as_ref { ($prim:ident, $type:ty) => { impl AbsDiffEq for $type { type Epsilon = <$prim as AbsDiffEq>::Epsilon; fn default_epsilon() -> Self::Epsilon { $prim::default_epsilon() } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_ref().abs_diff_eq(other.as_ref(), epsilon) } } impl RelativeEq for $type { fn default_max_relative() -> Self::Epsilon { $prim::default_max_relative() } fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.as_ref() .relative_eq(other.as_ref(), epsilon, max_relative) } } impl UlpsEq for $type { fn default_max_ulps() -> u32 { $prim::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } }; } macro_rules! impl_approx_xzy_axes { ($prim:ident, $type:ty) => { impl AbsDiffEq for $type { type Epsilon = <$prim as AbsDiffEq>::Epsilon; fn default_epsilon() -> Self::Epsilon { $prim::default_epsilon() } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) } } impl RelativeEq for $type { fn default_max_relative() -> Self::Epsilon { $prim::default_max_relative() } fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) } } impl UlpsEq for $type { fn default_max_ulps() -> u32 { $prim::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) } } }; } macro_rules! impl_approx_xzyw_axes { ($prim:ident, $type:ty) => { impl AbsDiffEq for $type { type Epsilon = <$prim as AbsDiffEq>::Epsilon; fn default_epsilon() -> Self::Epsilon { $prim::default_epsilon() } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) && AbsDiffEq::abs_diff_eq(&self.w_axis, &other.w_axis, epsilon) } } impl RelativeEq for $type { fn default_max_relative() -> Self::Epsilon { $prim::default_max_relative() } fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) && RelativeEq::relative_eq(&self.w_axis, &other.w_axis, epsilon, max_relative) } } impl UlpsEq for $type { fn default_max_ulps() -> u32 { $prim::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) && UlpsEq::ulps_eq(&self.w_axis, &other.w_axis, epsilon, max_ulps) } } }; } impl_approx_as_ref!(f32, Mat2); impl_approx_as_ref!(f32, Mat3); impl_approx_as_ref!(f32, Mat4); impl_approx_as_ref!(f32, Quat); impl_approx_as_ref!(f32, Vec2); impl_approx_as_ref!(f32, Vec3); impl_approx_as_ref!(f32, Vec4); impl_approx_as_ref!(f32, Vec3A); impl_approx_xzy_axes!(f32, Affine2); impl_approx_xzyw_axes!(f32, Affine3A); impl_approx_xzy_axes!(f32, Mat3A); impl_approx_xzy_axes!(f64, DAffine2); impl_approx_xzyw_axes!(f64, DAffine3); impl_approx_as_ref!(f64, DMat2); impl_approx_as_ref!(f64, DMat3); impl_approx_as_ref!(f64, DMat4); impl_approx_as_ref!(f64, DQuat); impl_approx_as_ref!(f64, DVec2); impl_approx_as_ref!(f64, DVec3); impl_approx_as_ref!(f64, DVec4); #[cfg(test)] mod test { use crate::*; use approx::*; macro_rules! impl_approx_test { ($prim:ident, $type:ident, $ones:expr) => { let one_eps = $ones * $type::default_epsilon(); let two_eps = one_eps + one_eps; let one_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 1); let four_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 16); approx::assert_abs_diff_eq!($ones, $ones); approx::assert_abs_diff_eq!($ones, $ones + one_eps); approx::assert_abs_diff_eq!($ones, $ones - one_eps); approx::assert_abs_diff_ne!($ones, $ones + two_eps); approx::assert_abs_diff_ne!($ones, $ones - two_eps); approx::assert_relative_eq!($ones, $ones); approx::assert_relative_ne!($ones, $ones - $ones); // defaults to 4 ulps and I have no idea how to pass other parameters to this macro :) approx::assert_ulps_eq!($ones, one_ulp); approx::assert_ulps_ne!($ones, four_ulp); }; ($prim:ident, $type:ident) => { impl_approx_test!($prim, $type, $type::ONE) }; } #[test] fn test_approx() { const ONESF32: [f32; 16] = [1.0; 16]; impl_approx_test!(f32, Vec2); impl_approx_test!(f32, Vec3); impl_approx_test!(f32, Vec3A); impl_approx_test!(f32, Vec4); impl_approx_test!(f32, Quat, Quat::from_slice(&ONESF32)); impl_approx_test!(f32, Mat2, Mat2::from_cols_slice(&ONESF32)); impl_approx_test!(f32, Mat3, Mat3::from_cols_slice(&ONESF32)); impl_approx_test!(f32, Mat3A, Mat3A::from_cols_slice(&ONESF32)); impl_approx_test!(f32, Mat4, Mat4::from_cols_slice(&ONESF32)); const ONESF64: [f64; 16] = [1.0; 16]; impl_approx_test!(f64, DVec2); impl_approx_test!(f64, DVec3); impl_approx_test!(f64, DVec4); impl_approx_test!(f64, DQuat, DQuat::from_slice(&ONESF64)); impl_approx_test!(f64, DMat2, DMat2::from_cols_slice(&ONESF64)); impl_approx_test!(f64, DMat3, DMat3::from_cols_slice(&ONESF64)); impl_approx_test!(f64, DMat4, DMat4::from_cols_slice(&ONESF64)); } }