1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use num::Float;
use approx::ApproxEq;

use {Xyz, Yxy, Lab, Lch, Rgb, Hsl, Hsv, Hwb, Luma, LabHue, RgbHue, flt};
use pixel::{Srgb, GammaRgb};

macro_rules! impl_eq {
    (  $self_ty: ident , [$($element: ident),+]) => {
        impl<T: Float + ApproxEq> ApproxEq for $self_ty<T>
        where T::Epsilon: Copy + Float
        {
            type Epsilon = <T as ApproxEq>::Epsilon;

            fn default_epsilon() -> Self::Epsilon {
                T::default_epsilon()
            }
            fn default_max_relative() -> Self::Epsilon {
                T::default_max_relative()
            }
            fn default_max_ulps() -> u32 {
                T::default_max_ulps()
            }
            fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
                $( self.$element.relative_eq(&other.$element, epsilon, max_relative) )&&+
            }
            fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool{
                $( self.$element.ulps_eq(&other.$element, epsilon, max_ulps) )&&+
            }

            fn relative_ne(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
                $( self.$element.relative_ne(&other.$element, epsilon, max_relative) )&&+
            }
            fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
                $( self.$element.ulps_ne(&other.$element, epsilon, max_ulps) )&&+
            }
        }
    }
}

impl_eq!( Xyz, [x, y, z] );
impl_eq!( Yxy, [y, x, luma] );
impl_eq!( Lab, [l, a, b] );
impl_eq!( Rgb, [red, blue, green] );
impl_eq!( Luma, [luma] );
impl_eq!( Lch, [l, chroma, hue] );
impl_eq!( Hsl, [hue, saturation, lightness] );
impl_eq!( Hsv, [hue, saturation, value] );
impl_eq!( Hwb, [hue, whiteness, blackness] );
impl_eq!( Srgb, [red, blue, green, alpha] );
impl_eq!( GammaRgb, [red, blue, green, alpha, gamma] );

// For hues diffence is calculated and compared to zero. However due to the way floating point's
// work this is not so simple
// reference
// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
//
// The recommendation is use 180 * epsilon as the epsilon and do not compare by ulps.
// Because of this we loose some precision for values close to 0.0.
macro_rules! impl_eq_hue {
    (  $self_ty: ident ) => {
        impl<T: Float + ApproxEq> ApproxEq for $self_ty<T>
        where <T as ApproxEq>::Epsilon: Float
        {
            type Epsilon = <T as ApproxEq>::Epsilon;

            fn default_epsilon() -> Self::Epsilon {
                T::default_epsilon() * flt(180.0)
            }
            fn default_max_relative() -> Self::Epsilon {
                T::default_max_relative() * flt(180.0)
            }
            fn default_max_ulps() -> u32 {
                T::default_max_ulps() * 180
            }
            fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
                let diff: T = (*self - *other).to_degrees();
                T::relative_eq(&diff, &T::zero(), epsilon, max_relative)
            }
            fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool{
                let diff: T = (*self - *other).to_degrees();
                T::ulps_eq(&diff, &T::zero(), epsilon, max_ulps)
            }

            fn relative_ne(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
                let diff: T = (*self - *other).to_degrees();
                T::relative_ne(&diff, &T::zero(), epsilon, max_relative)
            }
            fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
                let diff: T = (*self - *other).to_degrees();
                T::ulps_ne(&diff, &T::zero(), epsilon, max_ulps)
            }
        }

    }
}

impl_eq_hue!( LabHue);
impl_eq_hue!( RgbHue);