1use std::f32::consts::PI;
2use std::slice;
3
4use crate::geometry::{
5 self,
6 Transform2D,
7};
8
9mod cache;
10pub use cache::{
11 Convexity,
12 PathCache,
13};
14
15const KAPPA90: f32 = 0.552_284_8; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
20pub enum Solidity {
21 Solid = 1,
23 Hole = 2,
25}
26
27impl Default for Solidity {
28 fn default() -> Self {
29 Self::Solid
30 }
31}
32
33#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34#[repr(u8)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub enum PackedVerb {
37 MoveTo,
38 LineTo,
39 BezierTo,
40 Solid,
41 Hole,
42 Close,
43}
44
45#[derive(Copy, Clone, Debug)]
46pub enum Verb {
47 MoveTo(f32, f32),
48 LineTo(f32, f32),
49 BezierTo(f32, f32, f32, f32, f32, f32),
50 Solid,
51 Hole,
52 Close,
53}
54
55impl Verb {
56 fn num_coordinates(&self) -> usize {
57 match *self {
58 Self::MoveTo(..) => 2,
59 Self::LineTo(..) => 2,
60 Self::BezierTo(..) => 6,
61 Self::Solid => 0,
62 Self::Hole => 0,
63 Self::Close => 0,
64 }
65 }
66
67 fn from_packed(packed: &PackedVerb, coords: &[f32]) -> Self {
68 match *packed {
69 PackedVerb::MoveTo => Self::MoveTo(coords[0], coords[1]),
70 PackedVerb::LineTo => Self::LineTo(coords[0], coords[1]),
71 PackedVerb::BezierTo => Self::BezierTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]),
72 PackedVerb::Solid => Self::Solid,
73 PackedVerb::Hole => Self::Hole,
74 PackedVerb::Close => Self::Close,
75 }
76 }
77}
78
79#[derive(Default, Clone, Debug)]
81#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
82pub struct Path {
83 verbs: Vec<PackedVerb>,
84 coords: Vec<f32>,
85 lastx: f32,
86 lasty: f32,
87 dist_tol: f32,
88 #[cfg_attr(feature = "serde", serde(skip))]
89 pub(crate) cache: Option<(u64, PathCache)>,
90}
91
92impl Path {
93 pub fn new() -> Self {
95 Self {
96 dist_tol: 0.01,
97 ..Default::default()
98 }
99 }
100
101 pub fn size(&self) -> usize {
103 std::mem::size_of::<PackedVerb>() * self.verbs.len() + std::mem::size_of::<f32>() * self.coords.len()
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.verbs.is_empty()
109 }
110
111 pub fn set_distance_tolerance(&mut self, value: f32) {
113 self.dist_tol = value;
114 }
115
116 pub fn verbs(&self) -> PathIter<'_> {
118 PathIter {
119 verbs: self.verbs.iter(),
120 coords: &self.coords,
121 }
122 }
123
124 pub(crate) fn cache<'a>(&'a mut self, transform: &Transform2D, tess_tol: f32, dist_tol: f32) -> &'a mut PathCache {
125 let key = transform.cache_key();
131
132 let mut needs_rebuild = true;
134
135 if let Some((transform_cache_key, _cache)) = self.cache.as_ref() {
136 needs_rebuild = key != *transform_cache_key;
137 }
138
139 if needs_rebuild {
140 let path_cache = PathCache::new(self.verbs(), transform, tess_tol, dist_tol);
141 self.cache = Some((key, path_cache));
142 }
143
144 &mut self.cache.as_mut().unwrap().1
145 }
146
147 pub fn move_to(&mut self, x: f32, y: f32) {
151 self.append(&[PackedVerb::MoveTo], &[x, y]);
152 }
153
154 pub fn line_to(&mut self, x: f32, y: f32) {
156 self.append(&[PackedVerb::LineTo], &[x, y]);
157 }
158
159 pub fn bezier_to(&mut self, c1x: f32, c1y: f32, c2x: f32, c2y: f32, x: f32, y: f32) {
161 self.append(&[PackedVerb::BezierTo], &[c1x, c1y, c2x, c2y, x, y]);
162 }
163
164 pub fn quad_to(&mut self, cx: f32, cy: f32, x: f32, y: f32) {
166 let x0 = self.lastx;
167 let y0 = self.lasty;
168
169 self.append(
170 &[PackedVerb::BezierTo],
171 &[
172 x0 + 2.0 / 3.0 * (cx - x0),
173 y0 + 2.0 / 3.0 * (cy - y0),
174 x + 2.0 / 3.0 * (cx - x),
175 y + 2.0 / 3.0 * (cy - y),
176 x,
177 y,
178 ],
179 );
180 }
181
182 pub fn close(&mut self) {
184 self.append(&[PackedVerb::Close], &[]);
185 }
186
187 pub fn solidity(&mut self, solidity: Solidity) {
189 match solidity {
190 Solidity::Solid => self.append(&[PackedVerb::Solid], &[]),
191 Solidity::Hole => self.append(&[PackedVerb::Hole], &[]),
192 }
193 }
194
195 pub fn arc(&mut self, cx: f32, cy: f32, r: f32, a0: f32, a1: f32, dir: Solidity) {
199 let mut da = a1 - a0;
200
201 if dir == Solidity::Hole {
202 if da.abs() >= PI * 2.0 {
203 da = PI * 2.0;
204 } else {
205 while da < 0.0 {
206 da += PI * 2.0
207 }
208 }
209 } else if da.abs() >= PI * 2.0 {
210 da = -PI * 2.0;
211 } else {
212 while da > 0.0 {
213 da -= PI * 2.0
214 }
215 }
216
217 let ndivs = ((da.abs() / (PI * 0.5) + 0.5) as i32).min(5).max(1);
219 let hda = (da / ndivs as f32) / 2.0;
220 let mut kappa = (4.0 / 3.0 * (1.0 - hda.cos()) / hda.sin()).abs();
221
222 let mut commands = Vec::with_capacity(ndivs as usize);
223 let mut coords = Vec::with_capacity(ndivs as usize);
224
225 if dir == Solidity::Solid {
226 kappa = -kappa;
227 }
228
229 let (mut px, mut py, mut ptanx, mut ptany) = (0f32, 0f32, 0f32, 0f32);
230
231 for i in 0..=ndivs {
232 let a = a0 + da * (i as f32 / ndivs as f32);
233 let dx = a.cos();
234 let dy = a.sin();
235 let x = cx + dx * r;
236 let y = cy + dy * r;
237 let tanx = -dy * r * kappa;
238 let tany = dx * r * kappa;
239
240 if i == 0 {
241 let first_move = if !self.verbs.is_empty() {
242 PackedVerb::LineTo
243 } else {
244 PackedVerb::MoveTo
245 };
246
247 commands.push(first_move);
248 coords.extend_from_slice(&[x, y]);
249 } else {
250 commands.push(PackedVerb::BezierTo);
251 coords.extend_from_slice(&[px + ptanx, py + ptany, x - tanx, y - tany, x, y]);
252 }
253
254 px = x;
255 py = y;
256 ptanx = tanx;
257 ptany = tany;
258 }
259
260 self.append(&commands, &coords);
261 }
262
263 pub fn arc_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, radius: f32) {
265 if self.verbs.is_empty() {
266 return;
267 }
268
269 let x0 = self.lastx;
270 let y0 = self.lasty;
271
272 if geometry::pt_equals(x0, y0, x1, y1, self.dist_tol)
274 || geometry::pt_equals(x1, y1, x2, y2, self.dist_tol)
275 || geometry::dist_pt_segment(x1, y1, x0, y0, x2, y2) < self.dist_tol * self.dist_tol
276 || radius < self.dist_tol
277 {
278 self.line_to(x1, y1);
279 }
280
281 let mut dx0 = x0 - x1;
282 let mut dy0 = y0 - y1;
283 let mut dx1 = x2 - x1;
284 let mut dy1 = y2 - y1;
285
286 geometry::normalize(&mut dx0, &mut dy0);
287 geometry::normalize(&mut dx1, &mut dy1);
288
289 let a = (dx0 * dx1 + dy0 * dy1).acos();
290 let d = radius / (a / 2.0).tan();
291
292 if d > 10000.0 {
293 return self.line_to(x1, y1);
294 }
295
296 let (cx, cy, a0, a1, dir);
297
298 if geometry::cross(dx0, dy0, dx1, dy1) > 0.0 {
299 cx = x1 + dx0 * d + dy0 * radius;
300 cy = y1 + dy0 * d + -dx0 * radius;
301 a0 = dx0.atan2(-dy0);
302 a1 = -dx1.atan2(dy1);
303 dir = Solidity::Hole;
304 } else {
305 cx = x1 + dx0 * d + -dy0 * radius;
306 cy = y1 + dy0 * d + dx0 * radius;
307 a0 = -dx0.atan2(dy0);
308 a1 = dx1.atan2(-dy1);
309 dir = Solidity::Solid;
310 }
311
312 self.arc(cx, cy, radius, a0, a1, dir);
313 }
314
315 pub fn rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
317 self.append(
318 &[
319 PackedVerb::MoveTo,
320 PackedVerb::LineTo,
321 PackedVerb::LineTo,
322 PackedVerb::LineTo,
323 PackedVerb::Close,
324 ],
325 &[x, y, x, y + h, x + w, y + h, x + w, y],
326 );
327 }
328
329 pub fn rounded_rect(&mut self, x: f32, y: f32, w: f32, h: f32, r: f32) {
331 self.rounded_rect_varying(x, y, w, h, r, r, r, r);
332 }
333
334 pub fn rounded_rect_varying(
336 &mut self,
337 x: f32,
338 y: f32,
339 w: f32,
340 h: f32,
341 rad_top_left: f32,
342 rad_top_right: f32,
343 rad_bottom_right: f32,
344 rad_bottom_left: f32,
345 ) {
346 if rad_top_left < 0.1 && rad_top_right < 0.1 && rad_bottom_right < 0.1 && rad_bottom_left < 0.1 {
347 self.rect(x, y, w, h);
348 } else {
349 let halfw = w.abs() * 0.5;
350 let halfh = h.abs() * 0.5;
351
352 let rx_bl = rad_bottom_left.min(halfw) * w.signum();
353 let ry_bl = rad_bottom_left.min(halfh) * h.signum();
354
355 let rx_br = rad_bottom_right.min(halfw) * w.signum();
356 let ry_br = rad_bottom_right.min(halfh) * h.signum();
357
358 let rx_tr = rad_top_right.min(halfw) * w.signum();
359 let ry_tr = rad_top_right.min(halfh) * h.signum();
360
361 let rx_tl = rad_top_left.min(halfw) * w.signum();
362 let ry_tl = rad_top_left.min(halfh) * h.signum();
363
364 self.append(
365 &[
366 PackedVerb::MoveTo,
367 PackedVerb::LineTo,
368 PackedVerb::BezierTo,
369 PackedVerb::LineTo,
370 PackedVerb::BezierTo,
371 PackedVerb::LineTo,
372 PackedVerb::BezierTo,
373 PackedVerb::LineTo,
374 PackedVerb::BezierTo,
375 PackedVerb::Close,
376 ],
377 &[
378 x,
379 y + ry_tl,
380 x,
381 y + h - ry_bl,
382 x,
384 y + h - ry_bl * (1.0 - KAPPA90),
385 x + rx_bl * (1.0 - KAPPA90),
386 y + h,
387 x + rx_bl,
388 y + h,
389 x + w - rx_br,
391 y + h,
392 x + w - rx_br * (1.0 - KAPPA90),
394 y + h,
395 x + w,
396 y + h - ry_br * (1.0 - KAPPA90),
397 x + w,
398 y + h - ry_br,
399 x + w,
401 y + ry_tr,
402 x + w,
404 y + ry_tr * (1.0 - KAPPA90),
405 x + w - rx_tr * (1.0 - KAPPA90),
406 y,
407 x + w - rx_tr,
408 y,
409 x + rx_tl,
411 y,
412 x + rx_tl * (1.0 - KAPPA90),
414 y,
415 x,
416 y + ry_tl * (1.0 - KAPPA90),
417 x,
418 y + ry_tl,
419 ],
420 );
421 }
422 }
423
424 pub fn ellipse(&mut self, cx: f32, cy: f32, rx: f32, ry: f32) {
426 self.append(
427 &[
428 PackedVerb::MoveTo,
429 PackedVerb::BezierTo,
430 PackedVerb::BezierTo,
431 PackedVerb::BezierTo,
432 PackedVerb::BezierTo,
433 PackedVerb::Close,
434 ],
435 &[
436 cx - rx,
437 cy,
438 cx - rx,
439 cy + ry * KAPPA90,
440 cx - rx * KAPPA90,
441 cy + ry,
442 cx,
443 cy + ry,
444 cx + rx * KAPPA90,
445 cy + ry,
446 cx + rx,
447 cy + ry * KAPPA90,
448 cx + rx,
449 cy,
450 cx + rx,
451 cy - ry * KAPPA90,
452 cx + rx * KAPPA90,
453 cy - ry,
454 cx,
455 cy - ry,
456 cx - rx * KAPPA90,
457 cy - ry,
458 cx - rx,
459 cy - ry * KAPPA90,
460 cx - rx,
461 cy,
462 ],
463 );
464 }
465
466 pub fn circle(&mut self, cx: f32, cy: f32, r: f32) {
468 self.ellipse(cx, cy, r, r);
469 }
470
471 fn append(&mut self, verbs: &[PackedVerb], coords: &[f32]) {
473 if coords.len() > 1 {
474 self.lastx = coords[coords.len() - 2];
475 self.lasty = coords[coords.len() - 1];
476 }
477
478 self.verbs.extend_from_slice(verbs);
479 self.coords.extend_from_slice(coords);
480 }
481}
482
483pub struct PathIter<'a> {
484 verbs: slice::Iter<'a, PackedVerb>,
485 coords: &'a [f32],
486}
487
488impl<'a> Iterator for PathIter<'a> {
489 type Item = Verb;
490
491 fn next(&mut self) -> Option<Self::Item> {
492 if let Some(verb) = self.verbs.next() {
493 let verb = Verb::from_packed(verb, self.coords);
494 let num_coords = verb.num_coordinates();
495 self.coords = &self.coords[num_coords..];
496 Some(verb)
497 } else {
498 None
499 }
500 }
501}
502
503impl ttf_parser::OutlineBuilder for Path {
504 fn move_to(&mut self, x: f32, y: f32) {
505 self.move_to(x, y);
506 }
507
508 fn line_to(&mut self, x: f32, y: f32) {
509 self.line_to(x, y);
510 }
511
512 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
513 self.quad_to(x1, y1, x, y);
514 }
515
516 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
517 self.bezier_to(x1, y1, x2, y2, x, y);
518 }
519
520 fn close(&mut self) {
521 self.close();
522 }
523}