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