glam/features/
impl_approx.rs

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            // 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    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            // defaults to 4 ulps and I have no idea how to pass other parameters to this macro :)
209            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}