1#![allow(clippy::excessive_precision)]
4
5use super::command::Command;
6use super::geometry::{Angle, BoundsBuilder, Point, Transform};
7#[allow(unused)]
8use super::F32Ext;
9
10use crate::lib::Vec;
11use core::f32;
12
13#[derive(Copy, Clone, PartialEq)]
15pub enum ArcSize {
16 Small,
18 Large,
20}
21
22#[derive(Copy, Clone, PartialEq)]
24pub enum ArcSweep {
25 Positive,
27 Negative,
29}
30
31pub trait PathBuilder: Sized {
33 fn current_point(&self) -> Point;
35
36 fn move_to(&mut self, to: impl Into<Point>) -> &mut Self;
38
39 fn rel_move_to(&mut self, to: impl Into<Point>) -> &mut Self {
42 self.move_to(to.into() + self.current_point())
43 }
44
45 fn line_to(&mut self, to: impl Into<Point>) -> &mut Self;
48
49 fn rel_line_to(&mut self, to: impl Into<Point>) -> &mut Self {
53 self.line_to(to.into() + self.current_point())
54 }
55
56 fn curve_to(
60 &mut self,
61 control1: impl Into<Point>,
62 control2: impl Into<Point>,
63 to: impl Into<Point>,
64 ) -> &mut Self;
65
66 fn rel_curve_to(
71 &mut self,
72 control1: impl Into<Point>,
73 control2: impl Into<Point>,
74 to: impl Into<Point>,
75 ) -> &mut Self {
76 let r = self.current_point();
77 self.curve_to(control1.into() + r, control2.into() + r, to.into() + r)
78 }
79
80 fn quad_to(&mut self, control1: impl Into<Point>, to: impl Into<Point>) -> &mut Self;
84
85 fn rel_quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
90 let r = self.current_point();
91 self.quad_to(control.into() + r, to.into() + r)
92 }
93
94 fn arc_to(
99 &mut self,
100 rx: f32,
101 ry: f32,
102 angle: Angle,
103 size: ArcSize,
104 sweep: ArcSweep,
105 to: impl Into<Point>,
106 ) -> &mut Self {
107 let from = self.current_point();
108 arc(
109 self,
110 from,
111 rx,
112 ry,
113 angle.to_radians(),
114 size,
115 sweep,
116 to.into(),
117 );
118 self
119 }
120
121 fn rel_arc_to(
127 &mut self,
128 rx: f32,
129 ry: f32,
130 angle: Angle,
131 size: ArcSize,
132 sweep: ArcSweep,
133 to: impl Into<Point>,
134 ) -> &mut Self {
135 self.arc_to(rx, ry, angle, size, sweep, to.into() + self.current_point())
136 }
137
138 fn close(&mut self) -> &mut Self;
140
141 fn add_rect(&mut self, xy: impl Into<Point>, w: f32, h: f32) -> &mut Self {
144 let p = xy.into();
145 let (l, t, r, b) = (p.x, p.y, p.x + w, p.y + h);
146 self.move_to(p);
147 self.line_to((r, t));
148 self.line_to((r, b));
149 self.line_to((l, b));
150 self.close()
151 }
152
153 fn add_round_rect(
156 &mut self,
157 xy: impl Into<Point>,
158 w: f32,
159 h: f32,
160 rx: f32,
161 ry: f32,
162 ) -> &mut Self {
163 let p = xy.into();
164 let size = ArcSize::Small;
165 let sweep = ArcSweep::Positive;
166 let a = Angle::from_radians(0.);
167 let hw = w * 0.5;
168 let rx = rx.max(0.).min(hw);
169 let hh = h * 0.5;
170 let ry = ry.max(0.).min(hh);
171 self.move_to((p.x + rx, p.y));
172 self.line_to((p.x + w - rx, p.y));
173 self.arc_to(rx, ry, a, size, sweep, (p.x + w, p.y + ry));
174 self.line_to((p.x + w, p.y + h - ry));
175 self.arc_to(rx, ry, a, size, sweep, (p.x + w - rx, p.y + h));
176 self.line_to((p.x + rx, p.y + h));
177 self.arc_to(rx, ry, a, size, sweep, (p.x, p.y + h - ry));
178 self.line_to((p.x, p.y + ry));
179 self.arc_to(rx, ry, a, size, sweep, (p.x + rx, p.y));
180 self.close()
181 }
182
183 fn add_ellipse(&mut self, center: impl Into<Point>, rx: f32, ry: f32) -> &mut Self {
186 let center = center.into();
187 let cx = center.x;
188 let cy = center.y;
189 let a = 0.551915024494;
190 let arx = a * rx;
191 let ary = a * ry;
192 self.move_to((cx + rx, cy));
193 self.curve_to((cx + rx, cy + ary), (cx + arx, cy + ry), (cx, cy + ry));
194 self.curve_to((cx - arx, cy + ry), (cx - rx, cy + ary), (cx - rx, cy));
195 self.curve_to((cx - rx, cy - ary), (cx - arx, cy - ry), (cx, cy - ry));
196 self.curve_to((cx + arx, cy - ry), (cx + rx, cy - ary), (cx + rx, cy));
197 self.close()
198 }
199
200 fn add_circle(&mut self, center: impl Into<Point>, r: f32) -> &mut Self {
203 self.add_ellipse(center, r, r)
204 }
205}
206
207impl PathBuilder for Vec<Command> {
208 fn current_point(&self) -> Point {
209 match self.last() {
210 None => Point::ZERO,
211 Some(cmd) => match cmd {
212 Command::MoveTo(p)
213 | Command::LineTo(p)
214 | Command::QuadTo(_, p)
215 | Command::CurveTo(_, _, p) => *p,
216 Command::Close => {
217 for cmd in self.iter().rev().skip(1) {
218 if let Command::MoveTo(p) = cmd {
219 return *p;
220 }
221 }
222 Point::ZERO
223 }
224 },
225 }
226 }
227
228 fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
229 self.push(Command::MoveTo(to.into()));
230 self
231 }
232
233 fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
234 self.push(Command::LineTo(to.into()));
235 self
236 }
237
238 fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
239 self.push(Command::QuadTo(control.into(), to.into()));
240 self
241 }
242
243 fn curve_to(
244 &mut self,
245 control1: impl Into<Point>,
246 control2: impl Into<Point>,
247 to: impl Into<Point>,
248 ) -> &mut Self {
249 self.push(Command::CurveTo(
250 control1.into(),
251 control2.into(),
252 to.into(),
253 ));
254 self
255 }
256
257 fn close(&mut self) -> &mut Self {
258 self.push(Command::Close);
259 self
260 }
261}
262
263pub struct TransformSink<'a, S> {
264 pub sink: &'a mut S,
265 pub transform: Transform,
266}
267
268impl<S: PathBuilder> PathBuilder for TransformSink<'_, S> {
269 fn current_point(&self) -> Point {
270 self.sink.current_point()
271 }
272
273 fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
274 self.sink.move_to(self.transform.transform_point(to.into()));
275 self
276 }
277
278 fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
279 self.sink.line_to(self.transform.transform_point(to.into()));
280 self
281 }
282
283 fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
284 self.sink.quad_to(
285 self.transform.transform_point(control.into()),
286 self.transform.transform_point(to.into()),
287 );
288 self
289 }
290
291 fn curve_to(
292 &mut self,
293 control1: impl Into<Point>,
294 control2: impl Into<Point>,
295 to: impl Into<Point>,
296 ) -> &mut Self {
297 self.sink.curve_to(
298 self.transform.transform_point(control1.into()),
299 self.transform.transform_point(control2.into()),
300 self.transform.transform_point(to.into()),
301 );
302 self
303 }
304
305 fn close(&mut self) -> &mut Self {
306 self.sink.close();
307 self
308 }
309}
310
311impl PathBuilder for BoundsBuilder {
312 fn current_point(&self) -> Point {
313 self.current
314 }
315
316 fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
317 let p = to.into();
318 self.add(p);
319 self.current = p;
320 self
321 }
322
323 fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
324 let p = to.into();
325 self.add(p);
326 self.current = p;
327 self
328 }
329
330 fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
331 self.add(control.into());
332 let p = to.into();
333 self.add(p);
334 self.current = p;
335 self
336 }
337
338 fn curve_to(
339 &mut self,
340 control1: impl Into<Point>,
341 control2: impl Into<Point>,
342 to: impl Into<Point>,
343 ) -> &mut Self {
344 self.add(control1.into());
345 self.add(control2.into());
346 let p = to.into();
347 self.add(p);
348 self.current = p;
349 self
350 }
351
352 fn close(&mut self) -> &mut Self {
353 self
354 }
355}
356
357#[derive(Copy, Clone, Default)]
359pub struct Arc {
360 count: usize,
361 center: (f32, f32),
362 radii: (f32, f32),
363 cosphi: f32,
364 sinphi: f32,
365 ang1: f32,
366 ang2: f32,
367 a: f32,
368}
369
370impl Arc {
371 pub fn new(
372 from: impl Into<[f32; 2]>,
373 rx: f32,
374 ry: f32,
375 angle: f32,
376 size: ArcSize,
377 sweep: ArcSweep,
378 to: impl Into<[f32; 2]>,
379 ) -> Self {
380 let from = from.into();
381 let to = to.into();
382 let (px, py) = (from[0], from[1]);
383 const TAU: f32 = 3.141579 * 2.;
384 let (sinphi, cosphi) = angle.sin_cos();
385 let pxp = cosphi * (px - to[0]) / 2. + sinphi * (py - to[1]) / 2.;
386 let pyp = -sinphi * (px - to[0]) / 2. + cosphi * (py - to[1]) / 2.;
387 if pxp == 0. && pyp == 0. {
388 return Self::default();
389 }
390 let mut rx = rx.abs();
391 let mut ry = ry.abs();
392 let lambda = pxp.powi(2) / rx.powi(2) + pyp.powi(2) / ry.powi(2);
393 if lambda > 1. {
394 let s = lambda.sqrt();
395 rx *= s;
396 ry *= s;
397 }
398 let large_arc = size == ArcSize::Large;
399 let sweep = sweep == ArcSweep::Positive;
400 let (cx, cy, ang1, mut ang2) = {
401 fn vec_angle(ux: f32, uy: f32, vx: f32, vy: f32) -> f32 {
402 let sign = if (ux * vy - uy * vx) < 0. { -1. } else { 1. };
403 let dot = (ux * vx + uy * vy).clamp(-1., 1.);
404 sign * dot.acos()
405 }
406 let rxsq = rx * rx;
407 let rysq = ry * ry;
408 let pxpsq = pxp * pxp;
409 let pypsq = pyp * pyp;
410 let mut radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq);
411 if radicant < 0. {
412 radicant = 0.;
413 }
414 radicant /= (rxsq * pypsq) + (rysq * pxpsq);
415 radicant = radicant.sqrt() * if large_arc == sweep { -1. } else { 1. };
416 let cxp = radicant * rx / ry * pyp;
417 let cyp = radicant * -ry / rx * pxp;
418 let cx = cosphi * cxp - sinphi * cyp + (px + to[0]) / 2.;
419 let cy = sinphi * cxp + cosphi * cyp + (py + to[1]) / 2.;
420 let vx1 = (pxp - cxp) / rx;
421 let vy1 = (pyp - cyp) / ry;
422 let vx2 = (-pxp - cxp) / rx;
423 let vy2 = (-pyp - cyp) / ry;
424 let ang1 = vec_angle(1., 0., vx1, vy1);
425 let mut ang2 = vec_angle(vx1, vy1, vx2, vy2);
426 if !sweep && ang2 > 0. {
427 ang2 -= TAU;
428 }
429 if sweep && ang2 < 0. {
430 ang2 += TAU;
431 }
432 (cx, cy, ang1, ang2)
433 };
434 let mut ratio = ang2.abs() / (TAU / 4.);
435 if (1. - ratio).abs() < 0.0000001 {
436 ratio = 1.
437 }
438 let segments = ratio.ceil().max(1.);
439 ang2 /= segments;
440 let a = if ang2 == f32::consts::FRAC_PI_2 {
441 0.551915024494
442 } else if ang2 == -f32::consts::FRAC_PI_2 {
443 -0.551915024494
444 } else {
445 4. / 3. * (ang2 / 4.).tan()
446 };
447 Self {
448 count: segments as usize,
449 center: (cx, cy),
450 radii: (rx, ry),
451 sinphi,
452 cosphi,
453 ang1,
454 ang2,
455 a,
456 }
457 }
458}
459
460impl Iterator for Arc {
461 type Item = Command;
462
463 fn next(&mut self) -> Option<Self::Item> {
464 if self.count == 0 {
465 return None;
466 }
467 self.count -= 1;
468 let (y1, x1) = self.ang1.sin_cos();
469 let (y2, x2) = (self.ang1 + self.ang2).sin_cos();
470 let a = self.a;
471 let (cx, cy) = self.center;
472 let (rx, ry) = self.radii;
473 let sinphi = self.sinphi;
474 let cosphi = self.cosphi;
475 let c1 = Point::new((x1 - y1 * a) * rx, (y1 + x1 * a) * ry);
476 let c1 = Point::new(
477 cx + (cosphi * c1.x - sinphi * c1.y),
478 cy + (sinphi * c1.x + cosphi * c1.y),
479 );
480 let c2 = Point::new((x2 + y2 * a) * rx, (y2 - x2 * a) * ry);
481 let c2 = Point::new(
482 cx + (cosphi * c2.x - sinphi * c2.y),
483 cy + (sinphi * c2.x + cosphi * c2.y),
484 );
485 let p = Point::new(x2 * rx, y2 * ry);
486 let p = Point::new(
487 cx + (cosphi * p.x - sinphi * p.y),
488 cy + (sinphi * p.x + cosphi * p.y),
489 );
490 self.ang1 += self.ang2;
491 Some(Command::CurveTo(c1, c2, p))
492 }
493}
494
495#[allow(clippy::too_many_arguments)]
496pub fn arc(
497 sink: &mut impl PathBuilder,
498 from: Point,
499 rx: f32,
500 ry: f32,
501 angle: f32,
502 size: ArcSize,
503 sweep: ArcSweep,
504 to: Point,
505) {
506 let p = from;
507 let (px, py) = (p.x, p.y);
508 const TAU: f32 = core::f32::consts::PI * 2.;
509 let (sinphi, cosphi) = angle.sin_cos();
510 let pxp = cosphi * (px - to.x) / 2. + sinphi * (py - to.y) / 2.;
511 let pyp = -sinphi * (px - to.x) / 2. + cosphi * (py - to.y) / 2.;
512 if pxp == 0. && pyp == 0. {
513 return;
514 }
515 let mut rx = rx.abs();
516 let mut ry = ry.abs();
517 let lambda = pxp.powi(2) / rx.powi(2) + pyp.powi(2) / ry.powi(2);
518 if lambda > 1. {
519 let s = lambda.sqrt();
520 rx *= s;
521 ry *= s;
522 }
523 let large_arc = size == ArcSize::Large;
524 let sweep = sweep == ArcSweep::Positive;
525 let (cx, cy, mut ang1, mut ang2) = {
526 fn vec_angle(ux: f32, uy: f32, vx: f32, vy: f32) -> f32 {
527 let sign = if (ux * vy - uy * vx) < 0. { -1. } else { 1. };
528 let dot = (ux * vx + uy * vy).clamp(-1., 1.);
529 sign * dot.acos()
530 }
531 let rxsq = rx * rx;
532 let rysq = ry * ry;
533 let pxpsq = pxp * pxp;
534 let pypsq = pyp * pyp;
535 let mut radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq);
536 if radicant < 0. {
537 radicant = 0.;
538 }
539 radicant /= (rxsq * pypsq) + (rysq * pxpsq);
540 radicant = radicant.sqrt() * if large_arc == sweep { -1. } else { 1. };
541 let cxp = radicant * rx / ry * pyp;
542 let cyp = radicant * -ry / rx * pxp;
543 let cx = cosphi * cxp - sinphi * cyp + (px + to.x) / 2.;
544 let cy = sinphi * cxp + cosphi * cyp + (py + to.y) / 2.;
545 let vx1 = (pxp - cxp) / rx;
546 let vy1 = (pyp - cyp) / ry;
547 let vx2 = (-pxp - cxp) / rx;
548 let vy2 = (-pyp - cyp) / ry;
549 let ang1 = vec_angle(1., 0., vx1, vy1);
550 let mut ang2 = vec_angle(vx1, vy1, vx2, vy2);
551 if !sweep && ang2 > 0. {
552 ang2 -= TAU;
553 }
554 if sweep && ang2 < 0. {
555 ang2 += TAU;
556 }
557 (cx, cy, ang1, ang2)
558 };
559 let mut ratio = ang2.abs() / (TAU / 4.);
560 if (1. - ratio).abs() < 0.0000001 {
561 ratio = 1.
562 }
563 let segments = ratio.ceil().max(1.);
564 ang2 /= segments;
565 let a = if ang2 == f32::consts::FRAC_PI_2 {
566 0.551915024494
567 } else if ang2 == -f32::consts::FRAC_PI_2 {
568 -0.551915024494
569 } else {
570 4. / 3. * (ang2 / 4.).tan()
571 };
572 for _ in 0..segments as usize {
573 let (y1, x1) = ang1.sin_cos();
574 let (y2, x2) = (ang1 + ang2).sin_cos();
575 let c1 = Point::new((x1 - y1 * a) * rx, (y1 + x1 * a) * ry);
576 let c1 = Point::new(
577 cx + (cosphi * c1.x - sinphi * c1.y),
578 cy + (sinphi * c1.x + cosphi * c1.y),
579 );
580 let c2 = Point::new((x2 + y2 * a) * rx, (y2 - x2 * a) * ry);
581 let c2 = Point::new(
582 cx + (cosphi * c2.x - sinphi * c2.y),
583 cy + (sinphi * c2.x + cosphi * c2.y),
584 );
585 let p = Point::new(x2 * rx, y2 * ry);
586 let p = Point::new(
587 cx + (cosphi * p.x - sinphi * p.y),
588 cy + (sinphi * p.x + cosphi * p.y),
589 );
590 sink.curve_to(c1, c2, p);
591 ang1 += ang2;
592 }
593}