From 4e5bc2303131310ec1a7fb42229ba018c3fa4192 Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Fri, 29 Jan 2016 16:59:35 +0100 Subject: [PATCH 1/2] Offer both 0 centered and positive hue -> float conversion --- src/hsl.rs | 2 +- src/hsv.rs | 2 +- src/hues.rs | 53 +++++++++++++++++++++++++++++++++++++++-------------- src/lch.rs | 2 +- src/rgb.rs | 4 ++-- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/hsl.rs b/src/hsl.rs index a0bcb7412..e01009b53 100644 --- a/src/hsl.rs +++ b/src/hsl.rs @@ -77,7 +77,7 @@ impl Mix for Hsl { fn mix(&self, other: &Hsl, factor: T) -> Hsl { let factor = clamp(factor, T::zero(), T::one()); - let hue_diff: T = (other.hue - self.hue).to_float(); + let hue_diff: T = (other.hue - self.hue).to_degrees(); Hsl { hue: self.hue + factor * hue_diff, diff --git a/src/hsv.rs b/src/hsv.rs index 8eda77db6..6ecce888f 100644 --- a/src/hsv.rs +++ b/src/hsv.rs @@ -76,7 +76,7 @@ impl Mix for Hsv { fn mix(&self, other: &Hsv, factor: T) -> Hsv { let factor = clamp(factor, T::zero(), T::one()); - let hue_diff: T = (other.hue - self.hue).to_float(); + let hue_diff: T = (other.hue - self.hue).to_degrees(); Hsv { hue: self.hue + factor * hue_diff, diff --git a/src/hues.rs b/src/hues.rs index 116236900..d4008c0bb 100644 --- a/src/hues.rs +++ b/src/hues.rs @@ -9,7 +9,7 @@ macro_rules! make_hues { $(#[$doc])+ /// ///The hue is a circular type, where `0` and `360` is the same, and - ///it's normalized to `(-180, +180]` when it's converted to a linear + ///it's normalized to `(-180, 180]` when it's converted to a linear ///number (like `f32`). This makes many calculations easier, but may ///also have some surprising effects if it's expected to act as a ///linear number. @@ -22,14 +22,24 @@ macro_rules! make_hues { $name(radians * T::from(180.0).unwrap() / T::from(PI).unwrap()) } - ///Convert the hue to radians. + ///Get the hue as degrees, in the range `(-180, 180]`. + pub fn to_degrees(self) -> T { + normalize_angle(self.0) + } + + ///Convert the hue to radians, in the range `(-π, π]`. pub fn to_radians(self) -> T { normalize_angle(self.0) * T::from(PI).unwrap() / T::from(180.0).unwrap() } - ///Returns the saved Hue value - pub fn to_float(self) -> T { - normalize_angle(self.0) + ///Convert the hue to positive degrees, in the range `[0, 360)`. + pub fn to_positive_degrees(self) -> T { + normalize_angle_positive(self.0) + } + + ///Convert the hue to positive radians, in the range `[0, 2π)`. + pub fn to_positive_radians(self) -> T { + normalize_angle_positive(self.0) * T::from(PI).unwrap() / T::from(180.0).unwrap() } } @@ -41,13 +51,13 @@ macro_rules! make_hues { impl Into for $name { fn into(self) -> f64 { - normalize_angle(self.0) as f64 + normalize_angle(self.0) } } impl Into for $name { fn into(self) -> f32 { - normalize_angle(self.0) as f32 + normalize_angle(self.0) } } impl Into for $name { @@ -58,15 +68,15 @@ macro_rules! make_hues { impl PartialEq for $name { fn eq(&self, other: &$name) -> bool { - let hue_s: T = (*self).to_float(); - let hue_o: T = (*other).to_float(); + let hue_s: T = (*self).to_degrees(); + let hue_o: T = (*other).to_degrees(); hue_s.eq(&hue_o) } } impl PartialEq for $name { fn eq(&self, other: &T) -> bool { - let hue: T = (*self).to_float(); + let hue: T = (*self).to_degrees(); hue.eq(&normalize_angle(*other)) } } @@ -121,12 +131,27 @@ make_hues! { } fn normalize_angle(mut deg: T) -> T { - while deg > T::from(180.0).unwrap() { - deg = deg - T::from(360.0).unwrap(); + let c180 = T::from(180.0).unwrap(); + let c360 = T::from(360.0).unwrap(); + while deg > c180 { + deg = deg - c360; + } + + while deg <= -c180 { + deg = deg + c360; + } + + deg +} + +fn normalize_angle_positive(mut deg: T) -> T { + let c360 = T::from(360.0).unwrap(); + while deg >= c360 { + deg = deg - c360; } - while deg <= -T::from(180.0).unwrap() { - deg = deg + T::from(360.0).unwrap(); + while deg < T::zero() { + deg = deg + c360; } deg diff --git a/src/lch.rs b/src/lch.rs index b58c9a65d..6d749f72f 100644 --- a/src/lch.rs +++ b/src/lch.rs @@ -75,7 +75,7 @@ impl Mix for Lch { fn mix(&self, other: &Lch, factor: T) -> Lch { let factor = clamp(factor, T::zero(), T::one()); - let hue_diff: T = (other.hue - self.hue).to_float(); + let hue_diff: T = (other.hue - self.hue).to_degrees(); Lch { l: self.l + factor * (other.l - self.l), chroma: self.chroma + factor * (other.chroma - self.chroma), diff --git a/src/rgb.rs b/src/rgb.rs index 16e3aa9d6..a73441a69 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -325,7 +325,7 @@ impl From> for Rgb { impl From> for Rgb { fn from(hsv: Hsv) -> Rgb { let c = hsv.value * hsv.saturation; - let h = ((hsv.hue.to_float() + T::from(360.0).unwrap()) % T::from(360.0).unwrap()) / T::from(60.0).unwrap(); + let h = hsv.hue.to_positive_degrees() / T::from(60.0).unwrap(); let x = c * (T::one() - (h % T::from(2.0).unwrap() - T::one()).abs()); let m = hsv.value - c; @@ -355,7 +355,7 @@ impl From> for Rgb { impl From> for Rgb { fn from(hsl: Hsl) -> Rgb { let c = (T::one() - (T::from(2.0).unwrap() * hsl.lightness - T::one()).abs()) * hsl.saturation; - let h = ((hsl.hue.to_float() + T::from(360.0).unwrap()) % T::from(360.0).unwrap()) / T::from(60.0).unwrap(); + let h = hsl.hue.to_positive_degrees() / T::from(60.0).unwrap(); let x = c * (T::one() - (h % T::from(2.0).unwrap() - T::one()).abs()); let m = hsl.lightness - T::from(0.5).unwrap() * c; From 275a4eecdc736cece389418934b06a3c35dba6cc Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Fri, 29 Jan 2016 17:30:21 +0100 Subject: [PATCH 2/2] Add test for hue -> float conversions --- src/hues.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/hues.rs b/src/hues.rs index d4008c0bb..cc3f7fde9 100644 --- a/src/hues.rs +++ b/src/hues.rs @@ -156,3 +156,23 @@ fn normalize_angle_positive(mut deg: T) -> T { deg } + +#[cfg(test)] +mod test { + use RgbHue; + + #[test] + fn float_conversion() { + for i in -180..180 { + let hue = RgbHue::from(4.0 * i as f32); + + let degs = hue.to_degrees(); + assert!(degs > -180.0 && degs <= 180.0); + + let pos_degs = hue.to_positive_degrees(); + assert!(pos_degs >= 0.0 && pos_degs < 360.0); + + assert_eq!(RgbHue::from(degs), RgbHue::from(pos_degs)); + } + } +}