1#[derive(Debug, Clone, Copy, PartialEq)]
2#[repr(u8)]
3pub enum LineCap {
4 Butt = 0,
5 Round = 1,
6 Square = 2,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq)]
10#[repr(u8)]
11pub enum LineJoin {
12 Miter = 0,
13 Round = 1,
14 Bevel = 2,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq)]
19pub enum WindingRule {
20 NonZero,
22 EvenOdd,
24}
25
26pub struct PathBuilder {
27 commands: Vec<PathCommand>,
28}
29
30#[derive(Debug, Clone)]
32pub enum PathCommand {
33 MoveTo { x: f64, y: f64 },
35 LineTo { x: f64, y: f64 },
37 CurveTo {
39 x1: f64,
40 y1: f64,
41 x2: f64,
42 y2: f64,
43 x3: f64,
44 y3: f64,
45 },
46 Rectangle {
48 x: f64,
49 y: f64,
50 width: f64,
51 height: f64,
52 },
53 ClosePath,
55}
56
57impl Default for PathBuilder {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63#[allow(dead_code)]
64impl PathBuilder {
65 pub fn new() -> Self {
66 Self {
67 commands: Vec::new(),
68 }
69 }
70
71 pub fn move_to(mut self, x: f64, y: f64) -> Self {
72 self.commands.push(PathCommand::MoveTo { x, y });
73 self
74 }
75
76 pub fn line_to(mut self, x: f64, y: f64) -> Self {
77 self.commands.push(PathCommand::LineTo { x, y });
78 self
79 }
80
81 pub fn curve_to(mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> Self {
82 self.commands.push(PathCommand::CurveTo {
83 x1,
84 y1,
85 x2,
86 y2,
87 x3,
88 y3,
89 });
90 self
91 }
92
93 pub fn rectangle(mut self, x: f64, y: f64, width: f64, height: f64) -> Self {
94 self.commands.push(PathCommand::Rectangle {
95 x,
96 y,
97 width,
98 height,
99 });
100 self
101 }
102
103 pub fn close(mut self) -> Self {
104 self.commands.push(PathCommand::ClosePath);
105 self
106 }
107
108 pub(crate) fn build(self) -> Vec<PathCommand> {
109 self.commands
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_line_cap_values() {
119 assert_eq!(LineCap::Butt as u8, 0);
120 assert_eq!(LineCap::Round as u8, 1);
121 assert_eq!(LineCap::Square as u8, 2);
122 }
123
124 #[test]
125 fn test_line_cap_equality() {
126 assert_eq!(LineCap::Butt, LineCap::Butt);
127 assert_ne!(LineCap::Butt, LineCap::Round);
128 assert_ne!(LineCap::Round, LineCap::Square);
129 }
130
131 #[test]
132 fn test_line_cap_debug() {
133 let butt = LineCap::Butt;
134 let debug_str = format!("{:?}", butt);
135 assert_eq!(debug_str, "Butt");
136
137 let round = LineCap::Round;
138 assert_eq!(format!("{:?}", round), "Round");
139
140 let square = LineCap::Square;
141 assert_eq!(format!("{:?}", square), "Square");
142 }
143
144 #[test]
145 fn test_line_cap_clone() {
146 let cap = LineCap::Round;
147 let cap_clone = cap;
148 assert_eq!(cap, cap_clone);
149 }
150
151 #[test]
152 fn test_line_cap_copy() {
153 let cap = LineCap::Square;
154 let cap_copy = cap; assert_eq!(cap, cap_copy);
156
157 assert_eq!(cap, LineCap::Square);
159 assert_eq!(cap_copy, LineCap::Square);
160 }
161
162 #[test]
163 fn test_line_join_values() {
164 assert_eq!(LineJoin::Miter as u8, 0);
165 assert_eq!(LineJoin::Round as u8, 1);
166 assert_eq!(LineJoin::Bevel as u8, 2);
167 }
168
169 #[test]
170 fn test_line_join_equality() {
171 assert_eq!(LineJoin::Miter, LineJoin::Miter);
172 assert_ne!(LineJoin::Miter, LineJoin::Round);
173 assert_ne!(LineJoin::Round, LineJoin::Bevel);
174 }
175
176 #[test]
177 fn test_line_join_debug() {
178 let miter = LineJoin::Miter;
179 let debug_str = format!("{:?}", miter);
180 assert_eq!(debug_str, "Miter");
181
182 let round = LineJoin::Round;
183 assert_eq!(format!("{:?}", round), "Round");
184
185 let bevel = LineJoin::Bevel;
186 assert_eq!(format!("{:?}", bevel), "Bevel");
187 }
188
189 #[test]
190 fn test_line_join_clone() {
191 let join = LineJoin::Bevel;
192 let join_clone = join;
193 assert_eq!(join, join_clone);
194 }
195
196 #[test]
197 fn test_line_join_copy() {
198 let join = LineJoin::Miter;
199 let join_copy = join; assert_eq!(join, join_copy);
201
202 assert_eq!(join, LineJoin::Miter);
204 assert_eq!(join_copy, LineJoin::Miter);
205 }
206
207 #[test]
208 fn test_path_builder_new() {
209 let builder = PathBuilder::new();
210 let commands = builder.build();
211 assert!(commands.is_empty());
212 }
213
214 #[test]
215 fn test_path_builder_default() {
216 let builder = PathBuilder::default();
217 let commands = builder.build();
218 assert!(commands.is_empty());
219 }
220
221 #[test]
222 fn test_path_builder_move_to() {
223 let builder = PathBuilder::new().move_to(10.0, 20.0);
224 let commands = builder.build();
225
226 assert_eq!(commands.len(), 1);
227 match &commands[0] {
228 PathCommand::MoveTo { x, y } => {
229 assert_eq!(*x, 10.0);
230 assert_eq!(*y, 20.0);
231 }
232 _ => panic!("Expected MoveTo command"),
233 }
234 }
235
236 #[test]
237 fn test_path_builder_line_to() {
238 let builder = PathBuilder::new().line_to(30.0, 40.0);
239 let commands = builder.build();
240
241 assert_eq!(commands.len(), 1);
242 match &commands[0] {
243 PathCommand::LineTo { x, y } => {
244 assert_eq!(*x, 30.0);
245 assert_eq!(*y, 40.0);
246 }
247 _ => panic!("Expected LineTo command"),
248 }
249 }
250
251 #[test]
252 fn test_path_builder_curve_to() {
253 let builder = PathBuilder::new().curve_to(10.0, 20.0, 30.0, 40.0, 50.0, 60.0);
254 let commands = builder.build();
255
256 assert_eq!(commands.len(), 1);
257 match &commands[0] {
258 PathCommand::CurveTo {
259 x1,
260 y1,
261 x2,
262 y2,
263 x3,
264 y3,
265 } => {
266 assert_eq!(*x1, 10.0);
267 assert_eq!(*y1, 20.0);
268 assert_eq!(*x2, 30.0);
269 assert_eq!(*y2, 40.0);
270 assert_eq!(*x3, 50.0);
271 assert_eq!(*y3, 60.0);
272 }
273 _ => panic!("Expected CurveTo command"),
274 }
275 }
276
277 #[test]
278 fn test_path_builder_close() {
279 let builder = PathBuilder::new().close();
280 let commands = builder.build();
281
282 assert_eq!(commands.len(), 1);
283 match &commands[0] {
284 PathCommand::ClosePath => {}
285 _ => panic!("Expected ClosePath command"),
286 }
287 }
288
289 #[test]
290 fn test_path_builder_complex_path() {
291 let builder = PathBuilder::new()
292 .move_to(0.0, 0.0)
293 .line_to(100.0, 0.0)
294 .line_to(100.0, 100.0)
295 .line_to(0.0, 100.0)
296 .close();
297
298 let commands = builder.build();
299 assert_eq!(commands.len(), 5);
300
301 match &commands[0] {
302 PathCommand::MoveTo { x, y } => {
303 assert_eq!(*x, 0.0);
304 assert_eq!(*y, 0.0);
305 }
306 _ => panic!("Expected MoveTo at index 0"),
307 }
308
309 match &commands[1] {
310 PathCommand::LineTo { x, y } => {
311 assert_eq!(*x, 100.0);
312 assert_eq!(*y, 0.0);
313 }
314 _ => panic!("Expected LineTo at index 1"),
315 }
316
317 match &commands[4] {
318 PathCommand::ClosePath => {}
319 _ => panic!("Expected ClosePath at index 4"),
320 }
321 }
322
323 #[test]
324 fn test_path_builder_bezier_curve() {
325 let builder = PathBuilder::new()
326 .move_to(0.0, 0.0)
327 .curve_to(50.0, 0.0, 100.0, 50.0, 100.0, 100.0)
328 .curve_to(100.0, 150.0, 50.0, 200.0, 0.0, 200.0);
329
330 let commands = builder.build();
331 assert_eq!(commands.len(), 3);
332
333 match &commands[1] {
334 PathCommand::CurveTo {
335 x1,
336 y1,
337 x2,
338 y2,
339 x3,
340 y3,
341 } => {
342 assert_eq!(*x1, 50.0);
343 assert_eq!(*y1, 0.0);
344 assert_eq!(*x2, 100.0);
345 assert_eq!(*y2, 50.0);
346 assert_eq!(*x3, 100.0);
347 assert_eq!(*y3, 100.0);
348 }
349 _ => panic!("Expected CurveTo at index 1"),
350 }
351 }
352
353 #[test]
354 fn test_path_command_debug() {
355 let move_cmd = PathCommand::MoveTo { x: 10.0, y: 20.0 };
356 let debug_str = format!("{:?}", move_cmd);
357 assert!(debug_str.contains("MoveTo"));
358 assert!(debug_str.contains("10.0"));
359 assert!(debug_str.contains("20.0"));
360
361 let line_cmd = PathCommand::LineTo { x: 30.0, y: 40.0 };
362 let line_debug = format!("{:?}", line_cmd);
363 assert!(line_debug.contains("LineTo"));
364
365 let curve_cmd = PathCommand::CurveTo {
366 x1: 1.0,
367 y1: 2.0,
368 x2: 3.0,
369 y2: 4.0,
370 x3: 5.0,
371 y3: 6.0,
372 };
373 let curve_debug = format!("{:?}", curve_cmd);
374 assert!(curve_debug.contains("CurveTo"));
375
376 let close_cmd = PathCommand::ClosePath;
377 let close_debug = format!("{:?}", close_cmd);
378 assert!(close_debug.contains("ClosePath"));
379 }
380
381 #[test]
382 fn test_path_command_clone() {
383 let move_cmd = PathCommand::MoveTo { x: 10.0, y: 20.0 };
384 let move_clone = move_cmd.clone();
385 match (move_cmd, move_clone) {
386 (PathCommand::MoveTo { x: x1, y: y1 }, PathCommand::MoveTo { x: x2, y: y2 }) => {
387 assert_eq!(x1, x2);
388 assert_eq!(y1, y2);
389 }
390 _ => panic!("Clone failed"),
391 }
392
393 let close_cmd = PathCommand::ClosePath;
394 let close_clone = close_cmd.clone();
395 match close_clone {
396 PathCommand::ClosePath => {}
397 _ => panic!("Clone failed for ClosePath"),
398 }
399 }
400
401 #[test]
402 fn test_path_builder_empty_path() {
403 let builder = PathBuilder::new();
404 let commands = builder.build();
405 assert_eq!(commands.len(), 0);
406 }
407
408 #[test]
409 fn test_path_builder_single_command() {
410 let move_builder = PathBuilder::new().move_to(5.0, 10.0);
412 assert_eq!(move_builder.build().len(), 1);
413
414 let line_builder = PathBuilder::new().line_to(15.0, 20.0);
415 assert_eq!(line_builder.build().len(), 1);
416
417 let curve_builder = PathBuilder::new().curve_to(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
418 assert_eq!(curve_builder.build().len(), 1);
419
420 let close_builder = PathBuilder::new().close();
421 assert_eq!(close_builder.build().len(), 1);
422 }
423
424 #[test]
425 fn test_path_builder_negative_coordinates() {
426 let builder = PathBuilder::new()
427 .move_to(-10.0, -20.0)
428 .line_to(-30.0, -40.0)
429 .curve_to(-1.0, -2.0, -3.0, -4.0, -5.0, -6.0);
430
431 let commands = builder.build();
432 assert_eq!(commands.len(), 3);
433
434 match &commands[0] {
435 PathCommand::MoveTo { x, y } => {
436 assert_eq!(*x, -10.0);
437 assert_eq!(*y, -20.0);
438 }
439 _ => panic!("Expected MoveTo"),
440 }
441 }
442
443 #[test]
444 fn test_path_builder_zero_values() {
445 let builder = PathBuilder::new()
446 .move_to(0.0, 0.0)
447 .line_to(0.0, 0.0)
448 .curve_to(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
449
450 let commands = builder.build();
451 assert_eq!(commands.len(), 3);
452
453 match &commands[0] {
455 PathCommand::MoveTo { x, y } => {
456 assert_eq!(*x, 0.0);
457 assert_eq!(*y, 0.0);
458 }
459 _ => panic!("Expected MoveTo"),
460 }
461 }
462
463 #[test]
464 fn test_path_builder_large_values() {
465 let large_val = 1e6;
466 let builder = PathBuilder::new()
467 .move_to(large_val, large_val)
468 .line_to(large_val * 2.0, large_val * 2.0);
469
470 let commands = builder.build();
471 match &commands[0] {
472 PathCommand::MoveTo { x, y } => {
473 assert_eq!(*x, large_val);
474 assert_eq!(*y, large_val);
475 }
476 _ => panic!("Expected MoveTo"),
477 }
478 }
479}