1#![warn(missing_docs)]
14
15use std::iter::Peekable;
16use std::str::Chars;
17
18mod curves;
19mod elements;
20mod utils;
21
22use elements::{ PathElementCommand, PathElementLabel, PreviousElementCommand };
23
24pub fn parse<'a>(path:&'a str) -> PathParser<'a> {
28 PathParser::new(path, 64)
29}
30
31pub fn parse_with_resolution<'a>(path:&'a str, resolution:u64) -> PathParser<'a> {
43 PathParser::new(path, resolution)
44}
45
46pub struct PathParser<'a> {
48 data: Peekable<Chars<'a>>,
49 current_command: Option<PathElementCommand>,
50 previous_command: Option<PreviousElementCommand>,
51 cursor: (f64, f64),
52 paths: Vec<Vec<(f64, f64)>>,
53 hard_ended: bool,
54 resolution: u64,
55}
56impl<'a> PathParser<'a> {
57 fn new(data:&'a str, resolution:u64) -> Self {
58 Self {
59 data: data.chars().peekable(),
60 current_command: None,
61 previous_command: None,
62 cursor: (0.0, 0.0),
63 paths: Vec::new(),
64 hard_ended: false,
65 resolution,
66 }
67 }
68
69 fn discard_separators(&mut self) {
71 while let Some(&ch) = self.data.peek() {
72 if !utils::is_separator(ch) { break;
74 }
75
76 let _ = self.data.next();
77 }
78 }
79
80 fn get_float(&mut self) -> Option<f64> {
81 self.discard_separators();
82
83 let mut s = String::new();
84 let mut decimal_count = 0;
85 while let Some(&ch) = self.data.peek() {
86 if ch == '+' || ch == '-' && s.is_empty() {
87 s.push(self.data.next()?);
88 } else if ch == '.' && decimal_count == 0 {
89 s.push(self.data.next()?);
90 decimal_count = 1;
91 } else if ch.is_digit(10) {
92 s.push(self.data.next()?);
93 } else {
94 break;
95 }
96 }
97
98 s.parse::<f64>().ok()
99 }
100
101 fn get_point(&mut self, relative:bool) -> Option<(f64, f64)> {
102 let x = self.get_float()?;
103 let y = self.get_float()?;
104
105 Some(match relative {
106 true => utils::add_point(self.cursor, (x, y)),
107 false => (x, y),
108 })
109 }
110
111 fn get_bool(&mut self) -> Option<bool> {
112 let n = self.get_float()?; if n == 1.0 {
114 Some(true)
115 } else if n == 0.0 {
116 Some(false)
117 } else {
118 None
119 }
120 }
121
122 fn get_path(&mut self) -> Option<(bool, Vec<(f64, f64)>)> {
124 let mut ended = false;
125 let mut hard_ended = false;
126 while !ended && !hard_ended {
127 (ended, hard_ended) = match self.advance() {
128 Some(ended) => (ended, false),
129 None => (false, true),
130 };
131 }
132
133 self.hard_ended = hard_ended;
134 self.paths.pop().map(|paths| (ended, paths))
135 }
136
137 fn advance(&mut self) -> Option<bool> {
138 self.discard_separators();
139 let elem = match utils::is_number_part(*self.data.peek()?) {
140 true => self.current_command?.updated(),
141 false => PathElementCommand::from_ch(self.data.next()?)?,
142 };
143
144 self.current_command = Some(elem);
146
147 let command = match elem.label() {
149 PathElementLabel::Move => self.handle_move(elem.relative()),
150 PathElementLabel::Line => self.handle_line(elem.relative()),
151 PathElementLabel::Horizontal => self.handle_horizontal(elem.relative()),
152 PathElementLabel::Vertical => self.handle_vertical(elem.relative()),
153 PathElementLabel::CubicBezier => self.handle_cubic_bezier(elem.relative()),
154 PathElementLabel::SmoothCubicBezier => self.handle_smooth_cubic_bezier(elem.relative()),
155 PathElementLabel::QuadraticBezier => self.handle_quadratic_bezier(elem.relative()),
156 PathElementLabel::SmoothQuadraticBezier => self.handle_smooth_quadratic_bezier(elem.relative()),
157 PathElementLabel::Arc => self.handle_arc(elem.relative()),
158 PathElementLabel::End => self.handle_end(),
159 }?;
160
161 self.previous_command = Some(command);
162 Some(self.previous_command == Some(PreviousElementCommand::End)) }
164
165 fn handle_move(&mut self, relative:bool) -> Option<PreviousElementCommand> {
166 self.cursor = self.get_point(relative)?;
167 self.paths.push(vec![self.cursor]);
168
169 Some(PreviousElementCommand::NotCurve)
170 }
171
172 fn update_paths(&mut self, end:(f64, f64)) {
173 if self.paths.len() == 0 {
175 self.paths.push(vec![self.cursor]);
176 }
177
178 let n = self.paths.len() - 1;
180 let n2 = self.paths[n].len();
181 if n2 > 0 && self.paths[n][n2 - 1] != self.cursor {
182 self.paths[n].push(self.cursor);
183 }
184
185 self.cursor = end;
187 }
188
189 fn insert_points(&mut self, mut points:Vec<(f64, f64)>) {
190 let end = match points.len() {
191 0 => self.cursor,
192 _ => points[points.len() - 1],
193 };
194
195 self.update_paths(end);
196 let n = self.paths.len() - 1;
197
198 self.paths[n].append(&mut points);
199 }
201
202 fn insert_line(&mut self, end:(f64, f64)) -> Option<PreviousElementCommand> {
203 self.update_paths(end);
204
205 let n = self.paths.len() - 1;
206 self.paths[n].push(end);
207
208 Some(PreviousElementCommand::NotCurve)
209 }
210
211 fn handle_line(&mut self, relative:bool) -> Option<PreviousElementCommand> {
212 let end = self.get_point(relative)?;
213 self.insert_line(end)
214 }
215
216 fn handle_horizontal(&mut self, relative:bool) -> Option<PreviousElementCommand> {
217 let y = self.cursor.1;
218 let x = match relative {
219 true => self.cursor.0 + self.get_float()?,
220 false => self.get_float()?,
221 };
222
223 self.insert_line((x, y))
224 }
225
226 fn handle_vertical(&mut self, relative:bool) -> Option<PreviousElementCommand> {
227 let x = self.cursor.0;
228 let y = match relative {
229 true => self.cursor.1 + self.get_float()?,
230 false => self.get_float()?,
231 };
232
233 self.insert_line((x, y))
234 }
235
236 fn handle_end(&mut self) -> Option<PreviousElementCommand> {
237 if self.paths.len() > 0 && self.paths[0].len() > 0 {
238 self.cursor = self.paths[0][0];
239 }
240
241 Some(PreviousElementCommand::End)
242 }
243
244 fn handle_cubic_bezier(&mut self, relative:bool) -> Option<PreviousElementCommand> {
245 let p1 = self.get_point(relative)?;
246 let p2 = self.get_point(relative)?;
247 let end = self.get_point(relative)?;
248
249 let points = curves::compute_cubic_bezier(self.cursor, p1, p2, end, self.resolution);
250 self.insert_points(points);
251
252 Some(PreviousElementCommand::CubicBezier(p2))
253 }
254
255 fn handle_smooth_cubic_bezier(&mut self, relative:bool) -> Option<PreviousElementCommand> {
256 let p2 = self.get_point(relative)?;
257 let end = self.get_point(relative)?;
258
259 let p1 = match self.previous_command {
260 Some(PreviousElementCommand::CubicBezier(p1)) => utils::reflect_point(p1, self.cursor),
261 _ => self.cursor,
262 };
263
264 let points = curves::compute_cubic_bezier(self.cursor, p1, p2, end, self.resolution);
265 self.insert_points(points);
266
267 Some(PreviousElementCommand::CubicBezier(p2))
268 }
269
270 fn handle_quadratic_bezier(&mut self, relative:bool) -> Option<PreviousElementCommand> {
271 let p1 = self.get_point(relative)?;
272 let end = self.get_point(relative)?;
273
274 let points = curves::compute_quadratic_bezier(self.cursor, p1, end, self.resolution);
275 self.insert_points(points);
276
277 Some(PreviousElementCommand::QuadraticBezier(p1))
278 }
279
280 fn handle_smooth_quadratic_bezier(&mut self, relative:bool) -> Option<PreviousElementCommand> {
281 let end = self.get_point(relative)?;
282 let p1 = match self.previous_command {
283 Some(PreviousElementCommand::QuadraticBezier(p1)) => utils::reflect_point(p1, self.cursor),
284 _ => self.cursor,
285 };
286
287 let points = curves::compute_quadratic_bezier(self.cursor, p1, end, self.resolution);
288 self.insert_points(points);
289
290 Some(PreviousElementCommand::QuadraticBezier(p1))
291 }
292
293 fn handle_arc(&mut self, relative:bool) -> Option<PreviousElementCommand> {
294 let r = self.get_point(relative)?;
295 let rotation = self.get_float()?;
296 let large = self.get_bool()?;
297 let sweep = self.get_bool()?;
298 let end = self.get_point(relative)?;
299
300 let points = curves::compute_arc(self.cursor, r, rotation, large, sweep, end, self.resolution);
301 self.insert_points(points);
302
303 Some(PreviousElementCommand::NotCurve)
304 }
305}
306
307impl Iterator for PathParser<'_> {
308 type Item = (bool, Vec<(f64, f64)>);
309 fn next(&mut self) -> Option<Self::Item> {
311 if self.hard_ended {
312 self.paths.pop().map(|paths| (false, paths))
313 } else {
314 self.get_path()
315 }
316 }
317}