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