gemini_engine/core/colchar/
colour.rsuse std::{
ops::{Add, AddAssign, Mul, MulAssign},
str::FromStr,
};
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn mul_by_f64_to_u8<T: Into<f64>>(value: T, rhs: f64) -> u8 {
(value.into() * rhs).round() as u8
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Colour {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Colour {
pub const BLACK: Self = Self::greyscale(0);
pub const WHITE: Self = Self::greyscale(255);
#[must_use]
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
#[must_use]
pub fn hsv(hue: u8, sat: u8, val: u8) -> Self {
let hue = f32::from(hue) / 255.0;
let sat = f32::from(sat) / 255.0;
let val = f32::from(val) / 255.0;
let index = (hue * 6.0).floor();
let f = hue.mul_add(6.0, -index);
let p = val * f.mul_add(-sat, 1.0);
let q = val * f.mul_add(-sat, 1.0);
let t = val * (1.0 - f).mul_add(-sat, 1.0);
let (red, green, blue) = [
(val, t, p),
(q, val, p),
(p, val, t),
(p, q, val),
(t, p, val),
(val, p, q),
][index as usize];
Self::rgb(
mul_by_f64_to_u8(red, 255.0),
mul_by_f64_to_u8(green, 255.0),
mul_by_f64_to_u8(blue, 255.0),
)
}
#[must_use]
pub const fn greyscale(v: u8) -> Self {
Self::rgb(v, v, v)
}
}
impl Add for Colour {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::rgb(self.r + rhs.r, self.g + rhs.g, self.b + rhs.b)
}
}
impl AddAssign for Colour {
fn add_assign(&mut self, rhs: Self) {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
}
}
impl Mul<f64> for Colour {
type Output = Self;
fn mul(self, rhs: f64) -> Self::Output {
Self::rgb(
mul_by_f64_to_u8(self.r, rhs),
mul_by_f64_to_u8(self.g, rhs),
mul_by_f64_to_u8(self.b, rhs),
)
}
}
impl MulAssign<f64> for Colour {
fn mul_assign(&mut self, rhs: f64) {
self.r = mul_by_f64_to_u8(self.r, rhs);
self.r = mul_by_f64_to_u8(self.g, rhs);
self.r = mul_by_f64_to_u8(self.b, rhs);
}
}
impl FromStr for Colour {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.replace(' ', "");
let parts: Vec<&str> = s.split(',').collect();
if parts.len() != 3 {
return Err(String::from("Incorrect number of arguments, string must be in format r,g,b to be parsed correctly"));
}
println!("{parts:?}");
let mut nums = [0u8; 3];
for i in 0..3 {
nums[i] = parts[i].parse::<u8>().map_err(|_| {
String::from("Could not parse part of argument, make sure it's a valid number")
})?;
}
Ok(Self::rgb(nums[0], nums[1], nums[2]))
}
}