colourado_iter/
lib.rs

1//! A small and minimalistic library to generate a random color palette.
2//! The user-facing `Color` struct contains RGB colors ranging from 0 to 1.
3//! All colors are of type f32 (no exceptions)
4//! 
5//! # Usage
6//! 
7//! ```rust
8//! use colourado_iter::{Color, ColorPalette, PaletteType};
9//! 
10//! let mut palette = ColorPalette::new(PaletteType::Random, false, &mut rand::thread_rng());
11//! let random_color = palette.next();
12//! let color_array: [f32; 3] = palette.next().unwrap().to_array();
13//! let many_colors = palette.take(20);
14//! let hue = 315.0;
15//! let saturation = 0.5;
16//! let value = 0.3;
17//! let rgb_color: Color = Color::hsv_to_rgb(hue, saturation, value);
18//! ```
19//! 
20//! The second param to ColorPalette::new() determines the color scheme.  
21//! Currently 3 different schemes are supported:  
22//! `PaletteType::Random` generates random colors 
23//! `PaletteType::Pastel` generates pastel colors 
24//! `PaletteType::Dark` generates dark colors  
25//! 
26//! The third param determines whether colors are generated close to each other
27//! or are spread apart. `true` generates adjacent colors while `false` will generate
28//! a very spread color palette.
29//!
30//! Optionally, you can use the `HsvPalette` struct to get a generator which spits out the immediate HSV values as opposed to a `Color` struct.
31//! 
32//! **WARNING** The `ColorPalette` iterator is infinite! It will never exhaust! As such, you should never
33//! use `collect` or `for x in` patterns with it. Instead, always use `take` if you want a certain number of colors. 
34
35use rand::Rng;
36
37mod color;
38pub use color::Color;
39
40/// Container for a vector of colors.
41/// You can also use it to store your own custom palette of you so desire. 
42pub struct HsvPalette {
43    iteration: usize,
44    base_divergence: f32,
45    palette_type: PaletteType,
46    hue: Hue,
47}
48
49pub struct ColorPalette(HsvPalette);
50
51pub enum PaletteType {
52    Random,
53    Pastel,
54    Dark,
55}
56
57pub(crate) type Hue = f32;
58pub(crate) type Saturation = f32;
59pub(crate) type Value = f32;
60pub type Hsv = (Hue, Saturation, Value);
61
62impl ColorPalette {
63    pub fn new<T: Rng>(palette_type: PaletteType, adjacent_colors: bool, rng: &mut T) -> Self {
64        ColorPalette(HsvPalette::new(
65            palette_type,
66            adjacent_colors,
67            rng
68        ))
69    }
70
71    pub fn get_inner(&self) -> &HsvPalette {
72        &self.0
73    }
74
75    pub fn get_inner_mut(&mut self) -> &mut HsvPalette {
76        &mut self.0
77    }
78
79    pub fn into_inner(self) -> HsvPalette {
80        self.0
81    }
82}
83
84impl HsvPalette {
85    pub fn new<T: Rng>(palette_type: PaletteType, adjacent_colors: bool, rng: &mut T) -> Self {
86
87        let hue = rng.gen_range(0.0..360.0);
88
89        let mut base_divergence = 80.0;
90
91        if adjacent_colors {
92            base_divergence = 25.0;
93        }
94
95        Self {
96            base_divergence,
97            palette_type,
98            hue,
99            iteration: 0
100        }
101    }
102
103
104    fn palette_dark(&self) -> Hsv {
105        let iteration = self.iteration as f32;
106        let f = (iteration * 43.0).cos().abs();
107        let mut div = self.base_divergence;
108
109        if div < 15.0 {
110            div = 15.0;
111        }
112
113        let hue = (self.hue + div + f).abs() % 360.0;
114        let saturation = 0.32 + ((iteration * 0.75).sin() / 2.0).abs();
115        let value = 0.1 + (iteration.cos() / 6.0).abs();
116        (hue, saturation, value)
117    }
118
119    fn palette_pastel(&self) -> Hsv  {
120        let iteration = self.iteration as f32;
121        let f = (iteration * 25.0).cos().abs();
122        let mut div = self.base_divergence;
123
124        if div < 15.0 {
125            div = 15.0;
126        }
127
128        let hue = (self.hue + div + f).abs() % 360.0;
129        let saturation = ((iteration * 0.35).cos() / 5.0).abs();
130        let value = 0.5 + (iteration.cos() / 2.0).abs();
131        (hue, saturation, value)
132    }
133
134    fn palette_random(&self) -> Hsv  {
135        let iteration = self.iteration as f32;
136        let f = (iteration * 55.0).tan().abs();
137        let mut div = self.base_divergence;
138
139        if div < 15.0 {
140            div = 15.0;
141        }
142
143        let hue = (self.hue + div + f).abs() % 360.0;
144        let mut saturation = (iteration * 0.35).sin().abs();
145        let mut value = ((6.33 * iteration) * 0.5).cos().abs();
146
147        if saturation < 0.4 {
148            saturation = 0.4;
149        }
150
151        if value < 0.2 {
152            value = 0.2;
153        } else if value > 0.85 {
154            value = 0.85;
155        }
156        (hue, saturation, value)    
157    }
158
159    pub fn get(&self) -> Hsv {
160        match self.palette_type {
161            PaletteType::Random => self.palette_random(),
162            PaletteType::Pastel => self.palette_pastel(),
163            PaletteType::Dark => self.palette_dark(),
164        }
165    }
166}
167
168impl Iterator for HsvPalette {
169    type Item = Hsv;
170
171    fn next(&mut self) -> Option<Self::Item> {
172        let (hue, saturation, value) = self.get();
173        self.hue = hue;
174        self.iteration += 1;
175        Some((hue, saturation, value))
176    }
177}
178
179impl Iterator for ColorPalette {
180    type Item = Color;
181
182    fn next(&mut self) -> Option<Self::Item> {
183        if let Some((hue, saturation, value)) = self.0.next() {
184            Some(Color::hsv_to_rgb(hue, saturation, value))
185        } else {
186            None
187        }
188    }
189}
190
191
192
193#[cfg(test)]
194mod tests {
195    use super::ColorPalette;
196    use super::PaletteType;
197
198    #[test]
199    fn generates_palette() {
200        let palette = ColorPalette::new(PaletteType::Random, false, &mut rand::thread_rng());
201
202        let colors = palette.take(7);
203
204        for color in colors {
205            let (red, green, blue) = color.to_tuple();
206            assert!(red >= 0.0);
207            assert!(red <= 1.0);
208
209            assert!(green >= 0.0);
210            assert!(green <= 1.0);
211
212            assert!(blue >= 0.0);
213            assert!(blue <= 1.0);
214        }        
215    }
216}