1use crate::{
2 Affine2, Affine3A, DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2,
3 Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4,
4};
5use approx::{AbsDiffEq, RelativeEq, UlpsEq};
6
7macro_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
45macro_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
88macro_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
134impl_approx_as_ref!(f32, Mat2);
135impl_approx_as_ref!(f32, Mat3);
136impl_approx_as_ref!(f32, Mat4);
137impl_approx_as_ref!(f32, Quat);
138impl_approx_as_ref!(f32, Vec2);
139impl_approx_as_ref!(f32, Vec3);
140impl_approx_as_ref!(f32, Vec4);
141impl_approx_as_ref!(f32, Vec3A);
142
143impl_approx_xzy_axes!(f32, Affine2);
144impl_approx_xzyw_axes!(f32, Affine3A);
145impl_approx_xzy_axes!(f32, Mat3A);
146
147impl_approx_xzy_axes!(f64, DAffine2);
148impl_approx_xzyw_axes!(f64, DAffine3);
149impl_approx_as_ref!(f64, DMat2);
150impl_approx_as_ref!(f64, DMat3);
151impl_approx_as_ref!(f64, DMat4);
152impl_approx_as_ref!(f64, DQuat);
153impl_approx_as_ref!(f64, DVec2);
154impl_approx_as_ref!(f64, DVec3);
155impl_approx_as_ref!(f64, DVec4);
156
157#[cfg(test)]
158mod 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 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 macro_rules! impl_affine_approx_test {
190 ($prim:ident, $type:ident, $from:ident, $mat:ident, $ones:expr) => {
191 let ones = $mat::from($ones);
192 let one_eps = ones * $mat::default_epsilon();
193 let two_eps = one_eps + one_eps;
194
195 let one_ulp = ones * $prim::from_bits($prim::to_bits(1.0) + 1);
196 let four_ulp = ones * $prim::from_bits($prim::to_bits(1.0) + 16);
197
198 approx::assert_abs_diff_eq!($type::$from(ones), $type::$from(ones));
199 approx::assert_abs_diff_eq!($type::$from(ones), $type::$from(ones + one_eps));
200 approx::assert_abs_diff_eq!($type::$from(ones), $type::$from(ones - one_eps));
201
202 approx::assert_abs_diff_ne!($type::$from(ones), $type::$from(ones + two_eps));
203 approx::assert_abs_diff_ne!($type::$from(ones), $type::$from(ones - two_eps));
204
205 approx::assert_relative_eq!($type::$from(ones), $type::$from(ones));
206 approx::assert_relative_ne!($type::$from(ones), $type::$from(ones - ones));
207
208 approx::assert_ulps_eq!($type::$from(ones), $type::$from(one_ulp));
210 approx::assert_ulps_ne!($type::$from(ones), $type::$from(four_ulp));
211 };
212 }
213 #[test]
214 fn test_approx() {
215 const ONESF32: [f32; 16] = [1.0; 16];
216
217 impl_approx_test!(f32, Vec2);
218 impl_approx_test!(f32, Vec3);
219 impl_approx_test!(f32, Vec3A);
220 impl_approx_test!(f32, Vec4);
221 impl_approx_test!(f32, Quat, Quat::from_slice(&ONESF32));
222 impl_approx_test!(f32, Mat2, Mat2::from_cols_slice(&ONESF32));
223 impl_approx_test!(f32, Mat3, Mat3::from_cols_slice(&ONESF32));
224 impl_approx_test!(f32, Mat3A, Mat3A::from_cols_slice(&ONESF32));
225 impl_approx_test!(f32, Mat4, Mat4::from_cols_slice(&ONESF32));
226 impl_affine_approx_test!(
227 f32,
228 Affine2,
229 from_mat3,
230 Mat3,
231 Affine2::from_cols_slice(&ONESF32)
232 );
233 impl_affine_approx_test!(
234 f32,
235 Affine3A,
236 from_mat4,
237 Mat4,
238 Affine3A::from_cols_slice(&ONESF32)
239 );
240
241 const ONESF64: [f64; 16] = [1.0; 16];
242 impl_approx_test!(f64, DVec2);
243 impl_approx_test!(f64, DVec3);
244 impl_approx_test!(f64, DVec4);
245 impl_approx_test!(f64, DQuat, DQuat::from_slice(&ONESF64));
246 impl_approx_test!(f64, DMat2, DMat2::from_cols_slice(&ONESF64));
247 impl_approx_test!(f64, DMat3, DMat3::from_cols_slice(&ONESF64));
248 impl_approx_test!(f64, DMat4, DMat4::from_cols_slice(&ONESF64));
249 impl_affine_approx_test!(
250 f64,
251 DAffine2,
252 from_mat3,
253 DMat3,
254 DAffine2::from_cols_slice(&ONESF64)
255 );
256 impl_affine_approx_test!(
257 f64,
258 DAffine3,
259 from_mat4,
260 DMat4,
261 DAffine3::from_cols_slice(&ONESF64)
262 );
263 }
264}