1mod tokenize;
2
3use std::{iter::Peekable, str::Chars};
4use tokenize::{Tokenize, Token};
5use crate::point::Point;
6use crate::path::Path;
7use crate::path_builder::PathBuilder;
8
9enum LastControlPoint {
10 Quad(Point),
11 Cubic(Point),
12 None
13}
14
15pub fn parse(str: &str) -> Result<Path, String> {
17 let mut chars = str.chars();
18 let mut tokens = Tokenize::new(&mut chars).peekable();
19 let mut pb = PathBuilder::new();
20 let mut last_control_point = LastControlPoint::None;
21 loop {
22 match tokens.next().unwrap()? {
23 Token::LargeM => {
24 let ns = parse_n_nums(&mut tokens, 2)?;
25 pb.move_to(ns[0], ns[1]);
26 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
27 let ns = parse_n_nums(&mut tokens, 2)?;
28 pb.line_to(ns[0], ns[1]);
29 }
30 last_control_point = LastControlPoint::None;
31 }
32 Token::SmallM => {
33 let ns = parse_n_nums(&mut tokens, 2)?;
34 let current = pb.current_pos().unwrap_or((0.0, 0.0));
35 pb.move_to(current.0 + ns[0], current.1 + ns[1]);
36 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
37 let ns = parse_n_nums(&mut tokens, 2)?;
38 let current = pb.current_pos().unwrap_or((0.0, 0.0));
39 pb.line_to(current.0 + ns[0], current.1 + ns[1]);
40 }
41 last_control_point = LastControlPoint::None;
42 }
43 Token::LargeL => {
44 let ns = parse_n_nums(&mut tokens, 2)?;
45 pb.line_to(ns[0], ns[1]);
46 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
47 let ns = parse_n_nums(&mut tokens, 2)?;
48 pb.line_to(ns[0], ns[1]);
49 }
50 last_control_point = LastControlPoint::None;
51 }
52 Token::SmallL => {
53 let ns = parse_n_nums(&mut tokens, 2)?;
54 let current = pb.current_pos().unwrap_or((0.0, 0.0));
55 pb.line_to(current.0 + ns[0], current.1 + ns[1]);
56 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
57 let ns = parse_n_nums(&mut tokens, 2)?;
58 let current = pb.current_pos().unwrap_or((0.0, 0.0));
59 pb.line_to(current.0 + ns[0], current.1 + ns[1]);
60 }
61 last_control_point = LastControlPoint::None;
62 }
63 Token::LargeH => {
64 let ns = parse_n_nums(&mut tokens, 1)?;
65 let current = pb.current_pos().unwrap_or((0.0, 0.0));
66 pb.line_to(ns[0], current.1);
67 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
68 let ns = parse_n_nums(&mut tokens, 1)?;
69 let current = pb.current_pos().unwrap_or((0.0, 0.0));
70 pb.line_to(ns[0], current.1);
71 }
72 last_control_point = LastControlPoint::None;
73 }
74 Token::SmallH => {
75 let ns = parse_n_nums(&mut tokens, 1)?;
76 let current = pb.current_pos().unwrap_or((0.0, 0.0));
77 pb.line_to(current.0 + ns[0], current.1);
78 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
79 let ns = parse_n_nums(&mut tokens, 1)?;
80 let current = pb.current_pos().unwrap_or((0.0, 0.0));
81 pb.line_to(current.0 + ns[0], current.1);
82 }
83 last_control_point = LastControlPoint::None;
84 }
85 Token::LargeV => {
86 let ns = parse_n_nums(&mut tokens, 1)?;
87 let current = pb.current_pos().unwrap_or((0.0, 0.0));
88 pb.line_to(current.0, ns[0]);
89 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
90 let ns = parse_n_nums(&mut tokens, 1)?;
91 let current = pb.current_pos().unwrap_or((0.0, 0.0));
92 pb.line_to(current.0, ns[0]);
93 }
94 last_control_point = LastControlPoint::None;
95 }
96 Token::SmallV => {
97 let ns = parse_n_nums(&mut tokens, 1)?;
98 let current = pb.current_pos().unwrap_or((0.0, 0.0));
99 pb.line_to(current.0, current.1 + ns[0]);
100 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
101 let ns = parse_n_nums(&mut tokens, 1)?;
102 let current = pb.current_pos().unwrap_or((0.0, 0.0));
103 pb.line_to(current.0, current.1 + ns[0]);
104 }
105 last_control_point = LastControlPoint::None;
106 }
107 Token::LargeQ => {
108 let mut ns = parse_n_nums(&mut tokens, 4)?;
109 pb.quad(ns[0], ns[1], ns[2], ns[3]);
110 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
111 ns = parse_n_nums(&mut tokens, 4)?;
112 pb.quad(ns[0], ns[1], ns[2], ns[3]);
113 }
114 last_control_point = LastControlPoint::Quad(Point(ns[0], ns[1]));
115 }
116 Token::SmallQ => {
117 let mut ns = parse_n_nums(&mut tokens, 4)?;
118 let mut current = pb.current_pos().unwrap_or((0.0, 0.0));
119 pb.quad(current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3]);
120 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
121 ns = parse_n_nums(&mut tokens, 4)?;
122 current = pb.current_pos().unwrap_or((0.0, 0.0));
123 pb.quad(current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3]);
124 }
125 last_control_point = LastControlPoint::Quad(Point(current.0 + ns[0], current.1 + ns[1]));
126 }
127 Token::LargeT => {
128 let mut ns = parse_n_nums(&mut tokens, 2)?;
129 let mut cp = quad_reflection_point(&last_control_point, pb.current_pos().unwrap_or((0.0, 0.0)).into());
130 pb.quad(cp.0, cp.1, ns[0], ns[1]);
131 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
132 cp = Point(ns[0], ns[1]) * 2.0 - cp;
133 ns = parse_n_nums(&mut tokens, 2)?;
134 pb.quad(cp.0, cp.1, ns[0], ns[1]);
135 }
136 last_control_point = LastControlPoint::Quad(cp);
137 }
138 Token::SmallT => {
139 let mut ns = parse_n_nums(&mut tokens, 2)?;
140 let mut current = pb.current_pos().unwrap_or((0.0, 0.0)).into();
141 let mut cp = quad_reflection_point(&last_control_point, current);
142 pb.quad(cp.0, cp.1, current.0 + ns[0], current.1 + ns[1]);
143 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
144 cp = (current + Point(ns[0], ns[1])) * 2.0 - cp;
145 current = current + Point(ns[0], ns[1]);
146 ns = parse_n_nums(&mut tokens, 2)?;
147 pb.quad(cp.0, cp.1, current.0 + ns[0], current.1 + ns[1]);
148 }
149 last_control_point = LastControlPoint::Quad(cp);
150 }
151 Token::LargeC => {
152 let mut ns = parse_n_nums(&mut tokens, 6)?;
153 pb.cubic(ns[0], ns[1], ns[2], ns[3], ns[4], ns[5]);
154 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
155 ns = parse_n_nums(&mut tokens, 6)?;
156 pb.cubic(ns[0], ns[1], ns[2], ns[3], ns[4], ns[5]);
157 }
158 last_control_point = LastControlPoint::Cubic(Point(ns[2], ns[3]));
159 }
160 Token::SmallC => {
161 let mut ns = parse_n_nums(&mut tokens, 6)?;
162 let mut current = pb.current_pos().unwrap_or((0.0, 0.0));
163 pb.cubic(current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3], current.0 + ns[4], current.1 + ns[5]);
164 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
165 ns = parse_n_nums(&mut tokens, 6)?;
166 current = pb.current_pos().unwrap_or((0.0, 0.0));
167 pb.cubic(current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3], current.0 + ns[4], current.1 + ns[5]);
168 }
169 last_control_point = LastControlPoint::Cubic(Point(current.0 + ns[2], current.1 + ns[3]));
170 }
171 Token::LargeS => {
172 let mut ns = parse_n_nums(&mut tokens, 4)?;
173 let mut cp = cubic_reflection_point(&last_control_point, pb.current_pos().unwrap_or((0.0, 0.0)).into());
174 pb.cubic(cp.0, cp.1, ns[0], ns[1], ns[2], ns[3]);
175 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
176 cp = Point(ns[2], ns[3]) * 2.0 - Point(ns[0], ns[1]);
177 ns = parse_n_nums(&mut tokens, 4)?;
178 pb.cubic(cp.0, cp.1, ns[0], ns[1], ns[2], ns[3]);
179 }
180 last_control_point = LastControlPoint::Quad(Point(ns[0], ns[1]));
181 }
182 Token::SmallS => {
183 let mut ns = parse_n_nums(&mut tokens, 4)?;
184 let mut current = pb.current_pos().unwrap_or((0.0, 0.0)).into();
185 let mut cp = cubic_reflection_point(&last_control_point, current);
186 pb.cubic(cp.0, cp.1, current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3]);
187 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
188 cp = current + Point(ns[0], ns[1]) * 2.0 - Point(ns[2], ns[3]);
189 current = current + Point(ns[2], ns[3]);
190 ns = parse_n_nums(&mut tokens, 4)?;
191 pb.cubic(cp.0, cp.1, current.0 + ns[0], current.1 + ns[1], current.0 + ns[2], current.1 + ns[3]);
192 }
193 last_control_point = LastControlPoint::Quad(Point(current.0 + ns[0], current.1 + ns[1]));
194 }
195 Token::LargeA => {
196 let mut ns = parse_n_nums(&mut tokens, 7)?;
197 pb.ellipse_from_endpoint(ns[0], ns[1], ns[2].to_radians(), ns[3] != 0.0, ns[4] != 0.0, ns[5], ns[6]);
198 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
199 ns = parse_n_nums(&mut tokens, 7)?;
200 pb.ellipse_from_endpoint(ns[0], ns[1], ns[2].to_radians(), ns[3] != 0.0, ns[4] != 0.0, ns[5], ns[6]);
201 }
202 last_control_point = LastControlPoint::None;
203 }
204 Token::SmallA => {
205 let mut ns = parse_n_nums(&mut tokens, 7)?;
206 let mut current: Point = pb.current_pos().unwrap_or((0.0, 0.0)).into();
207 current = current + Point(ns[5], ns[6]);
208 pb.ellipse_from_endpoint(ns[0], ns[1], ns[2].to_radians(), ns[3] != 0.0, ns[4] != 0.0, current.0, current.1);
209 while is_num_or_comma(&tokens.peek().unwrap().to_owned()?) {
210 ns = parse_n_nums(&mut tokens, 7)?;
211 current = current + Point(ns[5], ns[6]);
212 pb.ellipse_from_endpoint(ns[0], ns[1], ns[2].to_radians(), ns[3] != 0.0, ns[4] != 0.0, current.0, current.1);
213 }
214 last_control_point = LastControlPoint::None;
215 }
216 Token::LargeZ | Token::SmallZ => {
217 pb.close();
218 last_control_point = LastControlPoint::None;
219 }
220 Token::EOS => {
221 break;
222 }
223 token => {
224 return Err(format!("Unexpected token: {:?}", token));
225 }
226 }
227 }
228 Ok(pb.end())
229}
230
231fn parse_n_nums(tokens: &mut Peekable<Tokenize<Chars>>, n: usize) -> Result<Vec<f64>, String> {
232 let mut v = Vec::new();
233 for _ in 0..n {
234 skip_comma(tokens)?;
235 match tokens.next().unwrap()? {
236 Token::Num(n) => {
237 v.push(n);
238 }
239 token => {
240 return Err(format!("Expects num, but found: {:?}", token));
241 }
242 };
243 }
244 Ok(v)
245}
246
247fn is_num_or_comma(token: &Token) -> bool {
248 match token {
249 Token::Num(_) | Token::Comma => true,
250 _ => false,
251 }
252}
253
254fn skip_comma(tokens: &mut Peekable<Tokenize<Chars>>) -> Result<(), String> {
255 match tokens.peek().unwrap().to_owned()? {
256 Token::Comma => {
257 tokens.next().unwrap()?;
258 }
259 _ => {}
260 }
261 Ok(())
262}
263
264fn quad_reflection_point(lcp: &LastControlPoint, pos: Point) -> Point {
265 match lcp {
266 LastControlPoint::Quad(p) => {
267 pos * 2.0 - *p
268 }
269 _ => {
270 pos
271 }
272 }
273}
274
275fn cubic_reflection_point(lcp: &LastControlPoint, pos: Point) -> Point {
276 match lcp {
277 LastControlPoint::Cubic(p) => {
278 pos * 2.0 - *p
279 }
280 _ => {
281 pos
282 }
283 }
284}