extern crate rand;
mod color_dictionary;
use rand::Rng;
use color_dictionary::{ColorDictionary, ColorInformation};
pub enum Color {
Monochrome,
Red,
Orange,
Yellow,
Green,
Blue,
Purple,
Pink
}
#[derive(Debug, PartialEq)]
pub enum Luminosity {
Random,
Bright,
Light,
Dark
}
#[derive(Debug, PartialEq)]
pub struct RandomColor {
pub hue: Option<ColorInformation>,
pub luminosity: Option<Luminosity>,
pub seed: Option<i32>,
pub alpha: Option<f32>,
}
impl RandomColor {
pub fn new() -> RandomColor {
RandomColor {
hue: None,
luminosity: None,
seed: None,
alpha: Some(1.0),
}
}
pub fn hue(&mut self, hue: Color) -> &mut RandomColor {
let cd = ColorDictionary::new();
self.hue = match hue {
Color::Monochrome => Some(cd.monochrome),
Color::Red => Some(cd.red),
Color::Orange => Some(cd.orange),
Color::Yellow => Some(cd.yellow),
Color::Green => Some(cd.green),
Color::Blue => Some(cd.blue),
Color::Purple => Some(cd.purple),
Color::Pink => Some(cd.pink),
};
self
}
pub fn luminosity(&mut self, luminosity: Luminosity) -> &mut RandomColor {
self.luminosity = Some(luminosity);
self
}
pub fn seed(&mut self, seed: i32) -> &mut RandomColor {
self.seed = Some(seed);
self
}
pub fn alpha(&mut self, alpha: f32) -> &mut RandomColor {
if alpha < 1.0 {
self.alpha = Some(alpha);
}
self
}
pub fn to_hsv_array(&self) -> [u32; 3] {
let (h, s, b) = self.generate_color();
[h as u32, s as u32, b as u32]
}
pub fn to_rgb_string(&self) -> String {
let (h, s, b) = self.generate_color();
let rgb = self.hsv_to_rgb(h, s, b);
format!("rgb({}, {}, {})", rgb[0], rgb[1], rgb[2])
}
pub fn to_rgba_string(&self) -> String {
let a: f32;
let (h, s, b) = self.generate_color();
let rgb = self.hsv_to_rgb(h, s, b);
match self.alpha {
Some(alpha) => a = alpha,
None => a = rand::random(),
}
format!("rgba({}, {}, {}, {})", rgb[0], rgb[1], rgb[2], a)
}
pub fn to_rgb_array(&self) -> [u32; 3] {
let (h, s, b) = self.generate_color();
self.hsv_to_rgb(h, s, b)
}
pub fn to_hsl_string(&self) -> String {
let (h, s, b) = self.generate_color();
let hsv = self.hsv_to_hsl(h, s, b);
format!("hsl({}, {}%, {}%)", hsv[0], hsv[1], hsv[2])
}
pub fn to_hsla_string(&self) -> String {
let a: f32;
let (h, s, b) = self.generate_color();
let hsv = self.hsv_to_hsl(h, s, b);
match self.alpha {
Some(alpha) => a = alpha,
None => a = rand::random(),
}
format!("hsl({}, {}%, {}%, {})", hsv[0], hsv[1], hsv[2], a)
}
pub fn to_hsl_array(&self) -> [u32; 3] {
let (h, s, b) = self.generate_color();
self.hsv_to_hsl(h, s, b)
}
pub fn to_hex(&self) -> String {
let (h, s, b) = self.generate_color();
format!("#{:x}{:x}{:x}", h, s, b)
}
fn generate_color(&self) -> (i32, i32, i32) {
let h = self.pick_hue();
let s = self.pick_saturation(&h);
let b = self.pick_brightness(&h, &s);
(h, s, b)
}
fn pick_hue(&self) -> i32 {
match self.hue {
None => self.random_within(0, 361),
Some(ref color) => self.random_within(color.range[0], color.range[1]),
}
}
fn pick_saturation(&self, hue: &i32) -> i32 {
let cd = ColorDictionary::new();
let s_range = cd.get_saturation_range(hue);
let s_min = s_range.0;
let s_max = s_range.1;
match self.luminosity {
Some(Luminosity::Random) => self.random_within(0, 100),
Some(Luminosity::Bright) => self.random_within(55, s_max),
Some(Luminosity::Dark) => self.random_within(s_max - 10, s_max),
Some(Luminosity::Light) => self.random_within(s_min, 55),
_ => self.random_within(s_min, s_max),
}
}
fn pick_brightness(&self, hue: &i32, saturation: &i32) -> i32 {
let cd = ColorDictionary::new();
let b_min = cd.get_minimum_value(hue, saturation);
let b_max = 100;
match self.luminosity {
Some(Luminosity::Random) => self.random_within(0, 100),
Some(Luminosity::Light) => self.random_within((b_max + b_min) / 2, b_max),
Some(Luminosity::Dark) => self.random_within(b_min, b_min + 20),
_ => self.random_within(b_min, b_max),
}
}
fn random_within(&self, min: i32, max: i32) -> i32 {
match self.seed {
None => rand::thread_rng().gen_range(min, max),
Some(seed) => {
let seed = (seed * 9301 + 49297) % 233280;
let mut rnd = seed / 233280;
(min + rnd * (max - min))
}
}
}
fn hsv_to_rgb(&self, mut hue: i32, saturation: i32, brightness: i32) -> [u32; 3] {
if hue == 0 {
hue = 1;
}
if hue == 360 {
hue = 359;
}
let h: f32 = hue as f32 / 360.0;
let s: f32 = saturation as f32 / 100.0;
let v: f32 = brightness as f32 / 100.0;
let h_i = (h * 6.0).floor();
let f = h * 6.0 - h_i;
let p = v * (1.0 - s);
let q = v * (1.0 - f * s);
let t = v * (1.0 - (1.0 - f) * s);
let (r, g, b) = match h_i as i32 {
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
_ => (v, p, q),
};
[
(r * 255.0).floor() as u32,
(g * 255.0).floor() as u32,
(b * 255.0).floor() as u32,
]
}
fn hsv_to_hsl(&self, hue: i32, saturation: i32, brightness: i32) -> [u32; 3] {
let h = hue;
let s = saturation as f32 / 100.0;
let v = brightness as f32 / 100.0;
let mut k = (2.0 - s) * v;
if k > 1.0 {
k = 2.0 - k;
}
[
h as u32,
((s * v / k * 10000.0) / 100.0) as u32,
(k / 2.0 * 100.0) as u32,
]
}
}
#[cfg(test)]
mod tests {
use RandomColor;
use color_dictionary::ColorDictionary;
use Color;
use Luminosity;
#[test]
fn accept_values() {
let cd = ColorDictionary::new();
let test_case = RandomColor {
hue: Some(cd.blue),
luminosity: Some(Luminosity::Light),
seed: Some(42),
alpha: Some(1.0),
}.to_hsl_string();
let rc = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hsl_string();
assert_eq!(test_case, rc);
}
#[test]
fn generates_color_as_hsv_array() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hsv_array();
assert_eq!(test_case, [179, 20, 100]);
}
#[test]
fn generates_color_as_rgb_string() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_rgb_string();
assert_eq!(test_case, "rgb(204, 255, 254)");
}
#[test]
fn generates_color_as_rgba_string() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_rgba_string();
assert_eq!(test_case, "rgba(204, 255, 254, 1)");
}
#[test]
fn generates_color_as_rgb_array() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_rgb_array();
assert_eq!(test_case, [204, 255, 254]);
}
#[test]
fn generates_color_as_hsl_string() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hsl_string();
assert_eq!(test_case, "hsl(179, 99%, 10%)");
}
#[test]
fn generates_color_as_hsla_string() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hsla_string();
assert_eq!(test_case, "hsl(179, 99%, 10%, 1)");
}
#[test]
fn generates_color_as_hsl_array() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hsl_array();
assert_eq!(test_case, [179, 99, 10]);
}
#[test]
fn generates_color_as_hex() {
let test_case = RandomColor::new()
.hue(Color::Blue)
.luminosity(Luminosity::Light)
.seed(42)
.alpha(1.0)
.to_hex();
assert_eq!(test_case, "#b31464");
}
}