use rand::Rng;
mod color;
pub use color::Color;
pub struct ColorPalette {
iteration: usize,
base_divergence: f32,
palette_type: PaletteType,
hue: Hue,
}
pub enum PaletteType {
Random,
Pastel,
Dark,
}
pub(crate) type Hue = f32;
pub(crate) type Saturation = f32;
pub(crate) type Value = f32;
pub type Hsv = (Hue, Saturation, Value);
impl ColorPalette {
pub fn new<T: Rng>(palette_type: PaletteType, adjacent_colors: bool, mut rng: T) -> Self {
let hue = rng.gen_range(0.0..360.0);
let mut base_divergence = 80.0;
if adjacent_colors {
base_divergence = 25.0;
}
ColorPalette {
base_divergence,
palette_type,
hue,
iteration: 0
}
}
fn palette_dark(&self) -> Hsv {
let iteration = self.iteration as f32;
let f = (iteration * 43.0).cos().abs();
let mut div = self.base_divergence;
if div < 15.0 {
div = 15.0;
}
let hue = (self.hue + div + f).abs() % 360.0;
let saturation = 0.32 + ((iteration * 0.75).sin() / 2.0).abs();
let value = 0.1 + (iteration.cos() / 6.0).abs();
(hue, saturation, value)
}
fn palette_pastel(&self) -> Hsv {
let iteration = self.iteration as f32;
let f = (iteration * 25.0).cos().abs();
let mut div = self.base_divergence;
if div < 15.0 {
div = 15.0;
}
let hue = (self.hue + div + f).abs() % 360.0;
let saturation = ((iteration * 0.35).cos() / 5.0).abs();
let value = 0.5 + (iteration.cos() / 2.0).abs();
(hue, saturation, value)
}
fn palette_random(&self) -> Hsv {
let iteration = self.iteration as f32;
let f = (iteration * 55.0).tan().abs();
let mut div = self.base_divergence;
if div < 15.0 {
div = 15.0;
}
let hue = (self.hue + div + f).abs() % 360.0;
let mut saturation = (iteration * 0.35).sin().abs();
let mut value = ((6.33 * iteration) * 0.5).cos().abs();
if saturation < 0.4 {
saturation = 0.4;
}
if value < 0.2 {
value = 0.2;
} else if value > 0.85 {
value = 0.85;
}
(hue, saturation, value)
}
}
impl Iterator for ColorPalette {
type Item = Color;
fn next(&mut self) -> Option<Self::Item> {
let (hue, saturation, value) = match self.palette_type {
PaletteType::Random => self.palette_random(),
PaletteType::Pastel => self.palette_pastel(),
PaletteType::Dark => self.palette_dark(),
};
let color = Color::hsv_to_rgb(hue, saturation, value);
self.hue = hue;
self.iteration += 1;
Some(color)
}
}
#[cfg(test)]
mod tests {
use super::ColorPalette;
use super::PaletteType;
#[test]
fn generates_palette() {
let palette = ColorPalette::new(PaletteType::Random, false, rand::thread_rng());
let colors = palette.take(7);
for color in colors {
let (red, green, blue) = color.to_tuple();
assert!(red >= 0.0);
assert!(red <= 1.0);
assert!(green >= 0.0);
assert!(green <= 1.0);
assert!(blue >= 0.0);
assert!(blue <= 1.0);
}
}
}