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