1#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum Color {
6 Rgb(f64, f64, f64),
8 Gray(f64),
10 Cmyk(f64, f64, f64, f64),
12}
13
14impl Color {
15 pub fn rgb(r: f64, g: f64, b: f64) -> Self {
17 Color::Rgb(r.clamp(0.0, 1.0), g.clamp(0.0, 1.0), b.clamp(0.0, 1.0))
18 }
19
20 pub fn gray(value: f64) -> Self {
22 Color::Gray(value.clamp(0.0, 1.0))
23 }
24
25 pub fn cmyk(c: f64, m: f64, y: f64, k: f64) -> Self {
27 Color::Cmyk(
28 c.clamp(0.0, 1.0),
29 m.clamp(0.0, 1.0),
30 y.clamp(0.0, 1.0),
31 k.clamp(0.0, 1.0),
32 )
33 }
34
35 pub fn black() -> Self {
37 Color::Gray(0.0)
38 }
39
40 pub fn white() -> Self {
42 Color::Gray(1.0)
43 }
44
45 pub fn red() -> Self {
47 Color::Rgb(1.0, 0.0, 0.0)
48 }
49
50 pub fn green() -> Self {
52 Color::Rgb(0.0, 1.0, 0.0)
53 }
54
55 pub fn blue() -> Self {
57 Color::Rgb(0.0, 0.0, 1.0)
58 }
59
60 pub fn yellow() -> Self {
61 Color::Rgb(1.0, 1.0, 0.0)
62 }
63
64 pub fn cyan() -> Self {
65 Color::Rgb(0.0, 1.0, 1.0)
66 }
67
68 pub fn magenta() -> Self {
69 Color::Rgb(1.0, 0.0, 1.0)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_rgb_color_creation() {
79 let color = Color::rgb(0.5, 0.7, 0.3);
80 assert_eq!(color, Color::Rgb(0.5, 0.7, 0.3));
81 }
82
83 #[test]
84 fn test_rgb_color_clamping() {
85 let color = Color::rgb(1.5, -0.3, 0.5);
86 assert_eq!(color, Color::Rgb(1.0, 0.0, 0.5));
87 }
88
89 #[test]
90 fn test_gray_color_creation() {
91 let color = Color::gray(0.5);
92 assert_eq!(color, Color::Gray(0.5));
93 }
94
95 #[test]
96 fn test_gray_color_clamping() {
97 let color1 = Color::gray(1.5);
98 assert_eq!(color1, Color::Gray(1.0));
99
100 let color2 = Color::gray(-0.5);
101 assert_eq!(color2, Color::Gray(0.0));
102 }
103
104 #[test]
105 fn test_cmyk_color_creation() {
106 let color = Color::cmyk(0.1, 0.2, 0.3, 0.4);
107 assert_eq!(color, Color::Cmyk(0.1, 0.2, 0.3, 0.4));
108 }
109
110 #[test]
111 fn test_cmyk_color_clamping() {
112 let color = Color::cmyk(1.5, -0.2, 0.5, 2.0);
113 assert_eq!(color, Color::Cmyk(1.0, 0.0, 0.5, 1.0));
114 }
115
116 #[test]
117 fn test_predefined_colors() {
118 assert_eq!(Color::black(), Color::Gray(0.0));
119 assert_eq!(Color::white(), Color::Gray(1.0));
120 assert_eq!(Color::red(), Color::Rgb(1.0, 0.0, 0.0));
121 assert_eq!(Color::green(), Color::Rgb(0.0, 1.0, 0.0));
122 assert_eq!(Color::blue(), Color::Rgb(0.0, 0.0, 1.0));
123 assert_eq!(Color::yellow(), Color::Rgb(1.0, 1.0, 0.0));
124 assert_eq!(Color::cyan(), Color::Rgb(0.0, 1.0, 1.0));
125 assert_eq!(Color::magenta(), Color::Rgb(1.0, 0.0, 1.0));
126 }
127
128 #[test]
129 fn test_color_equality() {
130 let color1 = Color::rgb(0.5, 0.5, 0.5);
131 let color2 = Color::rgb(0.5, 0.5, 0.5);
132 let color3 = Color::rgb(0.5, 0.5, 0.6);
133
134 assert_eq!(color1, color2);
135 assert_ne!(color1, color3);
136
137 let gray1 = Color::gray(0.5);
138 let gray2 = Color::gray(0.5);
139 assert_eq!(gray1, gray2);
140
141 let cmyk1 = Color::cmyk(0.1, 0.2, 0.3, 0.4);
142 let cmyk2 = Color::cmyk(0.1, 0.2, 0.3, 0.4);
143 assert_eq!(cmyk1, cmyk2);
144 }
145
146 #[test]
147 fn test_color_different_types_inequality() {
148 let rgb = Color::rgb(0.5, 0.5, 0.5);
149 let gray = Color::gray(0.5);
150 let cmyk = Color::cmyk(0.5, 0.5, 0.5, 0.5);
151
152 assert_ne!(rgb, gray);
153 assert_ne!(rgb, cmyk);
154 assert_ne!(gray, cmyk);
155 }
156
157 #[test]
158 fn test_color_debug() {
159 let rgb = Color::rgb(0.1, 0.2, 0.3);
160 let debug_str = format!("{:?}", rgb);
161 assert!(debug_str.contains("Rgb"));
162 assert!(debug_str.contains("0.1"));
163 assert!(debug_str.contains("0.2"));
164 assert!(debug_str.contains("0.3"));
165
166 let gray = Color::gray(0.5);
167 let gray_debug = format!("{:?}", gray);
168 assert!(gray_debug.contains("Gray"));
169 assert!(gray_debug.contains("0.5"));
170
171 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
172 let cmyk_debug = format!("{:?}", cmyk);
173 assert!(cmyk_debug.contains("Cmyk"));
174 assert!(cmyk_debug.contains("0.1"));
175 assert!(cmyk_debug.contains("0.2"));
176 assert!(cmyk_debug.contains("0.3"));
177 assert!(cmyk_debug.contains("0.4"));
178 }
179
180 #[test]
181 fn test_color_clone() {
182 let rgb = Color::rgb(0.5, 0.6, 0.7);
183 let rgb_clone = rgb;
184 assert_eq!(rgb, rgb_clone);
185
186 let gray = Color::gray(0.5);
187 let gray_clone = gray;
188 assert_eq!(gray, gray_clone);
189
190 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
191 let cmyk_clone = cmyk;
192 assert_eq!(cmyk, cmyk_clone);
193 }
194
195 #[test]
196 fn test_color_copy() {
197 let rgb = Color::rgb(0.5, 0.6, 0.7);
198 let rgb_copy = rgb; assert_eq!(rgb, rgb_copy);
200
201 assert_eq!(rgb, Color::Rgb(0.5, 0.6, 0.7));
203 assert_eq!(rgb_copy, Color::Rgb(0.5, 0.6, 0.7));
204 }
205
206 #[test]
207 fn test_edge_case_values() {
208 let color = Color::rgb(0.0, 0.5, 1.0);
210 assert_eq!(color, Color::Rgb(0.0, 0.5, 1.0));
211
212 let gray = Color::gray(0.0);
213 assert_eq!(gray, Color::Gray(0.0));
214
215 let gray_max = Color::gray(1.0);
216 assert_eq!(gray_max, Color::Gray(1.0));
217
218 let cmyk = Color::cmyk(0.0, 0.0, 0.0, 0.0);
219 assert_eq!(cmyk, Color::Cmyk(0.0, 0.0, 0.0, 0.0));
220
221 let cmyk_max = Color::cmyk(1.0, 1.0, 1.0, 1.0);
222 assert_eq!(cmyk_max, Color::Cmyk(1.0, 1.0, 1.0, 1.0));
223 }
224
225 #[test]
226 fn test_floating_point_precision() {
227 let color = Color::rgb(0.333333333, 0.666666666, 0.999999999);
228 match color {
229 Color::Rgb(r, g, b) => {
230 assert!((r - 0.333333333).abs() < 1e-9);
231 assert!((g - 0.666666666).abs() < 1e-9);
232 assert!((b - 0.999999999).abs() < 1e-9);
233 }
234 _ => panic!("Expected RGB color"),
235 }
236 }
237
238 #[test]
239 fn test_rgb_clamping_infinity() {
240 let inf_color = Color::rgb(f64::INFINITY, f64::NEG_INFINITY, 0.5);
242 assert_eq!(inf_color, Color::Rgb(1.0, 0.0, 0.5));
243
244 let large_color = Color::rgb(1000.0, -1000.0, 0.5);
246 assert_eq!(large_color, Color::Rgb(1.0, 0.0, 0.5));
247 }
248
249 #[test]
250 fn test_cmyk_all_components() {
251 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
253 match cmyk {
254 Color::Cmyk(c, m, y, k) => {
255 assert_eq!(c, 0.1);
256 assert_eq!(m, 0.2);
257 assert_eq!(y, 0.3);
258 assert_eq!(k, 0.4);
259 }
260 _ => panic!("Expected CMYK color"),
261 }
262 }
263
264 #[test]
265 fn test_pattern_matching() {
266 let colors = vec![
267 Color::rgb(0.5, 0.5, 0.5),
268 Color::gray(0.5),
269 Color::cmyk(0.1, 0.2, 0.3, 0.4),
270 ];
271
272 let mut rgb_count = 0;
273 let mut gray_count = 0;
274 let mut cmyk_count = 0;
275
276 for color in colors {
277 match color {
278 Color::Rgb(_, _, _) => rgb_count += 1,
279 Color::Gray(_) => gray_count += 1,
280 Color::Cmyk(_, _, _, _) => cmyk_count += 1,
281 }
282 }
283
284 assert_eq!(rgb_count, 1);
285 assert_eq!(gray_count, 1);
286 assert_eq!(cmyk_count, 1);
287 }
288}