1 use {
2     FuzzyEq,
3 };
4 
5 /// List of all path commands.
6 #[derive(Clone, Copy, PartialEq, Debug)]
7 #[allow(missing_docs)]
8 pub enum PathCommand {
9     MoveTo,
10     LineTo,
11     HorizontalLineTo,
12     VerticalLineTo,
13     CurveTo,
14     SmoothCurveTo,
15     Quadratic,
16     SmoothQuadratic,
17     EllipticalArc,
18     ClosePath,
19 }
20 
21 /// Representation of the path segment.
22 ///
23 /// If you want to change the segment type (for example MoveTo to LineTo)
24 /// you should create a new segment.
25 /// But you still can change points or make segment relative or absolute.
26 #[allow(missing_docs)]
27 #[derive(Clone, Copy, PartialEq, Debug)]
28 pub enum PathSegment {
29     MoveTo {
30         abs: bool,
31         x: f64,
32         y: f64,
33     },
34     LineTo {
35         abs: bool,
36         x: f64,
37         y: f64,
38     },
39     HorizontalLineTo {
40         abs: bool,
41         x: f64,
42     },
43     VerticalLineTo {
44         abs: bool,
45         y: f64,
46     },
47     CurveTo {
48         abs: bool,
49         x1: f64,
50         y1: f64,
51         x2: f64,
52         y2: f64,
53         x: f64,
54         y: f64,
55     },
56     SmoothCurveTo {
57         abs: bool,
58         x2: f64,
59         y2: f64,
60         x: f64,
61         y: f64,
62     },
63     Quadratic {
64         abs: bool,
65         x1: f64,
66         y1: f64,
67         x: f64,
68         y: f64,
69     },
70     SmoothQuadratic {
71         abs: bool,
72         x: f64,
73         y: f64,
74     },
75     EllipticalArc {
76         abs: bool,
77         rx: f64,
78         ry: f64,
79         x_axis_rotation: f64,
80         large_arc: bool,
81         sweep: bool,
82         x: f64,
83         y: f64,
84     },
85     ClosePath {
86         abs: bool,
87     },
88 }
89 
90 impl PathSegment {
91     /// Sets the segment absolute value.
set_absolute(&mut self, new_abs: bool)92     pub fn set_absolute(&mut self, new_abs: bool) {
93         match *self {
94               PathSegment::MoveTo { ref mut abs, .. }
95             | PathSegment::LineTo { ref mut abs, .. }
96             | PathSegment::HorizontalLineTo { ref mut abs, .. }
97             | PathSegment::VerticalLineTo { ref mut abs, .. }
98             | PathSegment::CurveTo { ref mut abs, .. }
99             | PathSegment::SmoothCurveTo { ref mut abs, .. }
100             | PathSegment::Quadratic { ref mut abs, .. }
101             | PathSegment::SmoothQuadratic { ref mut abs, .. }
102             | PathSegment::EllipticalArc { ref mut abs, .. }
103             | PathSegment::ClosePath { ref mut abs, .. } => { *abs = new_abs; }
104         }
105     }
106 
107     /// Returns a segment type.
cmd(&self) -> PathCommand108     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     /// Returns `true` if the segment is absolute.
124     #[inline]
is_absolute(&self) -> bool125     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     /// Returns `true` if the segment is relative.
is_relative(&self) -> bool142     pub fn is_relative(&self) -> bool {
143         !self.is_absolute()
144     }
145 
146     /// Returns the `x` coordinate of the segment if it has one.
x(&self) -> Option<f64>147     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 { .. }
159             | PathSegment::ClosePath { .. } => None,
160         }
161     }
162 
163     /// Returns the `y` coordinate of the segment if it has one.
y(&self) -> Option<f64>164     pub fn y(&self) -> Option<f64> {
165         match *self {
166               PathSegment::MoveTo { y, .. }
167             | PathSegment::LineTo { y, .. }
168             | PathSegment::VerticalLineTo { y, .. }
169             | PathSegment::CurveTo { y, .. }
170             | PathSegment::SmoothCurveTo { y, .. }
171             | PathSegment::Quadratic { y, .. }
172             | PathSegment::SmoothQuadratic { y, .. }
173             | PathSegment::EllipticalArc { y, .. } => Some(y),
174 
175               PathSegment::HorizontalLineTo { .. }
176             | PathSegment::ClosePath { .. } => None,
177         }
178     }
179 }
180 
181 impl FuzzyEq for PathSegment {
fuzzy_eq(&self, other: &Self) -> bool182     fn fuzzy_eq(&self, other: &Self) -> bool {
183         use self::PathSegment as Seg;
184 
185         // TODO: find a way to wrap it in macro
186         match (*self, *other) {
187             (Seg::MoveTo { abs, x, y }, Seg::MoveTo { abs: oabs, x: ox, y: oy }) |
188             (Seg::LineTo { abs, x, y }, Seg::LineTo { abs: oabs, x: ox, y: oy }) |
189             (Seg::SmoothQuadratic { abs, x, y }, Seg::SmoothQuadratic { abs: oabs, x: ox, y: oy }) => {
190                 abs == oabs && x.fuzzy_eq(&ox) && y.fuzzy_eq(&oy)
191             }
192             (Seg::HorizontalLineTo { abs, x }, Seg::HorizontalLineTo { abs: oabs, x: ox }) => {
193                 abs == oabs && x.fuzzy_eq(&ox)
194             }
195             (Seg::VerticalLineTo { abs, y }, Seg::VerticalLineTo { abs: oabs, y: oy }) => {
196                 abs == oabs && y.fuzzy_eq(&oy)
197             }
198             (Seg::CurveTo { abs, x1, y1, x2, y2, x, y },
199                 Seg::CurveTo { abs: oabs, x1: ox1, y1: oy1, x2: ox2, y2: oy2, x: ox, y: oy }) => {
200                     abs == oabs
201                 &&  x.fuzzy_eq(&ox)  &&  y.fuzzy_eq(&oy)
202                 && x1.fuzzy_eq(&ox1) && y1.fuzzy_eq(&oy1)
203                 && x2.fuzzy_eq(&ox2) && y2.fuzzy_eq(&oy2)
204             }
205             (Seg::SmoothCurveTo { abs, x2, y2, x, y },
206                 Seg::SmoothCurveTo { abs: oabs, x2: ox2, y2: oy2, x: ox, y: oy }) => {
207                 abs == oabs
208                 &&  x.fuzzy_eq(&ox)  &&  y.fuzzy_eq(&oy)
209                 && x2.fuzzy_eq(&ox2) && y2.fuzzy_eq(&oy2)
210             }
211             (Seg::Quadratic { abs, x1, y1, x, y },
212                 Seg::Quadratic { abs: oabs, x1: ox1, y1: oy1, x: ox, y: oy }) => {
213                    abs == oabs
214                 &&  x.fuzzy_eq(&ox)  &&  y.fuzzy_eq(&oy)
215                 && x1.fuzzy_eq(&ox1) && y1.fuzzy_eq(&oy1)
216             }
217             (Seg::EllipticalArc { abs, rx, ry, x_axis_rotation, large_arc, sweep, x, y },
218                 Seg::EllipticalArc { abs: oabs, rx: orx, ry: ory, x_axis_rotation: ox_axis_rotation,
219                     large_arc: olarge_arc, sweep: osweep, x: ox, y: oy }) => {
220                     abs == oabs
221                 &&  x.fuzzy_eq(&ox)  &&  y.fuzzy_eq(&oy)
222                 && rx.fuzzy_eq(&orx) && ry.fuzzy_eq(&ory)
223                 && x_axis_rotation.fuzzy_eq(&ox_axis_rotation)
224                 && large_arc == olarge_arc
225                 && sweep == osweep
226             }
227             (Seg::ClosePath { abs }, Seg::ClosePath { abs: oabs }) => {
228                 abs == oabs
229             }
230             _ => false,
231         }
232     }
233 }
234 
235 #[cfg(test)]
236 mod fuzzy_eq_tests {
237     use super::*;
238 
239     macro_rules! test {
240         ($name:ident,  $seg1:expr, $seg2:expr) => (
241         #[test]
242         fn $name() {
243             assert!($seg1 != $seg2);
244             assert!($seg1.fuzzy_eq(&$seg2));
245         })
246     }
247 
248     // TODO: find a better way
249 
250     test!(m,
251         PathSegment::MoveTo { abs: true, x: 10.0, y: 10.1 + 10.2 },
252         PathSegment::MoveTo { abs: true, x: 10.0, y: 20.3 }
253     );
254 
255     test!(l,
256         PathSegment::LineTo { abs: true, x: 10.0, y: 10.1 + 10.2 },
257         PathSegment::LineTo { abs: true, x: 10.0, y: 20.3 }
258     );
259 
260     test!(h,
261         PathSegment::HorizontalLineTo { abs: true, x: 10.1 + 10.2 },
262         PathSegment::HorizontalLineTo { abs: true, x: 20.3 }
263     );
264 
265     test!(v,
266         PathSegment::VerticalLineTo { abs: true, y: 10.1 + 10.2 },
267         PathSegment::VerticalLineTo { abs: true, y: 20.3 }
268     );
269 
270     test!(c,
271         PathSegment::CurveTo { abs: true, x1: 10.0, y1: 10.1 + 10.2, x2: 10.0, y2: 10.0, x: 10.0, y: 10.0 },
272         PathSegment::CurveTo { abs: true, x1: 10.0, y1: 20.3, x2: 10.0, y2: 10.0, x: 10.0, y: 10.0 }
273     );
274 
275     test!(s,
276         PathSegment::SmoothCurveTo { abs: true, x2: 10.0, y2: 10.1 + 10.2, x: 10.0, y: 10.0 },
277         PathSegment::SmoothCurveTo { abs: true, x2: 10.0, y2: 20.3, x: 10.0, y: 10.0 }
278     );
279 
280     test!(q,
281         PathSegment::Quadratic { abs: true, x1: 10.0, y1: 10.1 + 10.2, x: 10.0, y: 10.0 },
282         PathSegment::Quadratic { abs: true, x1: 10.0, y1: 20.3, x: 10.0, y: 10.0 }
283     );
284 
285     test!(t,
286         PathSegment::SmoothQuadratic { abs: true, x: 10.0, y: 10.1 + 10.2 },
287         PathSegment::SmoothQuadratic { abs: true, x: 10.0, y: 20.3 }
288     );
289 
290     test!(a,
291         PathSegment::EllipticalArc {
292             abs: true,
293             rx: 100.0,
294             ry: 100.0,
295             x_axis_rotation: 0.0,
296             large_arc: true,
297             sweep: true,
298             x: 10.1 + 10.2,
299             y: 10.0,
300         },
301         PathSegment::EllipticalArc {
302             abs: true,
303             rx: 100.0,
304             ry: 100.0,
305             x_axis_rotation: 0.0,
306             large_arc: true,
307             sweep: true,
308             x: 20.3,
309             y: 10.0,
310         }
311     );
312 }
313