1use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
13pub struct Color {
14 pub r: u8,
15 pub g: u8,
16 pub b: u8,
17}
18
19impl Color {
20 pub fn new(r: u8, g: u8, b: u8) -> Self {
22 Self { r, g, b }
23 }
24
25 pub fn from_hsv(h: f64, s: f64, v: f64) -> Self {
32 let c = v * s;
33 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
34 let m = v - c;
35
36 let (r, g, b) = if h < 60.0 {
37 (c, x, 0.0)
38 } else if h < 120.0 {
39 (x, c, 0.0)
40 } else if h < 180.0 {
41 (0.0, c, x)
42 } else if h < 240.0 {
43 (0.0, x, c)
44 } else if h < 300.0 {
45 (x, 0.0, c)
46 } else {
47 (c, 0.0, x)
48 };
49
50 Self {
51 r: ((r + m) * 255.0) as u8,
52 g: ((g + m) * 255.0) as u8,
53 b: ((b + m) * 255.0) as u8,
54 }
55 }
56
57 pub fn black() -> Self {
59 Self::new(0, 0, 0)
60 }
61
62 pub fn white() -> Self {
64 Self::new(255, 255, 255)
65 }
66
67 pub fn lerp(&self, other: &Color, t: f64) -> Color {
73 let t = t.clamp(0.0, 1.0);
74 Color {
75 r: (self.r as f64 + (other.r as f64 - self.r as f64) * t) as u8,
76 g: (self.g as f64 + (other.g as f64 - self.g as f64) * t) as u8,
77 b: (self.b as f64 + (other.b as f64 - self.b as f64) * t) as u8,
78 }
79 }
80}
81
82impl std::fmt::Display for Color {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "RGB({},{},{})", self.r, self.g, self.b)
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_color_creation() {
94 let color = Color::new(255, 128, 64);
95 assert_eq!(color.r, 255);
96 assert_eq!(color.g, 128);
97 assert_eq!(color.b, 64);
98 }
99
100 #[test]
101 fn test_color_constants() {
102 let black = Color::black();
103 assert_eq!(black.r, 0);
104 assert_eq!(black.g, 0);
105 assert_eq!(black.b, 0);
106
107 let white = Color::white();
108 assert_eq!(white.r, 255);
109 assert_eq!(white.g, 255);
110 assert_eq!(white.b, 255);
111 }
112
113 #[test]
114 fn test_lerp() {
115 let red = Color::new(255, 0, 0);
116 let blue = Color::new(0, 0, 255);
117
118 let mid = red.lerp(&blue, 0.5);
119 assert_eq!(mid.r, 127);
120 assert_eq!(mid.g, 0);
121 assert_eq!(mid.b, 127);
122
123 let at_red = red.lerp(&blue, 0.0);
124 assert_eq!(at_red.r, 255);
125
126 let at_blue = red.lerp(&blue, 1.0);
127 assert_eq!(at_blue.b, 255);
128 }
129
130 #[test]
131 fn test_hsv_conversion() {
132 let red = Color::from_hsv(0.0, 1.0, 1.0);
134 assert_eq!(red.r, 255);
135 assert_eq!(red.g, 0);
136 assert_eq!(red.b, 0);
137
138 let green = Color::from_hsv(120.0, 1.0, 1.0);
140 assert_eq!(green.r, 0);
141 assert_eq!(green.g, 255);
142 assert_eq!(green.b, 0);
143
144 let blue = Color::from_hsv(240.0, 1.0, 1.0);
146 assert_eq!(blue.r, 0);
147 assert_eq!(blue.g, 0);
148 assert_eq!(blue.b, 255);
149 }
150}