use rand::Rng;
mod color;
pub use color::Color;
pub struct HsvPalette {
iteration: usize,
base_divergence: f32,
palette_type: PaletteType,
hue: Hue,
}
pub struct ColorPalette(HsvPalette);
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, rng: &mut T) -> Self {
ColorPalette(HsvPalette::new(
palette_type,
adjacent_colors,
rng
))
}
pub fn get_inner(&self) -> &HsvPalette {
&self.0
}
pub fn get_inner_mut(&mut self) -> &mut HsvPalette {
&mut self.0
}
pub fn into_inner(self) -> HsvPalette {
self.0
}
}
impl HsvPalette {
pub fn new<T: Rng>(palette_type: PaletteType, adjacent_colors: bool, rng: &mut T) -> Self {
let hue = rng.gen_range(0.0..360.0);
let mut base_divergence = 80.0;
if adjacent_colors {
base_divergence = 25.0;
}
Self {
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)
}
pub fn get(&self) -> Hsv {
match self.palette_type {
PaletteType::Random => self.palette_random(),
PaletteType::Pastel => self.palette_pastel(),
PaletteType::Dark => self.palette_dark(),
}
}
}
impl Iterator for HsvPalette {
type Item = Hsv;
fn next(&mut self) -> Option<Self::Item> {
let (hue, saturation, value) = self.get();
self.hue = hue;
self.iteration += 1;
Some((hue, saturation, value))
}
}
impl Iterator for ColorPalette {
type Item = Color;
fn next(&mut self) -> Option<Self::Item> {
if let Some((hue, saturation, value)) = self.0.next() {
Some(Color::hsv_to_rgb(hue, saturation, value))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::ColorPalette;
use super::PaletteType;
#[test]
fn generates_palette() {
let palette = ColorPalette::new(PaletteType::Random, false, &mut 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);
}
}
}