use crate::utils;
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct ColorRGBA {
r: f32,
g: f32,
b: f32,
a: f32,
}
impl ColorRGBA {
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
return Self {
r: r.clamp(0.0, 1.0),
g: g.clamp(0.0, 1.0),
b: b.clamp(0.0, 1.0),
a: a.clamp(0.0, 1.0),
};
}
pub fn new_rgb(r: f32, g: f32, b: f32) -> Self {
return Self::new(r, g, b, 1.0);
}
pub unsafe fn new_unsafe(r: f32, g: f32, b: f32, a: f32) -> Self {
return Self {
r: r,
g: g,
b: b,
a: a,
};
}
pub fn get_red(&self) -> f32 {
return self.r;
}
pub fn get_green(&self) -> f32 {
return self.g;
}
pub fn get_blue(&self) -> f32 {
return self.b;
}
pub fn get_alpha(&self) -> f32 {
return self.a;
}
pub fn get(&self) -> [f32; 4] {
return [self.r, self.g, self.b, self.a];
}
}
impl Color for ColorRGBA {
const TYPE: ColorType = ColorType::RGB;
fn get_rgba(&self) -> ColorRGBA {
return *self;
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct ColorHSLA {
h: f32,
s: f32,
l: f32,
a: f32,
}
impl ColorHSLA {
pub fn new(h: f32, s: f32, l: f32, a: f32) -> Self {
return Self {
h: h.rem_euclid(1.0),
s: s.clamp(0.0, 1.0),
l: l.clamp(0.0, 1.0),
a: a.clamp(0.0, 1.0),
};
}
pub fn new_hsl(h: f32, s: f32, l: f32) -> Self {
return Self::new(h, s, l, 1.0);
}
pub unsafe fn new_unsafe(h: f32, s: f32, l: f32, a: f32) -> Self {
return Self {
h: h,
s: s,
l: l,
a: a,
};
}
pub fn get_hue(&self) -> f32 {
return self.h;
}
pub fn get_saturation(&self) -> f32 {
return self.s;
}
pub fn get_lightness(&self) -> f32 {
return self.l;
}
pub fn get_alpha(&self) -> f32 {
return self.a;
}
pub fn get(&self) -> [f32; 4] {
return [self.h, self.s, self.l, self.a];
}
}
impl Color for ColorHSLA {
const TYPE: ColorType = ColorType::HSL;
fn get_hsla(&self) -> ColorHSLA {
return *self;
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct ColorHSVA {
h: f32,
s: f32,
v: f32,
a: f32,
}
impl ColorHSVA {
pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self {
return Self {
h: h.rem_euclid(1.0),
s: s.clamp(0.0, 1.0),
v: v.clamp(0.0, 1.0),
a: a.clamp(0.0, 1.0),
};
}
pub fn new_hsv(h: f32, s: f32, v: f32) -> Self {
return Self::new(h, s, v, 1.0);
}
pub unsafe fn new_unsafe(h: f32, s: f32, v: f32, a: f32) -> Self {
return Self {
h: h,
s: s,
v: v,
a: a,
};
}
pub fn get_hue(&self) -> f32 {
return self.h;
}
pub fn get_saturation(&self) -> f32 {
return self.s;
}
pub fn get_value(&self) -> f32 {
return self.v;
}
pub fn get_alpha(&self) -> f32 {
return self.a;
}
pub fn get(&self) -> [f32; 4] {
return [self.h, self.s, self.v, self.a];
}
}
impl Color for ColorHSVA {
const TYPE: ColorType = ColorType::HSV;
fn get_hsva(&self) -> ColorHSVA {
return *self;
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct ColorHSIA {
h: f32,
s: f32,
i: f32,
a: f32,
}
impl ColorHSIA {
pub fn new(h: f32, s: f32, i: f32, a: f32) -> Self {
return Self {
h: h.rem_euclid(1.0),
s: s.clamp(0.0, 1.0),
i: i.clamp(0.0, 1.0),
a: a.clamp(0.0, 1.0),
};
}
pub fn new_hsi(h: f32, s: f32, i: f32) -> Self {
return Self::new(h, s, i, 1.0);
}
pub unsafe fn new_unsafe(h: f32, s: f32, i: f32, a: f32) -> Self {
return Self {
h: h,
s: s,
i: i,
a: a,
};
}
pub fn get_hue(&self) -> f32 {
return self.h;
}
pub fn get_saturation(&self) -> f32 {
return self.s;
}
pub fn get_intensity(&self) -> f32 {
return self.i;
}
pub fn get_alpha(&self) -> f32 {
return self.a;
}
pub fn get(&self) -> [f32; 4] {
return [self.h, self.s, self.i, self.a];
}
}
impl Color for ColorHSIA {
const TYPE: ColorType = ColorType::HSI;
fn get_hsia(&self) -> ColorHSIA {
return *self;
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct ColorND<const N: usize> {
values: [f32; N],
}
impl<const N: usize> ColorND<N> {
pub fn new(values: &[f32; N]) -> Self {
let values: [f32; N] = values
.iter()
.map(|value| {
return value.clamp(0.0, 1.0);
})
.collect::<Vec<f32>>()
.try_into()
.expect("Will never fail");
return Self { values };
}
pub fn get(&self) -> &[f32; N] {
return &self.values;
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub enum ColorType {
RGB,
HSV,
HSL,
HSI,
}
pub trait Color {
const TYPE: ColorType;
fn get_rgba(&self) -> ColorRGBA {
return match Self::TYPE {
ColorType::RGB => {
panic!("The get_rgba() method must be implemented for a color of TYPE RGB")
}
ColorType::HSV => utils::hsv_to_rgb(&self.get_hsva()),
ColorType::HSL => utils::hsl_to_rgb(&self.get_hsla()),
ColorType::HSI => utils::hsi_to_rgb(&self.get_hsia()),
};
}
fn get_hsva(&self) -> ColorHSVA {
return match Self::TYPE {
ColorType::RGB => utils::rgb_to_hsv(&self.get_rgba()),
ColorType::HSV => {
panic!("The get_hsva() method must be implemented for a color of TYPE HSV")
}
ColorType::HSL => utils::hsl_to_hsv(&self.get_hsla()),
ColorType::HSI => utils::hsi_to_hsv(&self.get_hsia()),
};
}
fn get_hsla(&self) -> ColorHSLA {
return match Self::TYPE {
ColorType::RGB => utils::rgb_to_hsl(&self.get_rgba()),
ColorType::HSV => utils::hsv_to_hsl(&self.get_hsva()),
ColorType::HSL => {
panic!("The get_hsla() method must be implemented for a color of TYPE HSL")
}
ColorType::HSI => utils::hsi_to_hsl(&self.get_hsia()),
};
}
fn get_hsia(&self) -> ColorHSIA {
return match Self::TYPE {
ColorType::RGB => utils::rgb_to_hsi(&self.get_rgba()),
ColorType::HSV => utils::hsv_to_hsi(&self.get_hsva()),
ColorType::HSL => utils::hsl_to_hsi(&self.get_hsla()),
ColorType::HSI => {
panic!("The get_hsia() method must be implemented for a color of TYPE HSI")
}
};
}
}
pub trait ColorMap<const N: usize> {
fn get_color(&self, color: ColorND<N>) -> impl Color;
}
#[cfg(test)]
mod tests {
use super::*;
mod color_rgba {
use super::*;
#[test]
fn new() {
let result_valid = ColorRGBA::new(0.1, 0.2, 0.3, 0.4);
let result_low_r = ColorRGBA::new(-0.1, 0.2, 0.3, 0.4);
let result_low_g = ColorRGBA::new(0.1, -0.2, 0.3, 0.4);
let result_low_b = ColorRGBA::new(0.1, 0.2, -0.3, 0.4);
let result_low_a = ColorRGBA::new(0.1, 0.2, 0.3, -0.4);
let result_hig_r = ColorRGBA::new(1.1, 0.2, 0.3, 0.4);
let result_hig_g = ColorRGBA::new(0.1, 1.2, 0.3, 0.4);
let result_hig_b = ColorRGBA::new(0.1, 0.2, 1.3, 0.4);
let result_hig_a = ColorRGBA::new(0.1, 0.2, 0.3, 1.4);
assert_eq!(
result_valid,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
}
);
assert_eq!(
result_low_r,
ColorRGBA {
r: 0.0,
g: 0.2,
b: 0.3,
a: 0.4,
}
);
assert_eq!(
result_low_g,
ColorRGBA {
r: 0.1,
g: 0.0,
b: 0.3,
a: 0.4,
}
);
assert_eq!(
result_low_b,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.0,
a: 0.4,
}
);
assert_eq!(
result_low_a,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.0,
}
);
assert_eq!(
result_hig_r,
ColorRGBA {
r: 1.0,
g: 0.2,
b: 0.3,
a: 0.4,
}
);
assert_eq!(
result_hig_g,
ColorRGBA {
r: 0.1,
g: 1.0,
b: 0.3,
a: 0.4,
}
);
assert_eq!(
result_hig_b,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 1.0,
a: 0.4,
}
);
assert_eq!(
result_hig_a,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}
);
}
#[test]
fn new_rgb() {
let result_valid = ColorRGBA::new_rgb(0.1, 0.2, 0.3);
let result_low_r = ColorRGBA::new_rgb(-0.1, 0.2, 0.3);
let result_low_g = ColorRGBA::new_rgb(0.1, -0.2, 0.3);
let result_low_b = ColorRGBA::new_rgb(0.1, 0.2, -0.3);
let result_hig_r = ColorRGBA::new_rgb(1.1, 0.2, 0.3);
let result_hig_g = ColorRGBA::new_rgb(0.1, 1.2, 0.3);
let result_hig_b = ColorRGBA::new_rgb(0.1, 0.2, 1.3);
assert_eq!(
result_valid,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}
);
assert_eq!(
result_low_r,
ColorRGBA {
r: 0.0,
g: 0.2,
b: 0.3,
a: 1.0,
}
);
assert_eq!(
result_low_g,
ColorRGBA {
r: 0.1,
g: 0.0,
b: 0.3,
a: 1.0,
}
);
assert_eq!(
result_low_b,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.0,
a: 1.0,
}
);
assert_eq!(
result_hig_r,
ColorRGBA {
r: 1.0,
g: 0.2,
b: 0.3,
a: 1.0,
}
);
assert_eq!(
result_hig_g,
ColorRGBA {
r: 0.1,
g: 1.0,
b: 0.3,
a: 1.0,
}
);
assert_eq!(
result_hig_b,
ColorRGBA {
r: 0.1,
g: 0.2,
b: 1.0,
a: 1.0,
}
);
}
#[test]
fn get_red() {
let value = ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
};
assert_eq!(value.get_red(), 0.1);
}
#[test]
fn get_green() {
let value = ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
};
assert_eq!(value.get_green(), 0.2);
}
#[test]
fn get_blue() {
let value = ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
};
assert_eq!(value.get_blue(), 0.3);
}
#[test]
fn get_alpha() {
let value = ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
};
assert_eq!(value.get_alpha(), 0.4);
}
#[test]
fn get() {
let value = ColorRGBA {
r: 0.1,
g: 0.2,
b: 0.3,
a: 0.4,
};
assert_eq!(value.get(), [0.1, 0.2, 0.3, 0.4]);
}
}
mod color_nd {
use super::*;
#[test]
fn new() {
let result_1_low = ColorND::new(&[-0.2]);
let result_1_mid = ColorND::new(&[0.2]);
let result_1_hig = ColorND::new(&[1.2]);
let result_3_low = ColorND::new(&[-0.2, 0.3, 0.4]);
let result_3_mid = ColorND::new(&[0.2, 0.3, 0.4]);
let result_3_hig = ColorND::new(&[0.2, 0.3, 1.4]);
assert_eq!(result_1_low, ColorND { values: [0.0] });
assert_eq!(result_1_mid, ColorND { values: [0.2] });
assert_eq!(result_1_hig, ColorND { values: [1.0] });
assert_eq!(
result_3_low,
ColorND {
values: [0.0, 0.3, 0.4]
}
);
assert_eq!(
result_3_mid,
ColorND {
values: [0.2, 0.3, 0.4]
}
);
assert_eq!(
result_3_hig,
ColorND {
values: [0.2, 0.3, 1.0]
}
);
}
}
}