1use super::FuzzyEq;
2
3#[derive(Clone, Copy, PartialEq, Debug)]
5#[allow(missing_docs)]
6pub enum PathCommand {
7 MoveTo,
8 LineTo,
9 HorizontalLineTo,
10 VerticalLineTo,
11 CurveTo,
12 SmoothCurveTo,
13 Quadratic,
14 SmoothQuadratic,
15 EllipticalArc,
16 ClosePath,
17}
18
19#[allow(missing_docs)]
25#[derive(Clone, Copy, PartialEq, Debug)]
26pub enum PathSegment {
27 MoveTo {
28 abs: bool,
29 x: f64,
30 y: f64,
31 },
32 LineTo {
33 abs: bool,
34 x: f64,
35 y: f64,
36 },
37 HorizontalLineTo {
38 abs: bool,
39 x: f64,
40 },
41 VerticalLineTo {
42 abs: bool,
43 y: f64,
44 },
45 CurveTo {
46 abs: bool,
47 x1: f64,
48 y1: f64,
49 x2: f64,
50 y2: f64,
51 x: f64,
52 y: f64,
53 },
54 SmoothCurveTo {
55 abs: bool,
56 x2: f64,
57 y2: f64,
58 x: f64,
59 y: f64,
60 },
61 Quadratic {
62 abs: bool,
63 x1: f64,
64 y1: f64,
65 x: f64,
66 y: f64,
67 },
68 SmoothQuadratic {
69 abs: bool,
70 x: f64,
71 y: f64,
72 },
73 EllipticalArc {
74 abs: bool,
75 rx: f64,
76 ry: f64,
77 x_axis_rotation: f64,
78 large_arc: bool,
79 sweep: bool,
80 x: f64,
81 y: f64,
82 },
83 ClosePath {
84 abs: bool,
85 },
86}
87
88impl PathSegment {
89 pub fn set_absolute(&mut self, new_abs: bool) {
91 match *self {
92 PathSegment::MoveTo { ref mut abs, .. }
93 | PathSegment::LineTo { ref mut abs, .. }
94 | PathSegment::HorizontalLineTo { ref mut abs, .. }
95 | PathSegment::VerticalLineTo { ref mut abs, .. }
96 | PathSegment::CurveTo { ref mut abs, .. }
97 | PathSegment::SmoothCurveTo { ref mut abs, .. }
98 | PathSegment::Quadratic { ref mut abs, .. }
99 | PathSegment::SmoothQuadratic { ref mut abs, .. }
100 | PathSegment::EllipticalArc { ref mut abs, .. }
101 | PathSegment::ClosePath { ref mut abs, .. } => {
102 *abs = new_abs;
103 }
104 }
105 }
106
107 pub fn cmd(&self) -> PathCommand {
109 match *self {
110 PathSegment::MoveTo { .. } => PathCommand::MoveTo,
111 PathSegment::LineTo { .. } => PathCommand::LineTo,
112 PathSegment::HorizontalLineTo { .. } => PathCommand::HorizontalLineTo,
113 PathSegment::VerticalLineTo { .. } => PathCommand::VerticalLineTo,
114 PathSegment::CurveTo { .. } => PathCommand::CurveTo,
115 PathSegment::SmoothCurveTo { .. } => PathCommand::SmoothCurveTo,
116 PathSegment::Quadratic { .. } => PathCommand::Quadratic,
117 PathSegment::SmoothQuadratic { .. } => PathCommand::SmoothQuadratic,
118 PathSegment::EllipticalArc { .. } => PathCommand::EllipticalArc,
119 PathSegment::ClosePath { .. } => PathCommand::ClosePath,
120 }
121 }
122
123 #[inline]
125 pub fn is_absolute(&self) -> bool {
126 match *self {
127 PathSegment::MoveTo { abs, .. }
128 | PathSegment::LineTo { abs, .. }
129 | PathSegment::HorizontalLineTo { abs, .. }
130 | PathSegment::VerticalLineTo { abs, .. }
131 | PathSegment::CurveTo { abs, .. }
132 | PathSegment::SmoothCurveTo { abs, .. }
133 | PathSegment::Quadratic { abs, .. }
134 | PathSegment::SmoothQuadratic { abs, .. }
135 | PathSegment::EllipticalArc { abs, .. }
136 | PathSegment::ClosePath { abs, .. } => abs,
137 }
138 }
139
140 #[inline]
141 pub fn is_relative(&self) -> bool {
143 !self.is_absolute()
144 }
145
146 pub fn x(&self) -> Option<f64> {
148 match *self {
149 PathSegment::MoveTo { x, .. }
150 | PathSegment::LineTo { x, .. }
151 | PathSegment::HorizontalLineTo { x, .. }
152 | PathSegment::CurveTo { x, .. }
153 | PathSegment::SmoothCurveTo { x, .. }
154 | PathSegment::Quadratic { x, .. }
155 | PathSegment::SmoothQuadratic { x, .. }
156 | PathSegment::EllipticalArc { x, .. } => Some(x),
157
158 PathSegment::VerticalLineTo { .. } | PathSegment::ClosePath { .. } => None,
159 }
160 }
161
162 pub fn y(&self) -> Option<f64> {
164 match *self {
165 PathSegment::MoveTo { y, .. }
166 | PathSegment::LineTo { y, .. }
167 | PathSegment::VerticalLineTo { y, .. }
168 | PathSegment::CurveTo { y, .. }
169 | PathSegment::SmoothCurveTo { y, .. }
170 | PathSegment::Quadratic { y, .. }
171 | PathSegment::SmoothQuadratic { y, .. }
172 | PathSegment::EllipticalArc { y, .. } => Some(y),
173
174 PathSegment::HorizontalLineTo { .. } | PathSegment::ClosePath { .. } => None,
175 }
176 }
177}
178
179impl FuzzyEq for PathSegment {
180 fn fuzzy_eq(&self, other: &Self) -> bool {
181 use self::PathSegment as Seg;
182
183 match (*self, *other) {
185 (
186 Seg::MoveTo { abs, x, y },
187 Seg::MoveTo {
188 abs: oabs,
189 x: ox,
190 y: oy,
191 },
192 )
193 | (
194 Seg::LineTo { abs, x, y },
195 Seg::LineTo {
196 abs: oabs,
197 x: ox,
198 y: oy,
199 },
200 )
201 | (
202 Seg::SmoothQuadratic { abs, x, y },
203 Seg::SmoothQuadratic {
204 abs: oabs,
205 x: ox,
206 y: oy,
207 },
208 ) => abs == oabs && x.fuzzy_eq(&ox) && y.fuzzy_eq(&oy),
209 (Seg::HorizontalLineTo { abs, x }, Seg::HorizontalLineTo { abs: oabs, x: ox }) => {
210 abs == oabs && x.fuzzy_eq(&ox)
211 }
212 (Seg::VerticalLineTo { abs, y }, Seg::VerticalLineTo { abs: oabs, y: oy }) => {
213 abs == oabs && y.fuzzy_eq(&oy)
214 }
215 (
216 Seg::CurveTo {
217 abs,
218 x1,
219 y1,
220 x2,
221 y2,
222 x,
223 y,
224 },
225 Seg::CurveTo {
226 abs: oabs,
227 x1: ox1,
228 y1: oy1,
229 x2: ox2,
230 y2: oy2,
231 x: ox,
232 y: oy,
233 },
234 ) => {
235 abs == oabs
236 && x.fuzzy_eq(&ox)
237 && y.fuzzy_eq(&oy)
238 && x1.fuzzy_eq(&ox1)
239 && y1.fuzzy_eq(&oy1)
240 && x2.fuzzy_eq(&ox2)
241 && y2.fuzzy_eq(&oy2)
242 }
243 (
244 Seg::SmoothCurveTo { abs, x2, y2, x, y },
245 Seg::SmoothCurveTo {
246 abs: oabs,
247 x2: ox2,
248 y2: oy2,
249 x: ox,
250 y: oy,
251 },
252 ) => {
253 abs == oabs
254 && x.fuzzy_eq(&ox)
255 && y.fuzzy_eq(&oy)
256 && x2.fuzzy_eq(&ox2)
257 && y2.fuzzy_eq(&oy2)
258 }
259 (
260 Seg::Quadratic { abs, x1, y1, x, y },
261 Seg::Quadratic {
262 abs: oabs,
263 x1: ox1,
264 y1: oy1,
265 x: ox,
266 y: oy,
267 },
268 ) => {
269 abs == oabs
270 && x.fuzzy_eq(&ox)
271 && y.fuzzy_eq(&oy)
272 && x1.fuzzy_eq(&ox1)
273 && y1.fuzzy_eq(&oy1)
274 }
275 (
276 Seg::EllipticalArc {
277 abs,
278 rx,
279 ry,
280 x_axis_rotation,
281 large_arc,
282 sweep,
283 x,
284 y,
285 },
286 Seg::EllipticalArc {
287 abs: oabs,
288 rx: orx,
289 ry: ory,
290 x_axis_rotation: ox_axis_rotation,
291 large_arc: olarge_arc,
292 sweep: osweep,
293 x: ox,
294 y: oy,
295 },
296 ) => {
297 abs == oabs
298 && x.fuzzy_eq(&ox)
299 && y.fuzzy_eq(&oy)
300 && rx.fuzzy_eq(&orx)
301 && ry.fuzzy_eq(&ory)
302 && x_axis_rotation.fuzzy_eq(&ox_axis_rotation)
303 && large_arc == olarge_arc
304 && sweep == osweep
305 }
306 (Seg::ClosePath { abs }, Seg::ClosePath { abs: oabs }) => abs == oabs,
307 _ => false,
308 }
309 }
310}
311
312#[cfg(test)]
313mod fuzzy_eq_tests {
314 use super::*;
315
316 macro_rules! test {
317 ($name:ident, $seg1:expr, $seg2:expr) => {
318 #[test]
319 fn $name() {
320 assert!($seg1 != $seg2);
321 assert!($seg1.fuzzy_eq(&$seg2));
322 }
323 };
324 }
325
326 test!(
329 m,
330 PathSegment::MoveTo {
331 abs: true,
332 x: 10.0,
333 y: 10.1 + 10.2
334 },
335 PathSegment::MoveTo {
336 abs: true,
337 x: 10.0,
338 y: 20.3
339 }
340 );
341
342 test!(
343 l,
344 PathSegment::LineTo {
345 abs: true,
346 x: 10.0,
347 y: 10.1 + 10.2
348 },
349 PathSegment::LineTo {
350 abs: true,
351 x: 10.0,
352 y: 20.3
353 }
354 );
355
356 test!(
357 h,
358 PathSegment::HorizontalLineTo {
359 abs: true,
360 x: 10.1 + 10.2
361 },
362 PathSegment::HorizontalLineTo { abs: true, x: 20.3 }
363 );
364
365 test!(
366 v,
367 PathSegment::VerticalLineTo {
368 abs: true,
369 y: 10.1 + 10.2
370 },
371 PathSegment::VerticalLineTo { abs: true, y: 20.3 }
372 );
373
374 test!(
375 c,
376 PathSegment::CurveTo {
377 abs: true,
378 x1: 10.0,
379 y1: 10.1 + 10.2,
380 x2: 10.0,
381 y2: 10.0,
382 x: 10.0,
383 y: 10.0
384 },
385 PathSegment::CurveTo {
386 abs: true,
387 x1: 10.0,
388 y1: 20.3,
389 x2: 10.0,
390 y2: 10.0,
391 x: 10.0,
392 y: 10.0
393 }
394 );
395
396 test!(
397 s,
398 PathSegment::SmoothCurveTo {
399 abs: true,
400 x2: 10.0,
401 y2: 10.1 + 10.2,
402 x: 10.0,
403 y: 10.0
404 },
405 PathSegment::SmoothCurveTo {
406 abs: true,
407 x2: 10.0,
408 y2: 20.3,
409 x: 10.0,
410 y: 10.0
411 }
412 );
413
414 test!(
415 q,
416 PathSegment::Quadratic {
417 abs: true,
418 x1: 10.0,
419 y1: 10.1 + 10.2,
420 x: 10.0,
421 y: 10.0
422 },
423 PathSegment::Quadratic {
424 abs: true,
425 x1: 10.0,
426 y1: 20.3,
427 x: 10.0,
428 y: 10.0
429 }
430 );
431
432 test!(
433 t,
434 PathSegment::SmoothQuadratic {
435 abs: true,
436 x: 10.0,
437 y: 10.1 + 10.2
438 },
439 PathSegment::SmoothQuadratic {
440 abs: true,
441 x: 10.0,
442 y: 20.3
443 }
444 );
445
446 test!(
447 a,
448 PathSegment::EllipticalArc {
449 abs: true,
450 rx: 100.0,
451 ry: 100.0,
452 x_axis_rotation: 0.0,
453 large_arc: true,
454 sweep: true,
455 x: 10.1 + 10.2,
456 y: 10.0,
457 },
458 PathSegment::EllipticalArc {
459 abs: true,
460 rx: 100.0,
461 ry: 100.0,
462 x_axis_rotation: 0.0,
463 large_arc: true,
464 sweep: true,
465 x: 20.3,
466 y: 10.0,
467 }
468 );
469}