Skip to main content

glam/features/
impl_approx_05.rs

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