Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement approx eq trait #51

Merged
merged 1 commit into from
Feb 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ strict = []

[dependencies]
num = "0.1"
approx = "0.1"

[dependencies.phf]
version = "0.7"
optional = true

[dev-dependencies]
image = "0.4"
approx = "0.1"
clap = "1"
csv = "0.14"
rustc-serialize = "0.3"
Expand Down
94 changes: 94 additions & 0 deletions src/equality.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use num::Float;
use approx::ApproxEq;

use {Xyz, Yxy, Lab, Lch, Rgb, Hsl, Hsv , Luma, LabHue, RgbHue, flt};

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] );
impl_eq!( Hsl, [hue, saturation, lightness] );
impl_eq!( Hsv, [hue, saturation, value] );

// 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);
21 changes: 11 additions & 10 deletions src/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,9 @@ mod test {
let b = Hsl::new(0.0.into(), 1.0, 0.5);
let c = Hsl::from(Hsv::new(0.0.into(), 1.0, 1.0));

assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);

}

#[test]
Expand All @@ -289,8 +290,8 @@ mod test {
let b = Hsl::new(30.0.into(), 1.0, 0.5);
let c = Hsl::from(Hsv::new(30.0.into(), 1.0, 1.0));

assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -299,8 +300,8 @@ mod test {
let b = Hsl::new(120.0.into(), 1.0, 0.5);
let c = Hsl::from(Hsv::new(120.0.into(), 1.0, 1.0));

assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -309,8 +310,8 @@ mod test {
let b = Hsl::new(240.0.into(), 1.0, 0.5);
let c = Hsl::from(Hsv::new(240.0.into(), 1.0, 1.0));

assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -319,8 +320,8 @@ mod test {
let b = Hsl::new(270.0.into(), 1.0, 0.5);
let c = Hsl::from(Hsv::new(270.0.into(), 1.0, 1.0));

assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand Down
22 changes: 11 additions & 11 deletions src/hsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl<T: Float> FromColor<T> for Hsv<T> {
T::one() - hsl.lightness
};
let mut s = T::zero();

// avoid divide by zero
let denom = hsl.lightness + x;
if denom.is_normal() {
Expand Down Expand Up @@ -279,8 +279,8 @@ mod test {
let b = Hsv::new(0.0.into(), 1.0, 1.0);
let c = Hsv::from(Hsl::new(0.0.into(), 1.0, 0.5));

assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -289,8 +289,8 @@ mod test {
let b = Hsv::new(30.0.into(), 1.0, 1.0);
let c = Hsv::from(Hsl::new(30.0.into(), 1.0, 0.5));

assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -299,8 +299,8 @@ mod test {
let b = Hsv::new(120.0.into(), 1.0, 1.0);
let c = Hsv::from(Hsl::new(120.0.into(), 1.0, 0.5));

assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -309,8 +309,8 @@ mod test {
let b = Hsv::new(240.0.into(), 1.0, 1.0);
let c = Hsv::from(Hsl::new(240.0.into(), 1.0, 0.5));

assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand All @@ -319,8 +319,8 @@ mod test {
let b = Hsv::new(270.0.into(), 1.0, 1.0);
let c = Hsv::from(Hsl::new(270.0.into(), 1.0, 0.5));

assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
assert_relative_eq!(a, b);
assert_relative_eq!(a, c);
}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions src/lab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,21 +266,21 @@ mod test {
fn red() {
let a = Lab::from(Rgb::new(1.0, 0.0, 0.0));
let b = Lab::new(53.23288 / 100.0, 80.09246 / 128.0, 67.2031 / 128.0);
assert_approx_eq!(a, b, [l, a, b]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
fn green() {
let a = Lab::from(Rgb::new(0.0, 1.0, 0.0));
let b = Lab::new(87.73704 / 100.0, -86.184654 / 128.0, 83.18117 / 128.0);
assert_approx_eq!(a, b, [l, a, b]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
fn blue() {
let a = Lab::from(Rgb::new(0.0, 0.0, 1.0));
let b = Lab::new(32.302586 / 100.0, 79.19668 / 128.0, -107.863686 / 128.0);
assert_approx_eq!(a, b, [l, a, b]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
Expand Down
15 changes: 1 addition & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
#![cfg_attr(feature = "strict", deny(missing_docs))]
#![cfg_attr(feature = "strict", deny(warnings))]

#[cfg(test)]
#[macro_use]
extern crate approx;

Expand All @@ -70,19 +69,6 @@ pub use yxy::{Yxy, Yxya};
pub use hues::{LabHue, RgbHue};
pub use convert::{FromColor, IntoColor};

//Helper macro for approximate component wise comparison. Most color spaces
//are in roughly the same ranges, so this epsilon should be alright.
#[cfg(test)]
macro_rules! assert_approx_eq {
($a:ident, $b:ident, [$($components:ident),+]) => ({
$(
let a: f32 = $a.$components.into();
let b: f32 = $b.$components.into();
assert_relative_eq!(a, b, epsilon = 0.0001);
)+
})
}

//Helper macro for checking ranges and clamping.
#[cfg(test)]
macro_rules! assert_ranges {
Expand Down Expand Up @@ -249,6 +235,7 @@ mod hues;

mod tristimulus;
mod convert;
mod equality;

macro_rules! make_color {
($(
Expand Down
8 changes: 4 additions & 4 deletions src/xyz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,28 +274,28 @@ mod test {
fn luma() {
let a = Xyz::from(Luma::new(0.5));
let b = Xyz::new(0.475235, 0.5, 0.544415);
assert_approx_eq!(a, b, [x, y, z]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
fn red() {
let a = Xyz::from(Rgb::new(1.0, 0.0, 0.0));
let b = Xyz::new(0.41240, 0.21260, 0.01930);
assert_approx_eq!(a, b, [x, y, z]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
fn green() {
let a = Xyz::from(Rgb::new(0.0, 1.0, 0.0));
let b = Xyz::new(0.35760, 0.71520, 0.11920);
assert_approx_eq!(a, b, [x, y, z]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
fn blue() {
let a = Xyz::from(Rgb::new(0.0, 0.0, 1.0));
let b = Xyz::new(0.18050, 0.07220, 0.95030);
assert_approx_eq!(a, b, [x, y, z]);
assert_relative_eq!(a, b, epsilon = 0.0001);
}

#[test]
Expand Down
8 changes: 4 additions & 4 deletions src/yxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,28 +243,28 @@ mod test {
fn luma() {
let a = Yxy::from(Luma::new(0.5));
let b = Yxy::new(0.312727, 0.329023, 0.5);
assert_approx_eq!(a, b, [x, y, luma]);
assert_relative_eq!(a, b, epsilon = 0.000001);
}

#[test]
fn red() {
let a = Yxy::from(Rgb::new(1.0, 0.0, 0.0));
let b = Yxy::new(0.64, 0.33, 0.212673);
assert_approx_eq!(a, b, [x, y, luma]);
assert_relative_eq!(a, b, epsilon = 0.000001);
}

#[test]
fn green() {
let a = Yxy::from(Rgb::new(0.0, 1.0, 0.0));
let b = Yxy::new(0.3, 0.6, 0.715152);
assert_approx_eq!(a, b, [x, y, luma]);
assert_relative_eq!(a, b, epsilon = 0.000001);
}

#[test]
fn blue() {
let a = Yxy::from(Rgb::new(0.0, 0.0, 1.0));
let b = Yxy::new(0.15, 0.06, 0.072175);
assert_approx_eq!(a, b, [x, y, luma]);
assert_relative_eq!(a, b, epsilon = 0.000001);
}

#[test]
Expand Down
21 changes: 0 additions & 21 deletions tests/color_convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,4 @@ extern crate rustc_serialize;
extern crate csv;
extern crate palette;


macro_rules! assert_color_eq {
($a:expr, $b:expr, [$($components:ident),+]) => ({
$(
let a: f32 = $a.$components.into();
let b: f32 = $b.$components.into();
assert_relative_eq!(a, b, epsilon = 0.05);
)+
})
}

// Check if the hue diff equal to zero
macro_rules! assert_color_hue_eq {
($a:expr, $b:expr, [$($components:ident),+], $eps:expr) => ({
$(
let out = ($a.$components - $b.$components).into();
assert_relative_eq!(out, 0.0, epsilon = $eps);
)+
})
}

mod convert;
4 changes: 2 additions & 2 deletions tests/convert/data_cie_15_2004.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ fn load_data() -> Vec<Cie2004> {
}

fn check_equal(src: &Cie2004, tgt: &Cie2004) {
assert_color_eq!(src.xyz, tgt.xyz, [x,y,z]);
assert_color_eq!(src.yxy, tgt.yxy, [x,y,luma]);
assert_relative_eq!(src.xyz, tgt.xyz, epsilon = 0.0001);
assert_relative_eq!(src.yxy, tgt.yxy, epsilon = 0.0001);
}


Expand Down
Loading