Skip to content

Commit e78dcdc

Browse files
committed
Change Mix, Shade and Saturate to use an associated type
1 parent 398bde6 commit e78dcdc

File tree

10 files changed

+110
-61
lines changed

10 files changed

+110
-61
lines changed

examples/readme_examples.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ fn display_colors(filename: &str, colors: &[[u8; 3]]) {
6969
}
7070
}
7171

72-
fn display_gradients<T: Float, A: Mix<T> + Clone, B: Mix<T> + Clone>(filename: &str, grad1: Gradient<T, A>, grad2: Gradient<T, B>)
72+
fn display_gradients<T: Float, A: Mix<Scalar=T> + Clone, B: Mix<Scalar=T> + Clone>(filename: &str, grad1: Gradient<A>, grad2: Gradient<B>)
7373
where Rgb<T>: From<A>,
7474
Rgb<T>: From<B>
7575
{

src/gradient.rs

+32-32
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//!Types for interpolation between multiple colors.
22
3-
use num::traits::Float;
3+
use num::{Float, One, Zero, NumCast};
44
use std::cmp::max;
55

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

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

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

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

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

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

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

8888
Take {
@@ -95,36 +95,36 @@ impl<T: Float, C: Mix<T> + Clone> Gradient<T, C> {
9595
}
9696

9797
///Slice this gradient to limit its domain.
98-
pub fn slice<R: Into<Range<T>>>(&self, range: R) -> Slice<T, C> {
98+
pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
9999
Slice {
100100
gradient: self,
101101
range: range.into(),
102102
}
103103
}
104104

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

113113
///An iterator over interpolated colors.
114-
pub struct Take<'a, T: Float + 'a, C: Mix<T> + Clone + 'a> {
115-
gradient: MaybeSlice<'a, T, C>,
116-
from: T,
117-
diff: T,
114+
pub struct Take<'a, C: Mix + Clone + 'a> {
115+
gradient: MaybeSlice<'a, C>,
116+
from: C::Scalar,
117+
diff: C::Scalar,
118118
len: usize,
119119
current: usize,
120120
}
121121

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

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

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

142142

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

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

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

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

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

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

276-
enum MaybeSlice<'a, T: Float + 'a, C: Mix<T> + Clone + 'a> {
277-
NotSlice(&'a Gradient<T, C>),
278-
Slice(Slice<'a, T, C>),
276+
enum MaybeSlice<'a, C: Mix + Clone + 'a> {
277+
NotSlice(&'a Gradient<C>),
278+
Slice(Slice<'a, C>),
279279
}
280280

281-
impl<'a, T: Float, C: Mix<T> + Clone> MaybeSlice<'a, T, C> {
282-
fn get(&self, i: T) -> C {
281+
impl<'a, C: Mix + Clone> MaybeSlice<'a, C> {
282+
fn get(&self, i: C::Scalar) -> C {
283283
match *self {
284284
MaybeSlice::NotSlice(g) => g.get(i),
285285
MaybeSlice::Slice(ref s) => s.get(i),

src/hsl.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ impl<T: Float> ColorSpace for Hsl<T> {
7575
}
7676
}
7777

78-
impl<T: Float> Mix<T> for Hsl<T> {
78+
impl<T: Float> Mix for Hsl<T> {
79+
type Scalar = T;
80+
7981
fn mix(&self, other: &Hsl<T>, factor: T) -> Hsl<T> {
8082
let factor = clamp(factor, T::zero(), T::one());
8183
let hue_diff: T = (other.hue - self.hue).to_float();
@@ -89,7 +91,9 @@ impl<T: Float> Mix<T> for Hsl<T> {
8991
}
9092
}
9193

92-
impl<T: Float> Shade<T> for Hsl<T> {
94+
impl<T: Float> Shade for Hsl<T> {
95+
type Scalar = T;
96+
9397
fn lighten(&self, amount: T) -> Hsl<T> {
9498
Hsl {
9599
hue: self.hue,
@@ -132,7 +136,9 @@ impl<T: Float> Hue for Hsl<T> {
132136
}
133137
}
134138

135-
impl<T: Float> Saturate<T> for Hsl<T> {
139+
impl<T: Float> Saturate for Hsl<T> {
140+
type Scalar = T;
141+
136142
fn saturate(&self, factor: T) -> Hsl<T> {
137143
Hsl {
138144
hue: self.hue,

src/hsv.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ impl<T: Float> ColorSpace for Hsv<T> {
7474
}
7575
}
7676

77-
impl<T: Float> Mix<T> for Hsv<T> {
77+
impl<T: Float> Mix for Hsv<T> {
78+
type Scalar = T;
79+
7880
fn mix(&self, other: &Hsv<T>, factor: T) -> Hsv<T> {
7981
let factor = clamp(factor, T::zero(), T::one());
8082
let hue_diff: T = (other.hue - self.hue).to_float();
@@ -88,7 +90,9 @@ impl<T: Float> Mix<T> for Hsv<T> {
8890
}
8991
}
9092

91-
impl<T: Float> Shade<T> for Hsv<T> {
93+
impl<T: Float> Shade for Hsv<T> {
94+
type Scalar = T;
95+
9296
fn lighten(&self, amount: T) -> Hsv<T> {
9397
Hsv {
9498
hue: self.hue,
@@ -131,7 +135,9 @@ impl<T: Float> Hue for Hsv<T> {
131135
}
132136
}
133137

134-
impl<T: Float> Saturate<T> for Hsv<T> {
138+
impl<T: Float> Saturate for Hsv<T> {
139+
type Scalar = T;
140+
135141
fn saturate(&self, factor: T) -> Hsv<T> {
136142
Hsv {
137143
hue: self.hue,

src/lab.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ impl<T: Float> ColorSpace for Lab<T> {
7777
}
7878
}
7979

80-
impl<T: Float> Mix<T> for Lab<T> {
80+
impl<T: Float> Mix for Lab<T> {
81+
type Scalar = T;
82+
8183
fn mix(&self, other: &Lab<T>, factor: T) -> Lab<T> {
8284
let factor = clamp(factor, T::zero(), T::one());
8385

@@ -90,7 +92,9 @@ impl<T: Float> Mix<T> for Lab<T> {
9092
}
9193
}
9294

93-
impl<T: Float> Shade<T> for Lab<T> {
95+
impl<T: Float> Shade for Lab<T> {
96+
type Scalar = T;
97+
9498
fn lighten(&self, amount: T) -> Lab<T> {
9599
Lab {
96100
l: self.l + amount,

src/lch.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ impl<T: Float> ColorSpace for Lch<T> {
7474
}
7575
}
7676

77-
impl<T: Float> Mix<T> for Lch<T> {
77+
impl<T: Float> Mix for Lch<T> {
78+
type Scalar = T;
79+
7880
fn mix(&self, other: &Lch<T>, factor: T) -> Lch<T> {
7981
let factor = clamp(factor, T::zero(), T::one());
8082
let hue_diff: T = (other.hue - self.hue).to_float();
@@ -87,7 +89,9 @@ impl<T: Float> Mix<T> for Lch<T> {
8789
}
8890
}
8991

90-
impl<T: Float> Shade<T> for Lch<T> {
92+
impl<T: Float> Shade for Lch<T> {
93+
type Scalar = T;
94+
9195
fn lighten(&self, amount: T) -> Lch<T> {
9296
Lch {
9397
l: self.l + amount,
@@ -130,7 +134,9 @@ impl<T: Float> Hue for Lch<T> {
130134
}
131135
}
132136

133-
impl<T: Float> Saturate<T> for Lch<T> {
137+
impl<T: Float> Saturate for Lch<T> {
138+
type Scalar = T;
139+
134140
fn saturate(&self, factor: T) -> Lch<T> {
135141
Lch {
136142
l: self.l,

src/lib.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,17 @@ macro_rules! make_color {
119119
}
120120
)+
121121

122-
impl<T:Float> Mix<T> for Color<T> {
122+
impl<T:Float> Mix for Color<T> {
123+
type Scalar = T;
124+
123125
fn mix(&self, other: &Color<T>, factor: T) -> Color<T> {
124126
Rgb::from(*self).mix(&Rgb::from(*other), factor).into()
125127
}
126128
}
127129

128-
impl<T:Float> Shade<T> for Color<T> {
130+
impl<T:Float> Shade for Color<T> {
131+
type Scalar = T;
132+
129133
fn lighten(&self, amount: T) -> Color<T> {
130134
Lab::from(*self).lighten(amount).into()
131135
}
@@ -149,7 +153,9 @@ macro_rules! make_color {
149153
}
150154
}
151155

152-
impl<T:Float> Saturate<T> for Color<T> {
156+
impl<T:Float> Saturate for Color<T> {
157+
type Scalar = T;
158+
153159
fn saturate(&self, factor: T) -> Color<T> {
154160
Lch::from(*self).saturate(factor).into()
155161
}
@@ -310,13 +316,16 @@ pub trait ColorSpace {
310316
///assert_eq!(a.mix(&b, 0.5), Rgb::linear_rgb(0.5, 0.5, 0.5));
311317
///assert_eq!(a.mix(&b, 1.0), b);
312318
///```
313-
pub trait Mix<T:Float> {
319+
pub trait Mix {
320+
///The type of the mixing factor.
321+
type Scalar: Float;
322+
314323
///Mix the color with an other color, by `factor`.
315324
///
316325
///`factor` sould be between `0.0` and `1.0`, where `0.0` will result in
317326
///the same color as `self` and `1.0` will result in the same color as
318327
///`other`.
319-
fn mix(&self, other: &Self, factor: T) -> Self;
328+
fn mix(&self, other: &Self, factor: Self::Scalar) -> Self;
320329
}
321330

322331
///The `Shade` trait allows a color to be lightened or darkened.
@@ -329,12 +338,15 @@ pub trait Mix<T:Float> {
329338
///
330339
///assert_eq!(a.lighten(0.1), b.darken(0.1));
331340
///```
332-
pub trait Shade<T:Float>: Sized {
341+
pub trait Shade: Sized {
342+
///The type of the lighten/darken amount.
343+
type Scalar: Float;
344+
333345
///Lighten the color by `amount`.
334-
fn lighten(&self, amount: T) -> Self;
346+
fn lighten(&self, amount: Self::Scalar) -> Self;
335347

336348
///Darken the color by `amount`.
337-
fn darken(&self, amount: T) -> Self {
349+
fn darken(&self, amount: Self::Scalar) -> Self {
338350
self.lighten(-amount)
339351
}
340352
}
@@ -390,12 +402,15 @@ pub trait Hue: GetHue {
390402
///
391403
///assert_eq!(a.saturate(1.0), b.desaturate(0.5));
392404
///```
393-
pub trait Saturate<T: Float>: Sized {
405+
pub trait Saturate: Sized {
406+
///The type of the (de)saturation factor.
407+
type Scalar: Float;
408+
394409
///Increase the saturation by `factor`.
395-
fn saturate(&self, factor: T) -> Self;
410+
fn saturate(&self, factor: Self::Scalar) -> Self;
396411

397412
///Decrease the saturation by `factor`.
398-
fn desaturate(&self, factor: T) -> Self {
413+
fn desaturate(&self, factor: Self::Scalar) -> Self {
399414
self.saturate(-factor)
400415
}
401416
}

0 commit comments

Comments
 (0)