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 pub fn to_pdf_array(&self) -> crate::objects::Object {
74 use crate::objects::Object;
75 match self {
76 Color::Gray(g) => Object::Array(vec![Object::Real(*g)]),
77 Color::Rgb(r, g, b) => {
78 Object::Array(vec![Object::Real(*r), Object::Real(*g), Object::Real(*b)])
79 }
80 Color::Cmyk(c, m, y, k) => Object::Array(vec![
81 Object::Real(*c),
82 Object::Real(*m),
83 Object::Real(*y),
84 Object::Real(*k),
85 ]),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_rgb_color_creation() {
96 let color = Color::rgb(0.5, 0.7, 0.3);
97 assert_eq!(color, Color::Rgb(0.5, 0.7, 0.3));
98 }
99
100 #[test]
101 fn test_rgb_color_clamping() {
102 let color = Color::rgb(1.5, -0.3, 0.5);
103 assert_eq!(color, Color::Rgb(1.0, 0.0, 0.5));
104 }
105
106 #[test]
107 fn test_gray_color_creation() {
108 let color = Color::gray(0.5);
109 assert_eq!(color, Color::Gray(0.5));
110 }
111
112 #[test]
113 fn test_gray_color_clamping() {
114 let color1 = Color::gray(1.5);
115 assert_eq!(color1, Color::Gray(1.0));
116
117 let color2 = Color::gray(-0.5);
118 assert_eq!(color2, Color::Gray(0.0));
119 }
120
121 #[test]
122 fn test_cmyk_color_creation() {
123 let color = Color::cmyk(0.1, 0.2, 0.3, 0.4);
124 assert_eq!(color, Color::Cmyk(0.1, 0.2, 0.3, 0.4));
125 }
126
127 #[test]
128 fn test_cmyk_color_clamping() {
129 let color = Color::cmyk(1.5, -0.2, 0.5, 2.0);
130 assert_eq!(color, Color::Cmyk(1.0, 0.0, 0.5, 1.0));
131 }
132
133 #[test]
134 fn test_predefined_colors() {
135 assert_eq!(Color::black(), Color::Gray(0.0));
136 assert_eq!(Color::white(), Color::Gray(1.0));
137 assert_eq!(Color::red(), Color::Rgb(1.0, 0.0, 0.0));
138 assert_eq!(Color::green(), Color::Rgb(0.0, 1.0, 0.0));
139 assert_eq!(Color::blue(), Color::Rgb(0.0, 0.0, 1.0));
140 assert_eq!(Color::yellow(), Color::Rgb(1.0, 1.0, 0.0));
141 assert_eq!(Color::cyan(), Color::Rgb(0.0, 1.0, 1.0));
142 assert_eq!(Color::magenta(), Color::Rgb(1.0, 0.0, 1.0));
143 }
144
145 #[test]
146 fn test_color_equality() {
147 let color1 = Color::rgb(0.5, 0.5, 0.5);
148 let color2 = Color::rgb(0.5, 0.5, 0.5);
149 let color3 = Color::rgb(0.5, 0.5, 0.6);
150
151 assert_eq!(color1, color2);
152 assert_ne!(color1, color3);
153
154 let gray1 = Color::gray(0.5);
155 let gray2 = Color::gray(0.5);
156 assert_eq!(gray1, gray2);
157
158 let cmyk1 = Color::cmyk(0.1, 0.2, 0.3, 0.4);
159 let cmyk2 = Color::cmyk(0.1, 0.2, 0.3, 0.4);
160 assert_eq!(cmyk1, cmyk2);
161 }
162
163 #[test]
164 fn test_color_different_types_inequality() {
165 let rgb = Color::rgb(0.5, 0.5, 0.5);
166 let gray = Color::gray(0.5);
167 let cmyk = Color::cmyk(0.5, 0.5, 0.5, 0.5);
168
169 assert_ne!(rgb, gray);
170 assert_ne!(rgb, cmyk);
171 assert_ne!(gray, cmyk);
172 }
173
174 #[test]
175 fn test_color_debug() {
176 let rgb = Color::rgb(0.1, 0.2, 0.3);
177 let debug_str = format!("{:?}", rgb);
178 assert!(debug_str.contains("Rgb"));
179 assert!(debug_str.contains("0.1"));
180 assert!(debug_str.contains("0.2"));
181 assert!(debug_str.contains("0.3"));
182
183 let gray = Color::gray(0.5);
184 let gray_debug = format!("{:?}", gray);
185 assert!(gray_debug.contains("Gray"));
186 assert!(gray_debug.contains("0.5"));
187
188 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
189 let cmyk_debug = format!("{:?}", cmyk);
190 assert!(cmyk_debug.contains("Cmyk"));
191 assert!(cmyk_debug.contains("0.1"));
192 assert!(cmyk_debug.contains("0.2"));
193 assert!(cmyk_debug.contains("0.3"));
194 assert!(cmyk_debug.contains("0.4"));
195 }
196
197 #[test]
198 fn test_color_clone() {
199 let rgb = Color::rgb(0.5, 0.6, 0.7);
200 let rgb_clone = rgb;
201 assert_eq!(rgb, rgb_clone);
202
203 let gray = Color::gray(0.5);
204 let gray_clone = gray;
205 assert_eq!(gray, gray_clone);
206
207 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
208 let cmyk_clone = cmyk;
209 assert_eq!(cmyk, cmyk_clone);
210 }
211
212 #[test]
213 fn test_color_copy() {
214 let rgb = Color::rgb(0.5, 0.6, 0.7);
215 let rgb_copy = rgb; assert_eq!(rgb, rgb_copy);
217
218 assert_eq!(rgb, Color::Rgb(0.5, 0.6, 0.7));
220 assert_eq!(rgb_copy, Color::Rgb(0.5, 0.6, 0.7));
221 }
222
223 #[test]
224 fn test_edge_case_values() {
225 let color = Color::rgb(0.0, 0.5, 1.0);
227 assert_eq!(color, Color::Rgb(0.0, 0.5, 1.0));
228
229 let gray = Color::gray(0.0);
230 assert_eq!(gray, Color::Gray(0.0));
231
232 let gray_max = Color::gray(1.0);
233 assert_eq!(gray_max, Color::Gray(1.0));
234
235 let cmyk = Color::cmyk(0.0, 0.0, 0.0, 0.0);
236 assert_eq!(cmyk, Color::Cmyk(0.0, 0.0, 0.0, 0.0));
237
238 let cmyk_max = Color::cmyk(1.0, 1.0, 1.0, 1.0);
239 assert_eq!(cmyk_max, Color::Cmyk(1.0, 1.0, 1.0, 1.0));
240 }
241
242 #[test]
243 fn test_floating_point_precision() {
244 let color = Color::rgb(0.333333333, 0.666666666, 0.999999999);
245 match color {
246 Color::Rgb(r, g, b) => {
247 assert!((r - 0.333333333).abs() < 1e-9);
248 assert!((g - 0.666666666).abs() < 1e-9);
249 assert!((b - 0.999999999).abs() < 1e-9);
250 }
251 _ => panic!("Expected RGB color"),
252 }
253 }
254
255 #[test]
256 fn test_rgb_clamping_infinity() {
257 let inf_color = Color::rgb(f64::INFINITY, f64::NEG_INFINITY, 0.5);
259 assert_eq!(inf_color, Color::Rgb(1.0, 0.0, 0.5));
260
261 let large_color = Color::rgb(1000.0, -1000.0, 0.5);
263 assert_eq!(large_color, Color::Rgb(1.0, 0.0, 0.5));
264 }
265
266 #[test]
267 fn test_cmyk_all_components() {
268 let cmyk = Color::cmyk(0.1, 0.2, 0.3, 0.4);
270 match cmyk {
271 Color::Cmyk(c, m, y, k) => {
272 assert_eq!(c, 0.1);
273 assert_eq!(m, 0.2);
274 assert_eq!(y, 0.3);
275 assert_eq!(k, 0.4);
276 }
277 _ => panic!("Expected CMYK color"),
278 }
279 }
280
281 #[test]
282 fn test_pattern_matching() {
283 let colors = vec![
284 Color::rgb(0.5, 0.5, 0.5),
285 Color::gray(0.5),
286 Color::cmyk(0.1, 0.2, 0.3, 0.4),
287 ];
288
289 let mut rgb_count = 0;
290 let mut gray_count = 0;
291 let mut cmyk_count = 0;
292
293 for color in colors {
294 match color {
295 Color::Rgb(_, _, _) => rgb_count += 1,
296 Color::Gray(_) => gray_count += 1,
297 Color::Cmyk(_, _, _, _) => cmyk_count += 1,
298 }
299 }
300
301 assert_eq!(rgb_count, 1);
302 assert_eq!(gray_count, 1);
303 assert_eq!(cmyk_count, 1);
304 }
305}