1use core::mem::swap;
4use core::ops::Range;
5
6use num_traits::NumCast;
7
8use crate::scalar::{cast, Float, Scalar};
9use crate::segment::{BoundingBox, Segment};
10use crate::{point, vector, Angle, Box2D, Point, Rotation, Transform, Vector};
11use crate::{CubicBezierSegment, Line, LineSegment, QuadraticBezierSegment};
12
13#[derive(Copy, Clone, Debug, PartialEq)]
15#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
16pub struct Arc<S> {
17 pub center: Point<S>,
18 pub radii: Vector<S>,
19 pub start_angle: Angle<S>,
20 pub sweep_angle: Angle<S>,
21 pub x_rotation: Angle<S>,
22}
23
24#[derive(Copy, Clone, Debug, PartialEq)]
26#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
27pub struct SvgArc<S> {
28 pub from: Point<S>,
29 pub to: Point<S>,
30 pub radii: Vector<S>,
31 pub x_rotation: Angle<S>,
32 pub flags: ArcFlags,
33}
34
35impl<S: Scalar> Arc<S> {
36 pub fn cast<NewS: NumCast>(self) -> Arc<NewS> {
37 Arc {
38 center: self.center.cast(),
39 radii: self.radii.cast(),
40 start_angle: self.start_angle.cast(),
41 sweep_angle: self.sweep_angle.cast(),
42 x_rotation: self.x_rotation.cast(),
43 }
44 }
45
46 pub fn circle(center: Point<S>, radius: S) -> Self {
48 Arc {
49 center,
50 radii: vector(radius, radius),
51 start_angle: Angle::zero(),
52 sweep_angle: Angle::two_pi(),
53 x_rotation: Angle::zero(),
54 }
55 }
56
57 pub fn from_svg_arc(arc: &SvgArc<S>) -> Arc<S> {
59 debug_assert!(!arc.from.x.is_nan());
60 debug_assert!(!arc.from.y.is_nan());
61 debug_assert!(!arc.to.x.is_nan());
62 debug_assert!(!arc.to.y.is_nan());
63 debug_assert!(!arc.radii.x.is_nan());
64 debug_assert!(!arc.radii.y.is_nan());
65 debug_assert!(!arc.x_rotation.get().is_nan());
66 assert!(!arc.is_straight_line());
70
71 let mut rx = S::abs(arc.radii.x);
72 let mut ry = S::abs(arc.radii.y);
73
74 let xr = arc.x_rotation.get() % (S::TWO * S::PI());
75 let cos_phi = Float::cos(xr);
76 let sin_phi = Float::sin(xr);
77 let hd_x = (arc.from.x - arc.to.x) / S::TWO;
78 let hd_y = (arc.from.y - arc.to.y) / S::TWO;
79 let hs_x = (arc.from.x + arc.to.x) / S::TWO;
80 let hs_y = (arc.from.y + arc.to.y) / S::TWO;
81
82 let p = Point::new(
84 cos_phi * hd_x + sin_phi * hd_y,
85 -sin_phi * hd_x + cos_phi * hd_y,
86 );
87
88 let rf = p.x * p.x / (rx * rx) + p.y * p.y / (ry * ry);
95 if rf > S::ONE {
96 let scale = S::sqrt(rf);
97 rx *= scale;
98 ry *= scale;
99 }
100
101 let rxry = rx * ry;
102 let rxpy = rx * p.y;
103 let rypx = ry * p.x;
104 let sum_of_sq = rxpy * rxpy + rypx * rypx;
105
106 debug_assert_ne!(sum_of_sq, S::ZERO);
107
108 let sign_coe = if arc.flags.large_arc == arc.flags.sweep {
110 -S::ONE
111 } else {
112 S::ONE
113 };
114 let coe = sign_coe * S::sqrt(S::abs((rxry * rxry - sum_of_sq) / sum_of_sq));
115 let transformed_cx = coe * rxpy / ry;
116 let transformed_cy = -coe * rypx / rx;
117
118 let center = point(
120 cos_phi * transformed_cx - sin_phi * transformed_cy + hs_x,
121 sin_phi * transformed_cx + cos_phi * transformed_cy + hs_y,
122 );
123
124 let start_v: Vector<S> = vector((p.x - transformed_cx) / rx, (p.y - transformed_cy) / ry);
125 let end_v: Vector<S> = vector((-p.x - transformed_cx) / rx, (-p.y - transformed_cy) / ry);
126
127 let two_pi = S::TWO * S::PI();
128
129 let start_angle = start_v.angle_from_x_axis();
130
131 let mut sweep_angle = (end_v.angle_from_x_axis() - start_angle).radians % two_pi;
132
133 if arc.flags.sweep && sweep_angle < S::ZERO {
134 sweep_angle += two_pi;
135 } else if !arc.flags.sweep && sweep_angle > S::ZERO {
136 sweep_angle -= two_pi;
137 }
138
139 Arc {
140 center,
141 radii: vector(rx, ry),
142 start_angle,
143 sweep_angle: Angle::radians(sweep_angle),
144 x_rotation: arc.x_rotation,
145 }
146 }
147
148 pub fn to_svg_arc(&self) -> SvgArc<S> {
150 let from = self.sample(S::ZERO);
151 let to = self.sample(S::ONE);
152 let flags = ArcFlags {
153 sweep: self.sweep_angle.get() >= S::ZERO,
154 large_arc: S::abs(self.sweep_angle.get()) >= S::PI(),
155 };
156 SvgArc {
157 from,
158 to,
159 radii: self.radii,
160 x_rotation: self.x_rotation,
161 flags,
162 }
163 }
164
165 #[inline]
167 pub fn for_each_quadratic_bezier<F>(&self, cb: &mut F)
168 where
169 F: FnMut(&QuadraticBezierSegment<S>),
170 {
171 arc_to_quadratic_beziers_with_t(self, &mut |curve, _| cb(curve));
172 }
173
174 #[inline]
176 pub fn for_each_quadratic_bezier_with_t<F>(&self, cb: &mut F)
177 where
178 F: FnMut(&QuadraticBezierSegment<S>, Range<S>),
179 {
180 arc_to_quadratic_beziers_with_t(self, cb);
181 }
182
183 #[inline]
185 pub fn for_each_cubic_bezier<F>(&self, cb: &mut F)
186 where
187 F: FnMut(&CubicBezierSegment<S>),
188 {
189 arc_to_cubic_beziers(self, cb);
190 }
191
192 #[inline]
194 pub fn sample(&self, t: S) -> Point<S> {
195 let angle = self.get_angle(t);
196 self.center + sample_ellipse(self.radii, self.x_rotation, angle).to_vector()
197 }
198
199 #[inline]
200 pub fn x(&self, t: S) -> S {
201 self.sample(t).x
202 }
203
204 #[inline]
205 pub fn y(&self, t: S) -> S {
206 self.sample(t).y
207 }
208
209 #[inline]
211 pub fn sample_tangent(&self, t: S) -> Vector<S> {
212 self.tangent_at_angle(self.get_angle(t))
213 }
214
215 #[inline]
217 pub fn get_angle(&self, t: S) -> Angle<S> {
218 self.start_angle + Angle::radians(self.sweep_angle.get() * t)
219 }
220
221 #[inline]
222 pub fn end_angle(&self) -> Angle<S> {
223 self.start_angle + self.sweep_angle
224 }
225
226 #[inline]
227 pub fn from(&self) -> Point<S> {
228 self.sample(S::ZERO)
229 }
230
231 #[inline]
232 pub fn to(&self) -> Point<S> {
233 self.sample(S::ONE)
234 }
235
236 pub fn split_range(&self, t_range: Range<S>) -> Self {
240 let angle_1 = Angle::radians(self.sweep_angle.get() * t_range.start);
241 let angle_2 = Angle::radians(self.sweep_angle.get() * t_range.end);
242
243 Arc {
244 center: self.center,
245 radii: self.radii,
246 start_angle: self.start_angle + angle_1,
247 sweep_angle: angle_2 - angle_1,
248 x_rotation: self.x_rotation,
249 }
250 }
251
252 pub fn split(&self, t: S) -> (Arc<S>, Arc<S>) {
254 let split_angle = Angle::radians(self.sweep_angle.get() * t);
255 (
256 Arc {
257 center: self.center,
258 radii: self.radii,
259 start_angle: self.start_angle,
260 sweep_angle: split_angle,
261 x_rotation: self.x_rotation,
262 },
263 Arc {
264 center: self.center,
265 radii: self.radii,
266 start_angle: self.start_angle + split_angle,
267 sweep_angle: self.sweep_angle - split_angle,
268 x_rotation: self.x_rotation,
269 },
270 )
271 }
272
273 pub fn before_split(&self, t: S) -> Arc<S> {
275 let split_angle = Angle::radians(self.sweep_angle.get() * t);
276 Arc {
277 center: self.center,
278 radii: self.radii,
279 start_angle: self.start_angle,
280 sweep_angle: split_angle,
281 x_rotation: self.x_rotation,
282 }
283 }
284
285 pub fn after_split(&self, t: S) -> Arc<S> {
287 let split_angle = Angle::radians(self.sweep_angle.get() * t);
288 Arc {
289 center: self.center,
290 radii: self.radii,
291 start_angle: self.start_angle + split_angle,
292 sweep_angle: self.sweep_angle - split_angle,
293 x_rotation: self.x_rotation,
294 }
295 }
296
297 pub fn flip(&self) -> Self {
299 let mut arc = *self;
300 arc.start_angle += self.sweep_angle;
301 arc.sweep_angle = -self.sweep_angle;
302
303 arc
304 }
305
306 pub fn for_each_flattened<F>(&self, tolerance: S, callback: &mut F)
311 where
312 F: FnMut(&LineSegment<S>),
313 {
314 let mut from = self.from();
315 let mut iter = *self;
316 loop {
317 let t = iter.flattening_step(tolerance);
318 if t >= S::ONE {
319 break;
320 }
321 iter = iter.after_split(t);
322 let to = iter.from();
323 callback(&LineSegment { from, to });
324 from = to;
325 }
326
327 callback(&LineSegment {
328 from,
329 to: self.to(),
330 });
331 }
332
333 pub fn for_each_flattened_with_t<F>(&self, tolerance: S, callback: &mut F)
340 where
341 F: FnMut(&LineSegment<S>, Range<S>),
342 {
343 let mut iter = *self;
344 let mut t0 = S::ZERO;
345 let mut from = self.from();
346 loop {
347 let step = iter.flattening_step(tolerance);
348
349 if step >= S::ONE {
350 break;
351 }
352
353 iter = iter.after_split(step);
354 let t1 = t0 + step * (S::ONE - t0);
355 let to = iter.from();
356 callback(&LineSegment { from, to }, t0..t1);
357 from = to;
358 t0 = t1;
359 }
360
361 callback(
362 &LineSegment {
363 from,
364 to: self.to(),
365 },
366 t0..S::ONE,
367 );
368 }
369
370 fn flattening_step(&self, tolerance: S) -> S {
373 let r = (self.from() - self.center).length();
380 let a = S::TWO * S::acos((r - tolerance) / r);
381 let result = S::min(a / self.sweep_angle.radians.abs(), S::ONE);
382
383 if result < S::EPSILON {
384 return S::ONE;
385 }
386
387 result
388 }
389
390 pub fn flattened(&self, tolerance: S) -> Flattened<S> {
393 Flattened::new(*self, tolerance)
394 }
395
396 pub fn fast_bounding_box(&self) -> Box2D<S> {
398 Transform::rotation(self.x_rotation).outer_transformed_box(&Box2D {
399 min: self.center - self.radii,
400 max: self.center + self.radii,
401 })
402 }
403
404 pub fn bounding_box(&self) -> Box2D<S> {
406 let from = self.from();
407 let to = self.to();
408 let mut min = Point::min(from, to);
409 let mut max = Point::max(from, to);
410 self.for_each_local_x_extremum_t(&mut |t| {
411 let p = self.sample(t);
412 min.x = S::min(min.x, p.x);
413 max.x = S::max(max.x, p.x);
414 });
415 self.for_each_local_y_extremum_t(&mut |t| {
416 let p = self.sample(t);
417 min.y = S::min(min.y, p.y);
418 max.y = S::max(max.y, p.y);
419 });
420
421 Box2D { min, max }
422 }
423
424 pub fn for_each_local_x_extremum_t<F>(&self, cb: &mut F)
425 where
426 F: FnMut(S),
427 {
428 let rx = self.radii.x;
429 let ry = self.radii.y;
430 let a1 = Angle::radians(-S::atan(ry * Float::tan(self.x_rotation.radians) / rx));
431 let a2 = Angle::pi() + a1;
432
433 self.for_each_extremum_inner(a1, a2, cb);
434 }
435
436 pub fn for_each_local_y_extremum_t<F>(&self, cb: &mut F)
437 where
438 F: FnMut(S),
439 {
440 let rx = self.radii.x;
441 let ry = self.radii.y;
442 let a1 = Angle::radians(S::atan(ry / (Float::tan(self.x_rotation.radians) * rx)));
443 let a2 = Angle::pi() + a1;
444
445 self.for_each_extremum_inner(a1, a2, cb);
446 }
447
448 fn for_each_extremum_inner<F>(&self, a1: Angle<S>, a2: Angle<S>, cb: &mut F)
449 where
450 F: FnMut(S),
451 {
452 let sweep = self.sweep_angle.radians;
453 let abs_sweep = S::abs(sweep);
454 let sign = S::signum(sweep);
455
456 let mut a1 = (a1 - self.start_angle).positive().radians;
457 let mut a2 = (a2 - self.start_angle).positive().radians;
458 if a1 * sign > a2 * sign {
459 swap(&mut a1, &mut a2);
460 }
461
462 let two_pi = S::TWO * S::PI();
463 if sweep >= S::ZERO {
464 if a1 < abs_sweep {
465 cb(a1 / abs_sweep);
466 }
467 if a2 < abs_sweep {
468 cb(a2 / abs_sweep);
469 }
470 } else {
471 if a1 > two_pi - abs_sweep {
472 cb(a1 / abs_sweep);
473 }
474 if a2 > two_pi - abs_sweep {
475 cb(a2 / abs_sweep);
476 }
477 }
478 }
479
480 pub fn bounding_range_x(&self) -> (S, S) {
481 let r = self.bounding_box();
482 (r.min.x, r.max.x)
483 }
484
485 pub fn bounding_range_y(&self) -> (S, S) {
486 let r = self.bounding_box();
487 (r.min.y, r.max.y)
488 }
489
490 pub fn fast_bounding_range_x(&self) -> (S, S) {
491 let r = self.fast_bounding_box();
492 (r.min.x, r.max.x)
493 }
494
495 pub fn fast_bounding_range_y(&self) -> (S, S) {
496 let r = self.fast_bounding_box();
497 (r.min.y, r.max.y)
498 }
499
500 pub fn approximate_length(&self, tolerance: S) -> S {
501 let mut len = S::ZERO;
502 self.for_each_flattened(tolerance, &mut |segment| {
503 len += segment.length();
504 });
505
506 len
507 }
508
509 #[inline]
510 fn tangent_at_angle(&self, angle: Angle<S>) -> Vector<S> {
511 let a = angle.get();
512 Rotation::new(self.x_rotation).transform_vector(vector(
513 -self.radii.x * Float::sin(a),
514 self.radii.y * Float::cos(a),
515 ))
516 }
517}
518
519impl<S: Scalar> From<SvgArc<S>> for Arc<S> {
520 fn from(svg: SvgArc<S>) -> Self {
521 svg.to_arc()
522 }
523}
524
525impl<S: Scalar> SvgArc<S> {
526 pub fn to_arc(&self) -> Arc<S> {
528 Arc::from_svg_arc(self)
529 }
530
531 pub fn is_straight_line(&self) -> bool {
535 S::abs(self.radii.x) <= S::EPSILON
536 || S::abs(self.radii.y) <= S::EPSILON
537 || self.from == self.to
538 }
539
540 pub fn for_each_quadratic_bezier<F>(&self, cb: &mut F)
542 where
543 F: FnMut(&QuadraticBezierSegment<S>),
544 {
545 if self.is_straight_line() {
546 cb(&QuadraticBezierSegment {
547 from: self.from,
548 ctrl: self.from,
549 to: self.to,
550 });
551 return;
552 }
553
554 Arc::from_svg_arc(self).for_each_quadratic_bezier(cb);
555 }
556
557 pub fn for_each_quadratic_bezier_with_t<F>(&self, cb: &mut F)
559 where
560 F: FnMut(&QuadraticBezierSegment<S>, Range<S>),
561 {
562 if self.is_straight_line() {
563 cb(
564 &QuadraticBezierSegment {
565 from: self.from,
566 ctrl: self.from,
567 to: self.to,
568 },
569 S::ZERO..S::ONE,
570 );
571 return;
572 }
573
574 Arc::from_svg_arc(self).for_each_quadratic_bezier_with_t(cb);
575 }
576
577 pub fn for_each_cubic_bezier<F>(&self, cb: &mut F)
579 where
580 F: FnMut(&CubicBezierSegment<S>),
581 {
582 if self.is_straight_line() {
583 cb(&CubicBezierSegment {
584 from: self.from,
585 ctrl1: self.from,
586 ctrl2: self.to,
587 to: self.to,
588 });
589 return;
590 }
591
592 Arc::from_svg_arc(self).for_each_cubic_bezier(cb);
593 }
594
595 pub fn for_each_flattened<F: FnMut(&LineSegment<S>)>(&self, tolerance: S, cb: &mut F) {
600 if self.is_straight_line() {
601 cb(&LineSegment {
602 from: self.from,
603 to: self.to,
604 });
605 return;
606 }
607
608 Arc::from_svg_arc(self).for_each_flattened(tolerance, cb);
609 }
610
611 pub fn for_each_flattened_with_t<F: FnMut(&LineSegment<S>, Range<S>)>(
618 &self,
619 tolerance: S,
620 cb: &mut F,
621 ) {
622 if self.is_straight_line() {
623 cb(
624 &LineSegment {
625 from: self.from,
626 to: self.to,
627 },
628 S::ZERO..S::ONE,
629 );
630 return;
631 }
632
633 Arc::from_svg_arc(self).for_each_flattened_with_t(tolerance, cb);
634 }
635}
636
637#[derive(Copy, Clone, Debug, PartialEq, Default)]
646#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
647pub struct ArcFlags {
648 pub large_arc: bool,
654 pub sweep: bool,
662}
663
664fn arc_to_quadratic_beziers_with_t<S, F>(arc: &Arc<S>, callback: &mut F)
665where
666 S: Scalar,
667 F: FnMut(&QuadraticBezierSegment<S>, Range<S>),
668{
669 let sign = arc.sweep_angle.get().signum();
670 let sweep_angle = S::abs(arc.sweep_angle.get()).min(S::PI() * S::TWO);
671
672 let n_steps = S::ceil(sweep_angle / S::FRAC_PI_4());
673 let step = Angle::radians(sweep_angle / n_steps * sign);
674
675 let mut t0 = S::ZERO;
676 let dt = S::ONE / n_steps;
677
678 let n = cast::<S, i32>(n_steps).unwrap();
679 for i in 0..n {
680 let a1 = arc.start_angle + step * cast(i).unwrap();
681 let a2 = arc.start_angle + step * cast(i + 1).unwrap();
682
683 let v1 = sample_ellipse(arc.radii, arc.x_rotation, a1).to_vector();
684 let v2 = sample_ellipse(arc.radii, arc.x_rotation, a2).to_vector();
685 let from = arc.center + v1;
686 let to = arc.center + v2;
687 let l1 = Line {
688 point: from,
689 vector: arc.tangent_at_angle(a1),
690 };
691 let l2 = Line {
692 point: to,
693 vector: arc.tangent_at_angle(a2),
694 };
695 let ctrl = l2.intersection(&l1).unwrap_or(from);
696
697 let t1 = if i + 1 == n { S::ONE } else { t0 + dt };
698
699 callback(&QuadraticBezierSegment { from, ctrl, to }, t0..t1);
700 t0 = t1;
701 }
702}
703
704fn arc_to_cubic_beziers<S, F>(arc: &Arc<S>, callback: &mut F)
705where
706 S: Scalar,
707 F: FnMut(&CubicBezierSegment<S>),
708{
709 let sign = arc.sweep_angle.get().signum();
710 let sweep_angle = S::abs(arc.sweep_angle.get()).min(S::PI() * S::TWO);
711
712 let n_steps = S::ceil(sweep_angle / S::FRAC_PI_2());
713 let step = Angle::radians(sweep_angle / n_steps * sign);
714
715 for i in 0..cast::<S, i32>(n_steps).unwrap() {
716 let a1 = arc.start_angle + step * cast(i).unwrap();
717 let a2 = arc.start_angle + step * cast(i + 1).unwrap();
718
719 let v1 = sample_ellipse(arc.radii, arc.x_rotation, a1).to_vector();
720 let v2 = sample_ellipse(arc.radii, arc.x_rotation, a2).to_vector();
721 let from = arc.center + v1;
722 let to = arc.center + v2;
723
724 let delta_a = a2 - a1;
728 let tan_da = Float::tan(delta_a.get() * S::HALF);
729 let alpha_sqrt = S::sqrt(S::FOUR + S::THREE * tan_da * tan_da);
730 let alpha = Float::sin(delta_a.get()) * (alpha_sqrt - S::ONE) / S::THREE;
731 let ctrl1 = from + arc.tangent_at_angle(a1) * alpha;
732 let ctrl2 = to - arc.tangent_at_angle(a2) * alpha;
733
734 callback(&CubicBezierSegment {
735 from,
736 ctrl1,
737 ctrl2,
738 to,
739 });
740 }
741}
742
743fn sample_ellipse<S: Scalar>(radii: Vector<S>, x_rotation: Angle<S>, angle: Angle<S>) -> Point<S> {
744 Rotation::new(x_rotation).transform_point(point(
745 radii.x * Float::cos(angle.get()),
746 radii.y * Float::sin(angle.get()),
747 ))
748}
749
750impl<S: Scalar> Segment for Arc<S> {
751 type Scalar = S;
752 fn from(&self) -> Point<S> {
753 self.from()
754 }
755 fn to(&self) -> Point<S> {
756 self.to()
757 }
758 fn sample(&self, t: S) -> Point<S> {
759 self.sample(t)
760 }
761 fn x(&self, t: S) -> S {
762 self.x(t)
763 }
764 fn y(&self, t: S) -> S {
765 self.y(t)
766 }
767 fn derivative(&self, t: S) -> Vector<S> {
768 self.sample_tangent(t)
769 }
770 fn split(&self, t: S) -> (Self, Self) {
771 self.split(t)
772 }
773 fn before_split(&self, t: S) -> Self {
774 self.before_split(t)
775 }
776 fn after_split(&self, t: S) -> Self {
777 self.after_split(t)
778 }
779 fn split_range(&self, t_range: Range<S>) -> Self {
780 self.split_range(t_range)
781 }
782 fn flip(&self) -> Self {
783 self.flip()
784 }
785 fn approximate_length(&self, tolerance: S) -> S {
786 self.approximate_length(tolerance)
787 }
788
789 fn for_each_flattened_with_t(
790 &self,
791 tolerance: Self::Scalar,
792 callback: &mut dyn FnMut(&LineSegment<S>, Range<S>),
793 ) {
794 self.for_each_flattened_with_t(tolerance, &mut |s, t| callback(s, t));
795 }
796}
797
798impl<S: Scalar> BoundingBox for Arc<S> {
799 type Scalar = S;
800 fn bounding_range_x(&self) -> (S, S) {
801 self.bounding_range_x()
802 }
803 fn bounding_range_y(&self) -> (S, S) {
804 self.bounding_range_y()
805 }
806 fn fast_bounding_range_x(&self) -> (S, S) {
807 self.fast_bounding_range_x()
808 }
809 fn fast_bounding_range_y(&self) -> (S, S) {
810 self.fast_bounding_range_y()
811 }
812}
813
814pub struct Flattened<S> {
819 arc: Arc<S>,
820 tolerance: S,
821 done: bool,
822}
823
824impl<S: Scalar> Flattened<S> {
825 pub(crate) fn new(arc: Arc<S>, tolerance: S) -> Self {
826 assert!(tolerance > S::ZERO);
827 Flattened {
828 arc,
829 tolerance,
830 done: false,
831 }
832 }
833}
834impl<S: Scalar> Iterator for Flattened<S> {
835 type Item = Point<S>;
836 fn next(&mut self) -> Option<Point<S>> {
837 if self.done {
838 return None;
839 }
840
841 let t = self.arc.flattening_step(self.tolerance);
842 if t >= S::ONE {
843 self.done = true;
844 return Some(self.arc.to());
845 }
846 self.arc = self.arc.after_split(t);
847
848 Some(self.arc.from())
849 }
850}
851
852#[test]
853fn test_from_svg_arc() {
854 use crate::vector;
855 use euclid::approxeq::ApproxEq;
856
857 let flags = ArcFlags {
858 large_arc: false,
859 sweep: false,
860 };
861
862 test_endpoints(&SvgArc {
863 from: point(0.0, -10.0),
864 to: point(10.0, 0.0),
865 radii: vector(10.0, 10.0),
866 x_rotation: Angle::radians(0.0),
867 flags,
868 });
869
870 test_endpoints(&SvgArc {
871 from: point(0.0, -10.0),
872 to: point(10.0, 0.0),
873 radii: vector(100.0, 10.0),
874 x_rotation: Angle::radians(0.0),
875 flags,
876 });
877
878 test_endpoints(&SvgArc {
879 from: point(0.0, -10.0),
880 to: point(10.0, 0.0),
881 radii: vector(10.0, 30.0),
882 x_rotation: Angle::radians(1.0),
883 flags,
884 });
885
886 test_endpoints(&SvgArc {
887 from: point(5.0, -10.0),
888 to: point(5.0, 5.0),
889 radii: vector(10.0, 30.0),
890 x_rotation: Angle::radians(-2.0),
891 flags,
892 });
893
894 test_endpoints(&SvgArc {
897 from: point(0.0, 0.0),
898 to: point(80.0, 60.0),
899 radii: vector(40.0, 40.0),
900 x_rotation: Angle::radians(0.0),
901 flags,
902 });
903
904 fn test_endpoints(svg_arc: &SvgArc<f64>) {
905 do_test_endpoints(&SvgArc {
906 flags: ArcFlags {
907 large_arc: false,
908 sweep: false,
909 },
910 ..svg_arc.clone()
911 });
912
913 do_test_endpoints(&SvgArc {
914 flags: ArcFlags {
915 large_arc: true,
916 sweep: false,
917 },
918 ..svg_arc.clone()
919 });
920
921 do_test_endpoints(&SvgArc {
922 flags: ArcFlags {
923 large_arc: false,
924 sweep: true,
925 },
926 ..svg_arc.clone()
927 });
928
929 do_test_endpoints(&SvgArc {
930 flags: ArcFlags {
931 large_arc: true,
932 sweep: true,
933 },
934 ..svg_arc.clone()
935 });
936 }
937
938 fn do_test_endpoints(svg_arc: &SvgArc<f64>) {
939 let eps = point(0.01, 0.01);
940 let arc = svg_arc.to_arc();
941 assert!(
942 arc.from().approx_eq_eps(&svg_arc.from, &eps),
943 "unexpected arc.from: {:?} == {:?}, flags: {:?}",
944 arc.from(),
945 svg_arc.from,
946 svg_arc.flags,
947 );
948 assert!(
949 arc.to().approx_eq_eps(&svg_arc.to, &eps),
950 "unexpected arc.from: {:?} == {:?}, flags: {:?}",
951 arc.to(),
952 svg_arc.to,
953 svg_arc.flags,
954 );
955 }
956}
957
958#[test]
959fn test_to_quadratics_and_cubics() {
960 use euclid::approxeq::ApproxEq;
961
962 fn do_test(arc: &Arc<f32>, expected_quadratic_count: u32, expected_cubic_count: u32) {
963 let last = arc.to();
964 {
965 let mut prev = arc.from();
966 let mut count = 0;
967 arc.for_each_quadratic_bezier(&mut |c| {
968 assert!(c.from.approx_eq(&prev));
969 prev = c.to;
970 count += 1;
971 });
972 assert!(prev.approx_eq(&last));
973 assert_eq!(count, expected_quadratic_count);
974 }
975 {
976 let mut prev = arc.from();
977 let mut count = 0;
978 arc.for_each_cubic_bezier(&mut |c| {
979 assert!(c.from.approx_eq(&prev));
980 prev = c.to;
981 count += 1;
982 });
983 assert!(prev.approx_eq(&last));
984 assert_eq!(count, expected_cubic_count);
985 }
986 }
987
988 do_test(
989 &Arc {
990 center: point(2.0, 3.0),
991 radii: vector(10.0, 3.0),
992 start_angle: Angle::radians(0.1),
993 sweep_angle: Angle::radians(3.0),
994 x_rotation: Angle::radians(0.5),
995 },
996 4,
997 2,
998 );
999
1000 do_test(
1001 &Arc {
1002 center: point(4.0, 5.0),
1003 radii: vector(3.0, 5.0),
1004 start_angle: Angle::radians(2.0),
1005 sweep_angle: Angle::radians(-3.0),
1006 x_rotation: Angle::radians(1.3),
1007 },
1008 4,
1009 2,
1010 );
1011
1012 do_test(
1013 &Arc {
1014 center: point(0.0, 0.0),
1015 radii: vector(100.0, 0.01),
1016 start_angle: Angle::radians(-1.0),
1017 sweep_angle: Angle::radians(0.1),
1018 x_rotation: Angle::radians(0.3),
1019 },
1020 1,
1021 1,
1022 );
1023
1024 do_test(
1025 &Arc {
1026 center: point(0.0, 0.0),
1027 radii: vector(1.0, 1.0),
1028 start_angle: Angle::radians(3.0),
1029 sweep_angle: Angle::radians(-0.1),
1030 x_rotation: Angle::radians(-0.3),
1031 },
1032 1,
1033 1,
1034 );
1035}
1036
1037#[test]
1038fn test_bounding_box() {
1039 use euclid::approxeq::ApproxEq;
1040
1041 fn approx_eq(r1: Box2D<f32>, r2: Box2D<f32>) -> bool {
1042 if !r1.min.x.approx_eq(&r2.min.x)
1043 || !r1.max.x.approx_eq(&r2.max.x)
1044 || !r1.min.y.approx_eq(&r2.min.y)
1045 || !r1.max.y.approx_eq(&r2.max.y)
1046 {
1047 std::println!("\n left: {r1:?}\n right: {r2:?}");
1048 return false;
1049 }
1050
1051 true
1052 }
1053
1054 let r = Arc {
1055 center: point(0.0, 0.0),
1056 radii: vector(1.0, 1.0),
1057 start_angle: Angle::radians(0.0),
1058 sweep_angle: Angle::pi(),
1059 x_rotation: Angle::zero(),
1060 }
1061 .bounding_box();
1062 assert!(approx_eq(
1063 r,
1064 Box2D {
1065 min: point(-1.0, 0.0),
1066 max: point(1.0, 1.0)
1067 }
1068 ));
1069
1070 let r = Arc {
1071 center: point(0.0, 0.0),
1072 radii: vector(1.0, 1.0),
1073 start_angle: Angle::radians(0.0),
1074 sweep_angle: Angle::pi(),
1075 x_rotation: Angle::pi(),
1076 }
1077 .bounding_box();
1078 assert!(approx_eq(
1079 r,
1080 Box2D {
1081 min: point(-1.0, -1.0),
1082 max: point(1.0, 0.0)
1083 }
1084 ));
1085
1086 let r = Arc {
1087 center: point(0.0, 0.0),
1088 radii: vector(2.0, 1.0),
1089 start_angle: Angle::radians(0.0),
1090 sweep_angle: Angle::pi(),
1091 x_rotation: Angle::pi() * 0.5,
1092 }
1093 .bounding_box();
1094 assert!(approx_eq(
1095 r,
1096 Box2D {
1097 min: point(-1.0, -2.0),
1098 max: point(0.0, 2.0)
1099 }
1100 ));
1101
1102 let r = Arc {
1103 center: point(1.0, 1.0),
1104 radii: vector(1.0, 1.0),
1105 start_angle: Angle::pi(),
1106 sweep_angle: Angle::pi(),
1107 x_rotation: -Angle::pi() * 0.25,
1108 }
1109 .bounding_box();
1110 assert!(approx_eq(
1111 r,
1112 Box2D {
1113 min: point(0.0, 0.0),
1114 max: point(1.707107, 1.707107)
1115 }
1116 ));
1117
1118 let mut angle = Angle::zero();
1119 for _ in 0..10 {
1120 std::println!("angle: {angle:?}");
1121 let r = Arc {
1122 center: point(0.0, 0.0),
1123 radii: vector(4.0, 4.0),
1124 start_angle: angle,
1125 sweep_angle: Angle::pi() * 2.0,
1126 x_rotation: Angle::pi() * 0.25,
1127 }
1128 .bounding_box();
1129 assert!(approx_eq(
1130 r,
1131 Box2D {
1132 min: point(-4.0, -4.0),
1133 max: point(4.0, 4.0)
1134 }
1135 ));
1136 angle += Angle::pi() * 2.0 / 10.0;
1137 }
1138
1139 let mut angle = Angle::zero();
1140 for _ in 0..10 {
1141 std::println!("angle: {angle:?}");
1142 let r = Arc {
1143 center: point(0.0, 0.0),
1144 radii: vector(4.0, 4.0),
1145 start_angle: Angle::zero(),
1146 sweep_angle: Angle::pi() * 2.0,
1147 x_rotation: angle,
1148 }
1149 .bounding_box();
1150 assert!(approx_eq(
1151 r,
1152 Box2D {
1153 min: point(-4.0, -4.0),
1154 max: point(4.0, 4.0)
1155 }
1156 ));
1157 angle += Angle::pi() * 2.0 / 10.0;
1158 }
1159}
1160
1161#[test]
1162fn negative_flattening_step() {
1163 let arc = Arc {
1167 center: point(-100.0, -150.0),
1168 radii: vector(50.0, 50.0),
1169 start_angle: Angle::radians(0.982944787),
1170 sweep_angle: Angle::radians(-898.0),
1171 x_rotation: Angle::zero(),
1172 };
1173
1174 arc.for_each_flattened(0.100000001, &mut |_| {});
1175
1176 let arc = Arc {
1180 center: point(0.0, 0.0),
1181 radii: vector(100.0, 10.0),
1182 start_angle: Angle::radians(0.2),
1183 sweep_angle: Angle::radians(-2.0),
1184 x_rotation: Angle::zero(),
1185 };
1186
1187 let flattened: std::vec::Vec<_> = arc.flattened(0.1).collect();
1188
1189 assert!(flattened.len() > 1);
1190}