feat: hsv color format implemented with rgb parsing

This commit is contained in:
2025-08-15 00:06:20 +02:00
parent 6c6ff8e420
commit 5ffeee7851
5 changed files with 199 additions and 27 deletions

View File

@@ -0,0 +1,69 @@
use std::fmt::Display;
use crate::color::{ColorHue, HSV, Percentage, RGB};
impl HSV {
pub fn new(h: u16, s: u8, v: u8) -> Self {
Self(
ColorHue::new(h as i16),
Percentage::new(s as i16),
Percentage::new(v as i16),
)
}
}
impl Display for HSV {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "hsv({}, {}%, {}%)", self.0, self.1, self.2)
}
}
impl PartialEq for HSV {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0 && self.1 == other.1 && self.2 == other.2
}
fn ne(&self, other: &Self) -> bool {
self.0 != other.0 || self.1 != other.1 || self.2 != other.2
}
}
impl From<RGB> for HSV {
fn from(color: RGB) -> Self {
let r = color.0.to_f32() / 255.0;
let g = color.1.to_f32() / 255.0;
let b = color.2.to_f32() / 255.0;
let min: f32 = r.min(g.min(b));
let max: f32 = r.max(g.max(b));
let v = max;
if min == max {
return Self::new(0, 0, v as u8);
}
let s = (max - min) / max;
let dif = max - min;
let rc = (max - r) / dif;
let gc = (max - g) / dif;
let bc = (max - b) / dif;
let mut h: f32;
if r == max {
h = bc - gc;
} else if g == max {
h = 2.0 + rc - bc;
} else {
h = 4.0 + gc - rc;
}
h = (h / 6.0).rem_euclid(1.0);
Self::new(
(h * 360.0).round() as u16,
(s * 100.0).round() as u8,
(v * 100.0).round() as u8,
)
}
}

View File

@@ -3,9 +3,10 @@
mod test; mod test;
pub mod hsl; pub mod hsl;
pub mod hsv;
pub mod rgb; pub mod rgb;
use std::fmt::{UpperHex, write}; use std::fmt::UpperHex;
use crate::core::ranged::RangedInt; use crate::core::ranged::RangedInt;
@@ -16,15 +17,12 @@ pub type Percentage = RangedInt<0, 100>;
pub struct RGB(ColorIntensity, ColorIntensity, ColorIntensity); pub struct RGB(ColorIntensity, ColorIntensity, ColorIntensity);
#[derive(Debug)] #[derive(Debug)]
pub struct HSL(ColorHue, Percentage, Percentage); pub struct HSL(ColorHue, Percentage, Percentage);
// pub struct HSV(ColorHue, Percentage, Percentage); #[derive(Debug)]
pub struct HSV(ColorHue, Percentage, Percentage);
#[derive(Debug)] #[derive(Debug)]
pub struct Color(RGB); pub struct Color(RGB);
impl Color { impl Color {}
pub fn format(&self) -> String {
format!("{:?}", self.0)
}
}
impl UpperHex for Color { impl UpperHex for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -53,3 +51,9 @@ impl From<HSL> for Color {
Color(RGB::from(color)) Color(RGB::from(color))
} }
} }
impl From<HSV> for Color {
fn from(color: HSV) -> Self {
Color(RGB::from(color))
}
}

View File

@@ -1,7 +1,7 @@
use std::fmt::UpperHex; use std::fmt::{Display, UpperHex};
use crate::{ use crate::{
color::{ColorIntensity, HSL, RGB}, color::{ColorIntensity, HSL, HSV, RGB},
core::ranged::BaseNumber, core::ranged::BaseNumber,
}; };
@@ -25,6 +25,12 @@ impl PartialEq for RGB {
} }
} }
impl Display for RGB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "rgb({}, {}, {})", self.0, self.1, self.2)
}
}
impl UpperHex for RGB { impl UpperHex for RGB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:0>2X}{:0>2X}{:0>2X}", self.0, self.1, self.2) write!(f, "{:0>2X}{:0>2X}{:0>2X}", self.0, self.1, self.2)
@@ -86,3 +92,66 @@ impl From<HSL> for RGB {
) )
} }
} }
impl From<HSV> for RGB {
fn from(color: HSV) -> Self {
let h = color.0.to_f32() / 360.0;
let s = color.1.to_f32() / 100.0;
let v = color.2.to_f32() / 100.0;
if s == 0.0 {
let grey = (v * 255.0) as u8;
return Self::new(grey, grey, grey);
}
let i = (h * 6.0) as u8;
let f = (h * 6.0) - i as f32;
let p = v * (1.0 - s);
let q = v * (1.0 - s * f);
let t = v * (1.0 - s * (1.0 - f));
let i = i % 6;
if i == 0 {
return Self::new(
(v * 255.0).round() as u8,
(t * 255.0).round() as u8,
(p * 255.0).round() as u8,
);
}
if i == 1 {
return Self::new(
(q * 255.0).round() as u8,
(v * 255.0).round() as u8,
(p * 255.0).round() as u8,
);
}
if i == 2 {
return Self::new(
(p * 255.0).round() as u8,
(v * 255.0).round() as u8,
(t * 255.0).round() as u8,
);
}
if i == 3 {
return Self::new(
(p * 255.0).round() as u8,
(q * 255.0).round() as u8,
(v * 255.0).round() as u8,
);
}
if i == 4 {
return Self::new(
(t * 255.0).round() as u8,
(p * 255.0).round() as u8,
(v * 255.0).round() as u8,
);
}
return Self::new(
(v * 255.0).round() as u8,
(p * 255.0).round() as u8,
(q * 255.0).round() as u8,
);
}
}

View File

@@ -1,8 +1,21 @@
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use std::fmt::format; use crate::color::{Color, HSL, HSV, RGB};
use crate::color::{Color, HSL, RGB}; #[test]
fn test_color_initialization() {
let red_hsl = Color::from(HSL::new(0, 100, 50));
let red_rgb = Color::from(RGB::new(255, 0, 0));
assert_eq!(red_hsl, red_rgb);
let green_hsl = Color::from(HSL::new(120, 100, 50));
let green_rgb = Color::from(RGB::new(0, 255, 0));
assert_eq!(green_hsl, green_rgb);
let blue_hsl = Color::from(HSL::new(240, 100, 50));
let blue_rgb = Color::from(RGB::new(0, 0, 255));
assert_eq!(blue_hsl, blue_rgb);
}
#[test] #[test]
fn test_conversion() { fn test_conversion() {
@@ -117,17 +130,36 @@ pub mod tests {
} }
#[test] #[test]
fn test_color_initialization() { fn test_hsv_from_rgb() {
let red_hsl = Color::from(HSL::new(0, 100, 50)); // Base colors
let red_rgb = Color::from(RGB::new(255, 0, 0)); let color = RGB::new(255, 0, 0);
assert_eq!(red_hsl, red_rgb); assert_eq!(HSV::from(color), HSV::new(0, 100, 100));
let color = RGB::new(0, 255, 0);
assert_eq!(HSV::from(color), HSV::new(120, 100, 100));
let color = RGB::new(0, 0, 255);
assert_eq!(HSV::from(color), HSV::new(240, 100, 100));
let green_hsl = Color::from(HSL::new(120, 100, 50)); // Complex colors
let green_rgb = Color::from(RGB::new(0, 255, 0)); let color = RGB::new(20, 240, 100);
assert_eq!(green_hsl, green_rgb); assert_eq!(HSV::from(color), HSV::new(142, 92, 94));
let color = RGB::new(220, 10, 50);
assert_eq!(HSV::from(color), HSV::new(349, 95, 86));
}
let blue_hsl = Color::from(HSL::new(240, 100, 50)); #[test]
let blue_rgb = Color::from(RGB::new(0, 0, 255)); fn test_rgb_from_hsv() {
assert_eq!(blue_hsl, blue_rgb); // Base colors
let color = HSV::new(0, 100, 100);
assert_eq!(RGB::from(color), RGB::new(255, 0, 0));
let color = HSV::new(120, 100, 100);
assert_eq!(RGB::from(color), RGB::new(0, 255, 0));
let color = HSV::new(240, 100, 100);
assert_eq!(RGB::from(color), RGB::new(0, 0, 255));
// Complex colors
let color = HSV::new(349, 95, 86);
assert_eq!(RGB::from(color), RGB::new(219, 11, 49));
let color = HSV::new(142, 92, 94);
assert_eq!(RGB::from(color), RGB::new(19, 240, 100));
} }
} }

View File

@@ -1,8 +1,7 @@
use clipboard::ClipboardContext; use clipboard::ClipboardContext;
use clipboard::ClipboardProvider; use clipboard::ClipboardProvider;
use crate::color::Color; use crate::color::HSV;
use crate::color::HSL;
use crate::color::RGB; use crate::color::RGB;
mod color; mod color;
@@ -18,8 +17,7 @@ fn main() {
println!("Hello, world!"); println!("Hello, world!");
example(); example();
let hsl_color = Color::from(HSL::new(0, 100, 50)); let color = RGB::new(220, 10, 50);
// let rgb_color = Color::from(HSL::new(193, 67, 28)); let hsv_color = HSV::from(RGB::new(220, 10, 50));
println!("RGB Color: {:X}", hsl_color); println!("RGB color: {}, HSV Color: {}", color, hsv_color);
// println!("RGB Color: {}", rgb_color.format());
} }