Skip to content

Change Mix, Shade and Saturate to use an associated type #23

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

Merged
merged 1 commit into from
Jan 24, 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 examples/readme_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn display_colors(filename: &str, colors: &[[u8; 3]]) {
}
}

fn display_gradients<T: Float, A: Mix<T> + Clone, B: Mix<T> + Clone>(filename: &str, grad1: Gradient<T, A>, grad2: Gradient<T, B>)
fn display_gradients<T: Float, A: Mix<Scalar=T> + Clone, B: Mix<Scalar=T> + Clone>(filename: &str, grad1: Gradient<A>, grad2: Gradient<B>)
where Rgb<T>: From<A>,
Rgb<T>: From<B>
{
Expand Down
64 changes: 32 additions & 32 deletions src/gradient.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//!Types for interpolation between multiple colors.

use num::traits::Float;
use num::{Float, One, Zero, NumCast};
use std::cmp::max;

use Mix;
Expand All @@ -14,18 +14,18 @@ use Mix;
///the domain of the gradient will have the same color as the closest control
///point.
#[derive(Clone, Debug)]
pub struct Gradient<T: Float, C: Mix<T> + Clone>(Vec<(T, C)>);
pub struct Gradient<C: Mix + Clone>(Vec<(C::Scalar, C)>);

impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {
impl<C: Mix + Clone> Gradient<C> {
///Create a gradient of evenly spaced colors with the domain [0.0, 1.0].
///There must be at least one color.
pub fn new<I: IntoIterator<Item = C>>(colors: I) -> Gradient<T, C> {
let mut points: Vec<_> = colors.into_iter().map(|c| (T::zero(), c)).collect();
pub fn new<I: IntoIterator<Item = C>>(colors: I) -> Gradient<C> {
let mut points: Vec<_> = colors.into_iter().map(|c| (C::Scalar::zero(), c)).collect();
assert!(points.len() > 0);
let step_size = T::one() / T::from(max(points.len() - 1, 1) as f64).unwrap();
let step_size = C::Scalar::one() / <C::Scalar as NumCast>::from(max(points.len() - 1, 1) as f64).unwrap();

for (i, &mut (ref mut p, _)) in points.iter_mut().enumerate() {
*p = T::from(i).unwrap() * step_size;
*p = <C::Scalar as NumCast>::from(i).unwrap() * step_size;
}

Gradient(points)
Expand All @@ -34,7 +34,7 @@ impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {
///Create a gradient of colors with custom spacing and domain. There must be
///at least one color and they are expected to be ordered by their
///position value.
pub fn with_domain(colors: Vec<(T, C)>) -> Gradient<T, C> {
pub fn with_domain(colors: Vec<(C::Scalar, C)>) -> Gradient<C> {
assert!(colors.len() > 0);

//Maybe sort the colors?
Expand All @@ -43,7 +43,7 @@ impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {

///Get a color from the gradient. The color of the closest control point
///will be returned if `i` is outside the domain.
pub fn get(&self, i: T) -> C {
pub fn get(&self, i: C::Scalar) -> C {
let &(mut min, ref min_color) = self.0.get(0).expect("a Gradient must contain at least one color");
let mut min_color = min_color;
let mut min_index = 0;
Expand Down Expand Up @@ -82,7 +82,7 @@ impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {
}

///Take `n` evenly spaced colors from the gradient, as an iterator.
pub fn take(&self, n: usize) -> Take<T, C> {
pub fn take(&self, n: usize) -> Take<C> {
let (min, max) = self.domain();

Take {
Expand All @@ -95,36 +95,36 @@ impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {
}

///Slice this gradient to limit its domain.
pub fn slice<R: Into<Range<T>>>(&self, range: R) -> Slice<T, C> {
pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
Slice {
gradient: self,
range: range.into(),
}
}

///Get the limits of this gradient's domain.
pub fn domain(&self) -> (T, T) {
pub fn domain(&self) -> (C::Scalar, C::Scalar) {
let &(min, _) = self.0.get(0).expect("a Gradient must contain at least one color");
let &(max, _) = self.0.last().expect("a Gradient must contain at least one color");
(min, max)
}
}

///An iterator over interpolated colors.
pub struct Take<'a, T: Float + 'a, C: Mix<T> + Clone + 'a> {
gradient: MaybeSlice<'a, T, C>,
from: T,
diff: T,
pub struct Take<'a, C: Mix + Clone + 'a> {
gradient: MaybeSlice<'a, C>,
from: C::Scalar,
diff: C::Scalar,
len: usize,
current: usize,
}

impl<'a, T: Float, C: Mix<T> + Clone> Iterator for Take<'a, T, C> {
impl<'a, C: Mix + Clone> Iterator for Take<'a, C> {
type Item = C;

fn next(&mut self) -> Option<C> {
if self.current < self.len {
let i = self.from + T::from(self.current).unwrap() * (self.diff / T::from(self.len).unwrap());
let i = self.from + <C::Scalar as NumCast>::from(self.current).unwrap() * (self.diff / <C::Scalar as NumCast>::from(self.len).unwrap());
self.current += 1;
Some(self.gradient.get(i))
} else {
Expand All @@ -137,25 +137,25 @@ impl<'a, T: Float, C: Mix<T> + Clone> Iterator for Take<'a, T, C> {
}
}

impl<'a, T: Float, C: Mix<T> + Clone> ExactSizeIterator for Take<'a, T, C> {}
impl<'a, C: Mix + Clone> ExactSizeIterator for Take<'a, C> {}


///A slice of a Gradient that limits its domain.
#[derive(Clone, Debug)]
pub struct Slice<'a, T: Float + 'a, C: Mix<T> + Clone + 'a> {
gradient: &'a Gradient<T, C>,
range: Range<T>,
pub struct Slice<'a, C: Mix + Clone + 'a> {
gradient: &'a Gradient<C>,
range: Range<C::Scalar>,
}

impl<'a, T: Float, C: Mix<T> + Clone> Slice<'a, T, C> {
impl<'a, C: Mix + Clone> Slice<'a, C> {
///Get a color from the gradient slice. The color of the closest domain
///limit will be returned if `i` is outside the domain.
pub fn get(&self, i: T) -> C {
pub fn get(&self, i: C::Scalar) -> C {
self.gradient.get(self.range.clamp(i))
}

///Take `n` evenly spaced colors from the gradient slice, as an iterator.
pub fn take(&self, n: usize) -> Take<T, C> {
pub fn take(&self, n: usize) -> Take<C> {
let (min, max) = self.domain();

Take {
Expand All @@ -169,15 +169,15 @@ impl<'a, T: Float, C: Mix<T> + Clone> Slice<'a, T, C> {

///Slice this gradient slice to further limit its domain. Ranges outside
///the domain will be clamped to the nearest domain limit.
pub fn slice<R: Into<Range<T>>>(&self, range: R) -> Slice<T, C> {
pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
Slice {
gradient: self.gradient,
range: self.range.constrain(&range.into())
}
}

///Get the limits of this gradient slice's domain.
pub fn domain(&self) -> (T, T) {
pub fn domain(&self) -> (C::Scalar, C::Scalar) {
if let Range { from: Some(from), to: Some(to) } = self.range {
(from, to)
} else {
Expand Down Expand Up @@ -273,13 +273,13 @@ impl<T: Float> From<::std::ops::RangeFull> for Range<T> {
}
}

enum MaybeSlice<'a, T: Float + 'a, C: Mix<T> + Clone + 'a> {
NotSlice(&'a Gradient<T, C>),
Slice(Slice<'a, T, C>),
enum MaybeSlice<'a, C: Mix + Clone + 'a> {
NotSlice(&'a Gradient<C>),
Slice(Slice<'a, C>),
}

impl<'a, T: Float, C: Mix<T> + Clone> MaybeSlice<'a, T, C> {
fn get(&self, i: T) -> C {
impl<'a, C: Mix + Clone> MaybeSlice<'a, C> {
fn get(&self, i: C::Scalar) -> C {
match *self {
MaybeSlice::NotSlice(g) => g.get(i),
MaybeSlice::Slice(ref s) => s.get(i),
Expand Down
12 changes: 9 additions & 3 deletions src/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ impl<T: Float> ColorSpace for Hsl<T> {
}
}

impl<T: Float> Mix<T> for Hsl<T> {
impl<T: Float> Mix for Hsl<T> {
type Scalar = T;

fn mix(&self, other: &Hsl<T>, factor: T) -> Hsl<T> {
let factor = clamp(factor, T::zero(), T::one());
let hue_diff: T = (other.hue - self.hue).to_float();
Expand All @@ -89,7 +91,9 @@ impl<T: Float> Mix<T> for Hsl<T> {
}
}

impl<T: Float> Shade<T> for Hsl<T> {
impl<T: Float> Shade for Hsl<T> {
type Scalar = T;

fn lighten(&self, amount: T) -> Hsl<T> {
Hsl {
hue: self.hue,
Expand Down Expand Up @@ -132,7 +136,9 @@ impl<T: Float> Hue for Hsl<T> {
}
}

impl<T: Float> Saturate<T> for Hsl<T> {
impl<T: Float> Saturate for Hsl<T> {
type Scalar = T;

fn saturate(&self, factor: T) -> Hsl<T> {
Hsl {
hue: self.hue,
Expand Down
12 changes: 9 additions & 3 deletions src/hsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ impl<T: Float> ColorSpace for Hsv<T> {
}
}

impl<T: Float> Mix<T> for Hsv<T> {
impl<T: Float> Mix for Hsv<T> {
type Scalar = T;

fn mix(&self, other: &Hsv<T>, factor: T) -> Hsv<T> {
let factor = clamp(factor, T::zero(), T::one());
let hue_diff: T = (other.hue - self.hue).to_float();
Expand All @@ -88,7 +90,9 @@ impl<T: Float> Mix<T> for Hsv<T> {
}
}

impl<T: Float> Shade<T> for Hsv<T> {
impl<T: Float> Shade for Hsv<T> {
type Scalar = T;

fn lighten(&self, amount: T) -> Hsv<T> {
Hsv {
hue: self.hue,
Expand Down Expand Up @@ -131,7 +135,9 @@ impl<T: Float> Hue for Hsv<T> {
}
}

impl<T: Float> Saturate<T> for Hsv<T> {
impl<T: Float> Saturate for Hsv<T> {
type Scalar = T;

fn saturate(&self, factor: T) -> Hsv<T> {
Hsv {
hue: self.hue,
Expand Down
8 changes: 6 additions & 2 deletions src/lab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ impl<T: Float> ColorSpace for Lab<T> {
}
}

impl<T: Float> Mix<T> for Lab<T> {
impl<T: Float> Mix for Lab<T> {
type Scalar = T;

fn mix(&self, other: &Lab<T>, factor: T) -> Lab<T> {
let factor = clamp(factor, T::zero(), T::one());

Expand All @@ -90,7 +92,9 @@ impl<T: Float> Mix<T> for Lab<T> {
}
}

impl<T: Float> Shade<T> for Lab<T> {
impl<T: Float> Shade for Lab<T> {
type Scalar = T;

fn lighten(&self, amount: T) -> Lab<T> {
Lab {
l: self.l + amount,
Expand Down
12 changes: 9 additions & 3 deletions src/lch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ impl<T: Float> ColorSpace for Lch<T> {
}
}

impl<T: Float> Mix<T> for Lch<T> {
impl<T: Float> Mix for Lch<T> {
type Scalar = T;

fn mix(&self, other: &Lch<T>, factor: T) -> Lch<T> {
let factor = clamp(factor, T::zero(), T::one());
let hue_diff: T = (other.hue - self.hue).to_float();
Expand All @@ -87,7 +89,9 @@ impl<T: Float> Mix<T> for Lch<T> {
}
}

impl<T: Float> Shade<T> for Lch<T> {
impl<T: Float> Shade for Lch<T> {
type Scalar = T;

fn lighten(&self, amount: T) -> Lch<T> {
Lch {
l: self.l + amount,
Expand Down Expand Up @@ -130,7 +134,9 @@ impl<T: Float> Hue for Lch<T> {
}
}

impl<T: Float> Saturate<T> for Lch<T> {
impl<T: Float> Saturate for Lch<T> {
type Scalar = T;

fn saturate(&self, factor: T) -> Lch<T> {
Lch {
l: self.l,
Expand Down
37 changes: 26 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,17 @@ macro_rules! make_color {
}
)+

impl<T:Float> Mix<T> for Color<T> {
impl<T:Float> Mix for Color<T> {
type Scalar = T;

fn mix(&self, other: &Color<T>, factor: T) -> Color<T> {
Rgb::from(*self).mix(&Rgb::from(*other), factor).into()
}
}

impl<T:Float> Shade<T> for Color<T> {
impl<T:Float> Shade for Color<T> {
type Scalar = T;

fn lighten(&self, amount: T) -> Color<T> {
Lab::from(*self).lighten(amount).into()
}
Expand All @@ -149,7 +153,9 @@ macro_rules! make_color {
}
}

impl<T:Float> Saturate<T> for Color<T> {
impl<T:Float> Saturate for Color<T> {
type Scalar = T;

fn saturate(&self, factor: T) -> Color<T> {
Lch::from(*self).saturate(factor).into()
}
Expand Down Expand Up @@ -310,13 +316,16 @@ pub trait ColorSpace {
///assert_eq!(a.mix(&b, 0.5), Rgb::linear_rgb(0.5, 0.5, 0.5));
///assert_eq!(a.mix(&b, 1.0), b);
///```
pub trait Mix<T:Float> {
pub trait Mix {
///The type of the mixing factor.
type Scalar: Float;

///Mix the color with an other color, by `factor`.
///
///`factor` sould be between `0.0` and `1.0`, where `0.0` will result in
///the same color as `self` and `1.0` will result in the same color as
///`other`.
fn mix(&self, other: &Self, factor: T) -> Self;
fn mix(&self, other: &Self, factor: Self::Scalar) -> Self;
}

///The `Shade` trait allows a color to be lightened or darkened.
Expand All @@ -329,12 +338,15 @@ pub trait Mix<T:Float> {
///
///assert_eq!(a.lighten(0.1), b.darken(0.1));
///```
pub trait Shade<T:Float>: Sized {
pub trait Shade: Sized {
///The type of the lighten/darken amount.
type Scalar: Float;

///Lighten the color by `amount`.
fn lighten(&self, amount: T) -> Self;
fn lighten(&self, amount: Self::Scalar) -> Self;

///Darken the color by `amount`.
fn darken(&self, amount: T) -> Self {
fn darken(&self, amount: Self::Scalar) -> Self {
self.lighten(-amount)
}
}
Expand Down Expand Up @@ -390,12 +402,15 @@ pub trait Hue: GetHue {
///
///assert_eq!(a.saturate(1.0), b.desaturate(0.5));
///```
pub trait Saturate<T: Float>: Sized {
pub trait Saturate: Sized {
///The type of the (de)saturation factor.
type Scalar: Float;

///Increase the saturation by `factor`.
fn saturate(&self, factor: T) -> Self;
fn saturate(&self, factor: Self::Scalar) -> Self;

///Decrease the saturation by `factor`.
fn desaturate(&self, factor: T) -> Self {
fn desaturate(&self, factor: Self::Scalar) -> Self {
self.saturate(-factor)
}
}
Loading