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

Fix or relax some color ranges and clamping #25

Merged
merged 4 commits into from
Jan 28, 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
20 changes: 19 additions & 1 deletion src/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut, Add, Sub, Mul, Div};

use num::Float;

use {Mix, Shade, GetHue, Hue, Saturate};
use {Mix, Shade, GetHue, Hue, Saturate, Limited, clamp};

///An alpha component wrapper for colors.
#[derive(Clone, Copy, Debug, PartialEq)]
Expand Down Expand Up @@ -86,6 +86,24 @@ impl<C: Saturate> Saturate for Alpha<C, C::Scalar> {
}
}

impl<C: Limited, T: Float> Limited for Alpha<C, T> {
fn is_valid(&self) -> bool {
self.color.is_valid() && self.alpha >= T::zero() && self.alpha <= T::one()
}

fn clamp(&self) -> Alpha<C, T> {
Alpha {
color: self.color.clamp(),
alpha: clamp(self.alpha, T::zero(), T::one()),
}
}

fn clamp_self(&mut self) {
self.color.clamp_self();
self.alpha = clamp(self.alpha, T::zero(), T::one());
}
}

impl<C: Default, T: Float> Default for Alpha<C, T> {
fn default() -> Alpha<C, T> {
Alpha {
Expand Down
21 changes: 18 additions & 3 deletions src/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use num::traits::Float;

use std::ops::{Add, Sub};

use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsv, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsv, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};

///Linear HSL with an alpha component. See the [`Hsla` implementation in `Alpha`](struct.Alpha.html#Hsla).
pub type Hsla<T = f32> = Alpha<Hsl<T>, T>;
Expand Down Expand Up @@ -54,7 +54,7 @@ impl<T: Float> Alpha<Hsl<T>, T> {
}
}

impl<T: Float> ColorSpace for Hsl<T> {
impl<T: Float> Limited for Hsl<T> {
fn is_valid(&self) -> bool {
self.saturation >= T::zero() && self.saturation <= T::one() &&
self.lightness >= T::zero() && self.lightness <= T::one()
Expand Down Expand Up @@ -290,7 +290,7 @@ impl<T: Float> From<Hsv<T>> for Hsl<T> {
#[cfg(test)]
mod test {
use super::Hsl;
use ::{Rgb, Hsv};
use {Rgb, Hsv};

#[test]
fn red() {
Expand Down Expand Up @@ -341,4 +341,19 @@ mod test {
assert_approx_eq!(a, b, [hue, saturation, lightness]);
assert_approx_eq!(a, c, [hue, saturation, lightness]);
}

#[test]
fn ranges() {
assert_ranges!{
Hsl;
limited {
saturation: 0.0 => 1.0,
lightness: 0.0 => 1.0
}
limited_min {}
unlimited {
hue: -360.0 => 360.0
}
}
}
}
19 changes: 17 additions & 2 deletions src/hsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use num::traits::Float;

use std::ops::{Add, Sub};

use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsl, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsl, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};

///Linear HSV with an alpha component. See the [`Hsva` implementation in `Alpha`](struct.Alpha.html#Hsva).
pub type Hsva<T = f32> = Alpha<Hsv<T>, T>;
Expand Down Expand Up @@ -53,7 +53,7 @@ impl<T: Float> Alpha<Hsv<T>, T> {
}
}

impl<T: Float> ColorSpace for Hsv<T> {
impl<T: Float> Limited for Hsv<T> {
fn is_valid(&self) -> bool {
self.saturation >= T::zero() && self.saturation <= T::one() &&
self.value >= T::zero() && self.value <= T::one()
Expand Down Expand Up @@ -337,4 +337,19 @@ mod test {
assert_approx_eq!(a, b, [hue, saturation, value]);
assert_approx_eq!(a, c, [hue, saturation, value]);
}

#[test]
fn ranges() {
assert_ranges!{
Hsv;
limited {
saturation: 0.0 => 1.0,
value: 0.0 => 1.0
}
limited_min {}
unlimited {
hue: -360.0 => 360.0
}
}
}
}
2 changes: 1 addition & 1 deletion src/hues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ fn normalize_angle<T: Float>(mut deg: T) -> T {
}

while deg <= -T::from(180.0).unwrap() {
deg = deg - T::from(360.0).unwrap();
deg = deg + T::from(360.0).unwrap();
}

deg
Expand Down
18 changes: 16 additions & 2 deletions src/lab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use num::traits::Float;

use std::ops::{Add, Sub, Mul, Div};

use {Color, Alpha, Rgb, Luma, Xyz, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, LabHue, clamp};
use {Color, Alpha, Rgb, Luma, Xyz, Lch, Hsv, Hsl, Limited, Mix, Shade, GetHue, LabHue, clamp};

use tristimulus::{X_N, Y_N, Z_N};

Expand Down Expand Up @@ -55,7 +55,7 @@ impl<T: Float> Alpha<Lab<T>, T> {
}
}

impl<T: Float> ColorSpace for Lab<T> {
impl<T: Float> Limited for Lab<T> {
fn is_valid(&self) -> bool {
self.l >= T::zero() && self.l <= T::one() &&
self.a >= -T::one() && self.a <= T::one() &&
Expand Down Expand Up @@ -307,4 +307,18 @@ mod test {
let b = Lab::new(32.302586 / 100.0, 79.19668 / 128.0, -107.863686 / 128.0);
assert_approx_eq!(a, b, [l, a, b]);
}

#[test]
fn ranges() {
assert_ranges!{
Lab;
limited {
l: 0.0 => 1.0,
a: -1.0 => 1.0,
b: -1.0 => 1.0
}
limited_min {}
unlimited {}
}
}
}
29 changes: 25 additions & 4 deletions src/lch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use num::traits::Float;

use std::ops::{Add, Sub};

use {Color, Alpha, ColorSpace, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp};
use {Color, Alpha, Limited, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp};

///CIE L*C*h° with an alpha component. See the [`Lcha` implementation in `Alpha`](struct.Alpha.html#Lcha).
pub type Lcha<T = f32> = Alpha<Lch<T>, T>;
Expand Down Expand Up @@ -52,10 +52,10 @@ impl<T: Float> Alpha<Lch<T>, T> {
}
}

impl<T: Float> ColorSpace for Lch<T> {
impl<T: Float> Limited for Lch<T> {
fn is_valid(&self) -> bool {
self.l >= T::zero() && self.l <= T::one() &&
self.chroma >= T::zero() && self.chroma <= T::from(1.41421356).unwrap() //should include all of L*a*b*, but will also overshoot...
self.chroma >= T::zero()
}

fn clamp(&self) -> Lch<T> {
Expand All @@ -66,7 +66,7 @@ impl<T: Float> ColorSpace for Lch<T> {

fn clamp_self(&mut self) {
self.l = clamp(self.l, T::zero(), T::one());
self.chroma = clamp(self.chroma, T::zero(), T::from(1.41421356).unwrap()); //should include all of L*a*b*, but will also overshoot...
self.chroma = self.chroma.max(T::zero())
}
}

Expand Down Expand Up @@ -235,3 +235,24 @@ impl<T: Float> From<Hsl<T>> for Lch<T> {
Lab::from(hsl).into()
}
}

#[cfg(test)]
mod test {
use Lch;

#[test]
fn ranges() {
assert_ranges!{
Lch;
limited {
l: 0.0 => 1.0
}
limited_min {
chroma: 0.0 => 2.0
}
unlimited {
hue: -360.0 => 360.0
}
}
}
}
150 changes: 148 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,152 @@ macro_rules! assert_approx_eq {
})
}

//Helper macro for checking ranges and clamping.
#[cfg(test)]
macro_rules! assert_ranges {
(@make_tuple $first:pat, $next:ident,) => (($first, $next));

(@make_tuple $first:pat, $next:ident, $($rest:ident,)*) => (
assert_ranges!(@make_tuple ($first, $next), $($rest,)*)
);

(
$ty:ident;
limited {$($limited:ident: $limited_from:expr => $limited_to:expr),+}
limited_min {$($limited_min:ident: $limited_min_from:expr => $limited_min_to:expr),*}
unlimited {$($unlimited:ident: $unlimited_from:expr => $unlimited_to:expr),*}
) => (
{
use std::iter::repeat;
use Limited;

{
print!("checking below limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)+

$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)*

$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)*

for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c = $ty::<f64> {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
};
let clamped = c.clamp();
let expected = $ty {
$($limited: $limited_from.into(),)+
$($limited_min: $limited_min_from.into(),)*
$($unlimited: $unlimited.into(),)*
};

assert!(!c.is_valid());
assert_eq!(clamped, expected);
}

println!("ok")
}

{
print!("checking within limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)+

$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)*

$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)*

for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c = $ty::<f64> {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
};
let clamped = c.clamp();

assert!(c.is_valid());
assert_eq!(clamped, c);
}

println!("ok")
}

{
print!("checking above limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)+

$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)*

$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)*

for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c = $ty::<f64> {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
};
let clamped = c.clamp();
let expected = $ty {
$($limited: $limited_to.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
};

assert!(!c.is_valid());
assert_eq!(clamped, expected);
}

println!("ok")
}
}
);
}

pub mod gradient;
pub mod pixel;

Expand Down Expand Up @@ -320,8 +466,8 @@ make_color! {
}
}

///Common functionality for color spaces.
pub trait ColorSpace {
///A trait for clamping and checking if colors are within their ranges.
pub trait Limited {
///Check if the color's components are within the expected ranges.
fn is_valid(&self) -> bool;

Expand Down
Loading