1 use crate::{ 2 Affine2, Affine3A, DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, 3 Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4, 4 }; 5 use approx::{AbsDiffEq, RelativeEq, UlpsEq}; 6 7 macro_rules! impl_approx_as_ref { 8 ($prim:ident, $type:ty) => { 9 impl AbsDiffEq for $type { 10 type Epsilon = <$prim as AbsDiffEq>::Epsilon; 11 fn default_epsilon() -> Self::Epsilon { 12 $prim::default_epsilon() 13 } 14 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 15 self.as_ref().abs_diff_eq(other.as_ref(), epsilon) 16 } 17 } 18 19 impl RelativeEq for $type { 20 fn default_max_relative() -> Self::Epsilon { 21 $prim::default_max_relative() 22 } 23 fn relative_eq( 24 &self, 25 other: &Self, 26 epsilon: Self::Epsilon, 27 max_relative: Self::Epsilon, 28 ) -> bool { 29 self.as_ref() 30 .relative_eq(other.as_ref(), epsilon, max_relative) 31 } 32 } 33 34 impl UlpsEq for $type { 35 fn default_max_ulps() -> u32 { 36 $prim::default_max_ulps() 37 } 38 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 39 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) 40 } 41 } 42 }; 43 } 44 45 macro_rules! impl_approx_xzy_axes { 46 ($prim:ident, $type:ty) => { 47 impl AbsDiffEq for $type { 48 type Epsilon = <$prim as AbsDiffEq>::Epsilon; 49 fn default_epsilon() -> Self::Epsilon { 50 $prim::default_epsilon() 51 } 52 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 53 AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) 54 && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) 55 && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) 56 } 57 } 58 59 impl RelativeEq for $type { 60 fn default_max_relative() -> Self::Epsilon { 61 $prim::default_max_relative() 62 } 63 fn relative_eq( 64 &self, 65 other: &Self, 66 epsilon: Self::Epsilon, 67 max_relative: Self::Epsilon, 68 ) -> bool { 69 RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) 70 && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) 71 && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) 72 } 73 } 74 75 impl UlpsEq for $type { 76 fn default_max_ulps() -> u32 { 77 $prim::default_max_ulps() 78 } 79 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 80 UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) 81 && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) 82 && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) 83 } 84 } 85 }; 86 } 87 88 macro_rules! impl_approx_xzyw_axes { 89 ($prim:ident, $type:ty) => { 90 impl AbsDiffEq for $type { 91 type Epsilon = <$prim as AbsDiffEq>::Epsilon; 92 fn default_epsilon() -> Self::Epsilon { 93 $prim::default_epsilon() 94 } 95 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 96 AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) 97 && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) 98 && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) 99 && AbsDiffEq::abs_diff_eq(&self.w_axis, &other.w_axis, epsilon) 100 } 101 } 102 103 impl RelativeEq for $type { 104 fn default_max_relative() -> Self::Epsilon { 105 $prim::default_max_relative() 106 } 107 fn relative_eq( 108 &self, 109 other: &Self, 110 epsilon: Self::Epsilon, 111 max_relative: Self::Epsilon, 112 ) -> bool { 113 RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) 114 && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) 115 && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) 116 && RelativeEq::relative_eq(&self.w_axis, &other.w_axis, epsilon, max_relative) 117 } 118 } 119 120 impl UlpsEq for $type { 121 fn default_max_ulps() -> u32 { 122 $prim::default_max_ulps() 123 } 124 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 125 UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) 126 && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) 127 && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) 128 && UlpsEq::ulps_eq(&self.w_axis, &other.w_axis, epsilon, max_ulps) 129 } 130 } 131 }; 132 } 133 134 impl_approx_as_ref!(f32, Mat2); 135 impl_approx_as_ref!(f32, Mat3); 136 impl_approx_as_ref!(f32, Mat4); 137 impl_approx_as_ref!(f32, Quat); 138 impl_approx_as_ref!(f32, Vec2); 139 impl_approx_as_ref!(f32, Vec3); 140 impl_approx_as_ref!(f32, Vec4); 141 impl_approx_as_ref!(f32, Vec3A); 142 143 impl_approx_xzy_axes!(f32, Affine2); 144 impl_approx_xzyw_axes!(f32, Affine3A); 145 impl_approx_xzy_axes!(f32, Mat3A); 146 147 impl_approx_xzy_axes!(f64, DAffine2); 148 impl_approx_xzyw_axes!(f64, DAffine3); 149 impl_approx_as_ref!(f64, DMat2); 150 impl_approx_as_ref!(f64, DMat3); 151 impl_approx_as_ref!(f64, DMat4); 152 impl_approx_as_ref!(f64, DQuat); 153 impl_approx_as_ref!(f64, DVec2); 154 impl_approx_as_ref!(f64, DVec3); 155 impl_approx_as_ref!(f64, DVec4); 156 157 #[cfg(test)] 158 mod test { 159 use crate::*; 160 use approx::*; 161 162 macro_rules! impl_approx_test { 163 ($prim:ident, $type:ident, $ones:expr) => { 164 let one_eps = $ones * $type::default_epsilon(); 165 let two_eps = one_eps + one_eps; 166 167 let one_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 1); 168 let four_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 16); 169 170 approx::assert_abs_diff_eq!($ones, $ones); 171 approx::assert_abs_diff_eq!($ones, $ones + one_eps); 172 approx::assert_abs_diff_eq!($ones, $ones - one_eps); 173 174 approx::assert_abs_diff_ne!($ones, $ones + two_eps); 175 approx::assert_abs_diff_ne!($ones, $ones - two_eps); 176 177 approx::assert_relative_eq!($ones, $ones); 178 approx::assert_relative_ne!($ones, $ones - $ones); 179 180 // defaults to 4 ulps and I have no idea how to pass other parameters to this macro :) 181 approx::assert_ulps_eq!($ones, one_ulp); 182 approx::assert_ulps_ne!($ones, four_ulp); 183 }; 184 ($prim:ident, $type:ident) => { 185 impl_approx_test!($prim, $type, $type::ONE) 186 }; 187 } 188 189 #[test] test_approx()190 fn test_approx() { 191 const ONESF32: [f32; 16] = [1.0; 16]; 192 193 impl_approx_test!(f32, Vec2); 194 impl_approx_test!(f32, Vec3); 195 impl_approx_test!(f32, Vec3A); 196 impl_approx_test!(f32, Vec4); 197 impl_approx_test!(f32, Quat, Quat::from_slice(&ONESF32)); 198 impl_approx_test!(f32, Mat2, Mat2::from_cols_slice(&ONESF32)); 199 impl_approx_test!(f32, Mat3, Mat3::from_cols_slice(&ONESF32)); 200 impl_approx_test!(f32, Mat3A, Mat3A::from_cols_slice(&ONESF32)); 201 impl_approx_test!(f32, Mat4, Mat4::from_cols_slice(&ONESF32)); 202 203 const ONESF64: [f64; 16] = [1.0; 16]; 204 impl_approx_test!(f64, DVec2); 205 impl_approx_test!(f64, DVec3); 206 impl_approx_test!(f64, DVec4); 207 impl_approx_test!(f64, DQuat, DQuat::from_slice(&ONESF64)); 208 impl_approx_test!(f64, DMat2, DMat2::from_cols_slice(&ONESF64)); 209 impl_approx_test!(f64, DMat3, DMat3::from_cols_slice(&ONESF64)); 210 impl_approx_test!(f64, DMat4, DMat4::from_cols_slice(&ONESF64)); 211 } 212 } 213