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;
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;
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().move_to(10.0, 20.0);
181 let commands = builder.build();
182
183 assert_eq!(commands.len(), 1);
184 match &commands[0] {
185 PathCommand::MoveTo(x, y) => {
186 assert_eq!(*x, 10.0);
187 assert_eq!(*y, 20.0);
188 }
189 _ => panic!("Expected MoveTo command"),
190 }
191 }
192
193 #[test]
194 fn test_path_builder_line_to() {
195 let builder = PathBuilder::new().line_to(30.0, 40.0);
196 let commands = builder.build();
197
198 assert_eq!(commands.len(), 1);
199 match &commands[0] {
200 PathCommand::LineTo(x, y) => {
201 assert_eq!(*x, 30.0);
202 assert_eq!(*y, 40.0);
203 }
204 _ => panic!("Expected LineTo command"),
205 }
206 }
207
208 #[test]
209 fn test_path_builder_curve_to() {
210 let builder = PathBuilder::new().curve_to(10.0, 20.0, 30.0, 40.0, 50.0, 60.0);
211 let commands = builder.build();
212
213 assert_eq!(commands.len(), 1);
214 match &commands[0] {
215 PathCommand::CurveTo(x1, y1, x2, y2, x3, y3) => {
216 assert_eq!(*x1, 10.0);
217 assert_eq!(*y1, 20.0);
218 assert_eq!(*x2, 30.0);
219 assert_eq!(*y2, 40.0);
220 assert_eq!(*x3, 50.0);
221 assert_eq!(*y3, 60.0);
222 }
223 _ => panic!("Expected CurveTo command"),
224 }
225 }
226
227 #[test]
228 fn test_path_builder_close() {
229 let builder = PathBuilder::new().close();
230 let commands = builder.build();
231
232 assert_eq!(commands.len(), 1);
233 match &commands[0] {
234 PathCommand::ClosePath => {}
235 _ => panic!("Expected ClosePath command"),
236 }
237 }
238
239 #[test]
240 fn test_path_builder_complex_path() {
241 let builder = PathBuilder::new()
242 .move_to(0.0, 0.0)
243 .line_to(100.0, 0.0)
244 .line_to(100.0, 100.0)
245 .line_to(0.0, 100.0)
246 .close();
247
248 let commands = builder.build();
249 assert_eq!(commands.len(), 5);
250
251 match &commands[0] {
252 PathCommand::MoveTo(x, y) => {
253 assert_eq!(*x, 0.0);
254 assert_eq!(*y, 0.0);
255 }
256 _ => panic!("Expected MoveTo at index 0"),
257 }
258
259 match &commands[1] {
260 PathCommand::LineTo(x, y) => {
261 assert_eq!(*x, 100.0);
262 assert_eq!(*y, 0.0);
263 }
264 _ => panic!("Expected LineTo at index 1"),
265 }
266
267 match &commands[4] {
268 PathCommand::ClosePath => {}
269 _ => panic!("Expected ClosePath at index 4"),
270 }
271 }
272
273 #[test]
274 fn test_path_builder_bezier_curve() {
275 let builder = PathBuilder::new()
276 .move_to(0.0, 0.0)
277 .curve_to(50.0, 0.0, 100.0, 50.0, 100.0, 100.0)
278 .curve_to(100.0, 150.0, 50.0, 200.0, 0.0, 200.0);
279
280 let commands = builder.build();
281 assert_eq!(commands.len(), 3);
282
283 match &commands[1] {
284 PathCommand::CurveTo(x1, y1, x2, y2, x3, y3) => {
285 assert_eq!(*x1, 50.0);
286 assert_eq!(*y1, 0.0);
287 assert_eq!(*x2, 100.0);
288 assert_eq!(*y2, 50.0);
289 assert_eq!(*x3, 100.0);
290 assert_eq!(*y3, 100.0);
291 }
292 _ => panic!("Expected CurveTo at index 1"),
293 }
294 }
295
296 #[test]
297 fn test_path_command_debug() {
298 let move_cmd = PathCommand::MoveTo(10.0, 20.0);
299 let debug_str = format!("{:?}", move_cmd);
300 assert!(debug_str.contains("MoveTo"));
301 assert!(debug_str.contains("10.0"));
302 assert!(debug_str.contains("20.0"));
303
304 let line_cmd = PathCommand::LineTo(30.0, 40.0);
305 let line_debug = format!("{:?}", line_cmd);
306 assert!(line_debug.contains("LineTo"));
307
308 let curve_cmd = PathCommand::CurveTo(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
309 let curve_debug = format!("{:?}", curve_cmd);
310 assert!(curve_debug.contains("CurveTo"));
311
312 let close_cmd = PathCommand::ClosePath;
313 let close_debug = format!("{:?}", close_cmd);
314 assert!(close_debug.contains("ClosePath"));
315 }
316
317 #[test]
318 fn test_path_command_clone() {
319 let move_cmd = PathCommand::MoveTo(10.0, 20.0);
320 let move_clone = move_cmd.clone();
321 match (move_cmd, move_clone) {
322 (PathCommand::MoveTo(x1, y1), PathCommand::MoveTo(x2, y2)) => {
323 assert_eq!(x1, x2);
324 assert_eq!(y1, y2);
325 }
326 _ => panic!("Clone failed"),
327 }
328
329 let close_cmd = PathCommand::ClosePath;
330 let close_clone = close_cmd.clone();
331 match close_clone {
332 PathCommand::ClosePath => {}
333 _ => panic!("Clone failed for ClosePath"),
334 }
335 }
336
337 #[test]
338 fn test_path_builder_empty_path() {
339 let builder = PathBuilder::new();
340 let commands = builder.build();
341 assert_eq!(commands.len(), 0);
342 }
343
344 #[test]
345 fn test_path_builder_single_command() {
346 let move_builder = PathBuilder::new().move_to(5.0, 10.0);
348 assert_eq!(move_builder.build().len(), 1);
349
350 let line_builder = PathBuilder::new().line_to(15.0, 20.0);
351 assert_eq!(line_builder.build().len(), 1);
352
353 let curve_builder = PathBuilder::new().curve_to(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
354 assert_eq!(curve_builder.build().len(), 1);
355
356 let close_builder = PathBuilder::new().close();
357 assert_eq!(close_builder.build().len(), 1);
358 }
359
360 #[test]
361 fn test_path_builder_negative_coordinates() {
362 let builder = PathBuilder::new()
363 .move_to(-10.0, -20.0)
364 .line_to(-30.0, -40.0)
365 .curve_to(-1.0, -2.0, -3.0, -4.0, -5.0, -6.0);
366
367 let commands = builder.build();
368 assert_eq!(commands.len(), 3);
369
370 match &commands[0] {
371 PathCommand::MoveTo(x, y) => {
372 assert_eq!(*x, -10.0);
373 assert_eq!(*y, -20.0);
374 }
375 _ => panic!("Expected MoveTo"),
376 }
377 }
378
379 #[test]
380 fn test_path_builder_zero_values() {
381 let builder = PathBuilder::new()
382 .move_to(0.0, 0.0)
383 .line_to(0.0, 0.0)
384 .curve_to(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
385
386 let commands = builder.build();
387 assert_eq!(commands.len(), 3);
388
389 match &commands[0] {
391 PathCommand::MoveTo(x, y) => {
392 assert_eq!(*x, 0.0);
393 assert_eq!(*y, 0.0);
394 }
395 _ => panic!("Expected MoveTo"),
396 }
397 }
398
399 #[test]
400 fn test_path_builder_large_values() {
401 let large_val = 1e6;
402 let builder = PathBuilder::new()
403 .move_to(large_val, large_val)
404 .line_to(large_val * 2.0, large_val * 2.0);
405
406 let commands = builder.build();
407 match &commands[0] {
408 PathCommand::MoveTo(x, y) => {
409 assert_eq!(*x, large_val);
410 assert_eq!(*y, large_val);
411 }
412 _ => panic!("Expected MoveTo"),
413 }
414 }
415}