1 use float::Float;
2 
3 use approx::{AbsDiffEq, RelativeEq, UlpsEq};
4 
5 use {cast, Component, Lab, LabHue, Lch, RgbHue, Xyz, Yxy};
6 use white_point::WhitePoint;
7 
8 macro_rules! impl_eq {
9     (  $self_ty: ident , [$($element: ident),+]) => {
10         impl<Wp, T> AbsDiffEq for $self_ty<Wp, T>
11         where T: Component + Float + AbsDiffEq,
12             T::Epsilon: Copy + Float,
13             Wp: WhitePoint + PartialEq
14         {
15             type Epsilon = T::Epsilon;
16 
17             fn default_epsilon() -> Self::Epsilon {
18                 T::default_epsilon()
19             }
20 
21             fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
22                 $( self.$element.abs_diff_eq(&other.$element, epsilon) )&&+
23             }
24             fn abs_diff_ne(&self, other: &Self, epsilon: T::Epsilon) -> bool {
25                 $( self.$element.abs_diff_ne(&other.$element, epsilon) )||+
26             }
27         }
28 
29         impl<Wp, T> RelativeEq for $self_ty<Wp, T>
30         where T: Component + Float + RelativeEq,
31             T::Epsilon: Copy + Float,
32             Wp: WhitePoint + PartialEq
33         {
34             fn default_max_relative() -> T::Epsilon {
35                 T::default_max_relative()
36             }
37 
38             fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
39                 $( self.$element.relative_eq(&other.$element, epsilon, max_relative) )&&+
40             }
41             fn relative_ne(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
42                 $( self.$element.relative_ne(&other.$element, epsilon, max_relative) )||+
43             }
44         }
45 
46         impl<Wp, T> UlpsEq for $self_ty<Wp, T>
47         where T: Component + Float + UlpsEq,
48             T::Epsilon: Copy + Float,
49             Wp: WhitePoint + PartialEq
50         {
51             fn default_max_ulps() -> u32 {
52                 T::default_max_ulps()
53             }
54 
55             fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
56                 $( self.$element.ulps_eq(&other.$element, epsilon, max_ulps) )&&+
57             }
58             fn ulps_ne(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
59                 $( self.$element.ulps_ne(&other.$element, epsilon, max_ulps) )||+
60             }
61         }
62     }
63 }
64 
65 impl_eq!(Xyz, [x, y, z]);
66 impl_eq!(Yxy, [y, x, luma]);
67 impl_eq!(Lab, [l, a, b]);
68 impl_eq!(Lch, [l, chroma, hue]);
69 
70 // For hues, the difference is calculated and compared to zero. However due to
71 // the way floating point's work this is not so simple.
72 //
73 // Reference:
74 // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
75 //
76 // The recommendation is use 180 * epsilon as the epsilon and do not compare by
77 // ulps. Because of this we loose some precision for values close to 0.0.
78 macro_rules! impl_eq_hue {
79     (  $self_ty: ident ) => {
80         impl<T: Float + AbsDiffEq> AbsDiffEq for $self_ty<T>
81         where T::Epsilon: Float
82         {
83             type Epsilon = T::Epsilon;
84 
85             fn default_epsilon() -> Self::Epsilon {
86                 T::default_epsilon() * cast(180.0)
87             }
88 
89             fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
90                 let diff: T = (*self - *other).to_degrees();
91                 T::abs_diff_eq(&diff, &T::zero(), epsilon)
92             }
93             fn abs_diff_ne(&self, other: &Self, epsilon: T::Epsilon) -> bool {
94                 let diff: T = (*self - *other).to_degrees();
95                 T::abs_diff_ne(&diff, &T::zero(), epsilon)
96             }
97         }
98 
99         impl<T: Float + RelativeEq> RelativeEq for $self_ty<T>
100         where T::Epsilon: Float
101         {
102             fn default_max_relative() -> Self::Epsilon {
103                 T::default_max_relative() * cast(180.0)
104             }
105 
106             fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
107                 let diff: T = (*self - *other).to_degrees();
108                 T::relative_eq(&diff, &T::zero(), epsilon, max_relative)
109             }
110             fn relative_ne(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
111                 let diff: T = (*self - *other).to_degrees();
112                 T::relative_ne(&diff, &T::zero(), epsilon, max_relative)
113             }
114         }
115 
116         impl<T: Float + UlpsEq> UlpsEq for $self_ty<T>
117         where T::Epsilon: Float
118         {
119             fn default_max_ulps() -> u32 {
120                 T::default_max_ulps() * 180
121             }
122 
123             fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
124                 let diff: T = (*self - *other).to_degrees();
125                 T::ulps_eq(&diff, &T::zero(), epsilon, max_ulps)
126             }
127             fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
128                 let diff: T = (*self - *other).to_degrees();
129                 T::ulps_ne(&diff, &T::zero(), epsilon, max_ulps)
130             }
131         }
132     }
133 }
134 
135 impl_eq_hue!(LabHue);
136 impl_eq_hue!(RgbHue);
137