1#![allow(clippy::many_single_char_names)]
7
8use core::iter::{Extend, FromIterator};
9use core::mem;
10use core::ops::{Mul, Range};
11
12use alloc::vec::Vec;
13
14use arrayvec::ArrayVec;
15
16use crate::common::{solve_cubic, solve_quadratic};
17use crate::MAX_EXTREMA;
18use crate::{
19 Affine, CubicBez, Line, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea,
20 ParamCurveExtrema, ParamCurveNearest, Point, QuadBez, Rect, Shape, TranslateScale, Vec2,
21};
22
23#[cfg(not(feature = "std"))]
24use crate::common::FloatFuncs;
25
26#[derive(Clone, Default, Debug, PartialEq)]
104#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct BezPath(Vec<PathEl>);
107
108#[derive(Clone, Copy, Debug, PartialEq)]
112#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub enum PathEl {
115 MoveTo(Point),
118 LineTo(Point),
120 QuadTo(Point, Point),
122 CurveTo(Point, Point, Point),
124 ClosePath,
126}
127
128#[derive(Clone, Copy, Debug, PartialEq)]
130#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132pub enum PathSeg {
133 Line(Line),
135 Quad(QuadBez),
137 Cubic(CubicBez),
139}
140
141#[derive(Debug, Clone, Copy)]
145pub struct LineIntersection {
146 pub line_t: f64,
150
151 pub segment_t: f64,
156}
157
158pub struct MinDistance {
160 pub distance: f64,
162 pub t1: f64,
168 pub t2: f64,
174}
175
176impl BezPath {
177 #[inline(always)]
179 pub fn new() -> BezPath {
180 BezPath::default()
181 }
182
183 pub fn with_capacity(capacity: usize) -> BezPath {
188 BezPath(Vec::with_capacity(capacity))
189 }
190
191 pub fn from_vec(v: Vec<PathEl>) -> BezPath {
204 debug_assert!(
205 v.is_empty() || matches!(v.first(), Some(PathEl::MoveTo(_))),
206 "BezPath must begin with MoveTo"
207 );
208 BezPath(v)
209 }
210
211 pub fn pop(&mut self) -> Option<PathEl> {
213 self.0.pop()
214 }
215
216 pub fn push(&mut self, el: PathEl) {
218 self.0.push(el);
219 debug_assert!(
220 matches!(self.0.first(), Some(PathEl::MoveTo(_))),
221 "BezPath must begin with MoveTo"
222 );
223 }
224
225 pub fn move_to<P: Into<Point>>(&mut self, p: P) {
227 self.push(PathEl::MoveTo(p.into()));
228 }
229
230 pub fn line_to<P: Into<Point>>(&mut self, p: P) {
238 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
239 self.push(PathEl::LineTo(p.into()));
240 }
241
242 pub fn quad_to<P: Into<Point>>(&mut self, p1: P, p2: P) {
250 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
251 self.push(PathEl::QuadTo(p1.into(), p2.into()));
252 }
253
254 pub fn curve_to<P: Into<Point>>(&mut self, p1: P, p2: P, p3: P) {
262 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
263 self.push(PathEl::CurveTo(p1.into(), p2.into(), p3.into()));
264 }
265
266 pub fn close_path(&mut self) {
271 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
272 self.push(PathEl::ClosePath);
273 }
274
275 #[inline(always)]
277 pub fn elements(&self) -> &[PathEl] {
278 &self.0
279 }
280
281 #[inline(always)]
283 pub fn elements_mut(&mut self) -> &mut [PathEl] {
284 &mut self.0
285 }
286
287 pub fn iter(&self) -> impl Iterator<Item = PathEl> + Clone + '_ {
289 self.0.iter().copied()
290 }
291
292 pub fn segments(&self) -> impl Iterator<Item = PathSeg> + Clone + '_ {
294 segments(self.iter())
295 }
296
297 pub fn truncate(&mut self, len: usize) {
299 self.0.truncate(len);
300 }
301
302 #[deprecated(since = "0.11.1", note = "use the free function flatten instead")]
306 pub fn flatten(&self, tolerance: f64, callback: impl FnMut(PathEl)) {
307 flatten(self, tolerance, callback);
308 }
309
310 pub fn get_seg(&self, ix: usize) -> Option<PathSeg> {
321 if ix == 0 || ix >= self.0.len() {
322 return None;
323 }
324 let last = match self.0[ix - 1] {
325 PathEl::MoveTo(p) => p,
326 PathEl::LineTo(p) => p,
327 PathEl::QuadTo(_, p2) => p2,
328 PathEl::CurveTo(_, _, p3) => p3,
329 PathEl::ClosePath => return None,
330 };
331 match self.0[ix] {
332 PathEl::LineTo(p) => Some(PathSeg::Line(Line::new(last, p))),
333 PathEl::QuadTo(p1, p2) => Some(PathSeg::Quad(QuadBez::new(last, p1, p2))),
334 PathEl::CurveTo(p1, p2, p3) => Some(PathSeg::Cubic(CubicBez::new(last, p1, p2, p3))),
335 PathEl::ClosePath => self.0[..ix].iter().rev().find_map(|el| match *el {
336 PathEl::MoveTo(start) if start != last => {
337 Some(PathSeg::Line(Line::new(last, start)))
338 }
339 _ => None,
340 }),
341 PathEl::MoveTo(_) => None,
342 }
343 }
344
345 pub fn is_empty(&self) -> bool {
347 self.0
348 .iter()
349 .all(|el| matches!(el, PathEl::MoveTo(..) | PathEl::ClosePath))
350 }
351
352 pub fn apply_affine(&mut self, affine: Affine) {
354 for el in self.0.iter_mut() {
355 *el = affine * (*el);
356 }
357 }
358
359 #[inline]
361 pub fn is_finite(&self) -> bool {
362 self.0.iter().all(|v| v.is_finite())
363 }
364
365 #[inline]
367 pub fn is_nan(&self) -> bool {
368 self.0.iter().any(|v| v.is_nan())
369 }
370
371 pub fn control_box(&self) -> Rect {
376 let mut cbox: Option<Rect> = None;
377 let mut add_pts = |pts: &[Point]| {
378 for pt in pts {
379 cbox = match cbox {
380 Some(cbox) => Some(cbox.union_pt(*pt)),
381 _ => Some(Rect::from_points(*pt, *pt)),
382 };
383 }
384 };
385 for &el in self.elements() {
386 match el {
387 PathEl::MoveTo(p0) | PathEl::LineTo(p0) => add_pts(&[p0]),
388 PathEl::QuadTo(p0, p1) => add_pts(&[p0, p1]),
389 PathEl::CurveTo(p0, p1, p2) => add_pts(&[p0, p1, p2]),
390 PathEl::ClosePath => {}
391 }
392 }
393 cbox.unwrap_or_default()
394 }
395
396 pub fn reverse_subpaths(&self) -> BezPath {
398 let elements = self.elements();
399 let mut start_ix = 1;
400 let mut start_pt = Point::default();
401 let mut reversed = BezPath(Vec::with_capacity(elements.len()));
402 let mut pending_move = false;
405 for (ix, el) in elements.iter().enumerate() {
406 match el {
407 PathEl::MoveTo(pt) => {
408 if pending_move {
409 reversed.push(PathEl::MoveTo(start_pt));
410 }
411 if start_ix < ix {
412 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
413 }
414 pending_move = true;
415 start_pt = *pt;
416 start_ix = ix + 1;
417 }
418 PathEl::ClosePath => {
419 if start_ix <= ix {
420 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
421 }
422 reversed.push(PathEl::ClosePath);
423 start_ix = ix + 1;
424 pending_move = false;
425 }
426 _ => {
427 pending_move = false;
428 }
429 }
430 }
431 if start_ix < elements.len() {
432 reverse_subpath(start_pt, &elements[start_ix..], &mut reversed);
433 } else if pending_move {
434 reversed.push(PathEl::MoveTo(start_pt));
435 }
436 reversed
437 }
438}
439
440fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) {
444 let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt);
445 reversed.push(PathEl::MoveTo(end_pt));
446 for (ix, el) in els.iter().enumerate().rev() {
447 let end_pt = if ix > 0 {
448 els[ix - 1].end_point().unwrap()
449 } else {
450 start_pt
451 };
452 match el {
453 PathEl::LineTo(_) => reversed.push(PathEl::LineTo(end_pt)),
454 PathEl::QuadTo(c0, _) => reversed.push(PathEl::QuadTo(*c0, end_pt)),
455 PathEl::CurveTo(c0, c1, _) => reversed.push(PathEl::CurveTo(*c1, *c0, end_pt)),
456 _ => panic!("reverse_subpath expects MoveTo and ClosePath to be removed"),
457 }
458 }
459}
460
461impl FromIterator<PathEl> for BezPath {
462 fn from_iter<T: IntoIterator<Item = PathEl>>(iter: T) -> Self {
463 let el_vec: Vec<_> = iter.into_iter().collect();
464 BezPath::from_vec(el_vec)
465 }
466}
467
468impl<'a> IntoIterator for &'a BezPath {
473 type Item = PathEl;
474 type IntoIter = core::iter::Cloned<core::slice::Iter<'a, PathEl>>;
475
476 fn into_iter(self) -> Self::IntoIter {
477 self.elements().iter().cloned()
478 }
479}
480
481impl IntoIterator for BezPath {
482 type Item = PathEl;
483 type IntoIter = alloc::vec::IntoIter<PathEl>;
484
485 fn into_iter(self) -> Self::IntoIter {
486 self.0.into_iter()
487 }
488}
489
490impl Extend<PathEl> for BezPath {
491 fn extend<I: IntoIterator<Item = PathEl>>(&mut self, iter: I) {
492 self.0.extend(iter);
493 }
494}
495
496const TO_QUAD_TOL: f64 = 0.1;
498
499pub fn flatten(
565 path: impl IntoIterator<Item = PathEl>,
566 tolerance: f64,
567 mut callback: impl FnMut(PathEl),
568) {
569 let sqrt_tol = tolerance.sqrt();
570 let mut last_pt = None;
571 let mut quad_buf = Vec::new();
572 for el in path {
573 match el {
574 PathEl::MoveTo(p) => {
575 last_pt = Some(p);
576 callback(PathEl::MoveTo(p));
577 }
578 PathEl::LineTo(p) => {
579 last_pt = Some(p);
580 callback(PathEl::LineTo(p));
581 }
582 PathEl::QuadTo(p1, p2) => {
583 if let Some(p0) = last_pt {
584 let q = QuadBez::new(p0, p1, p2);
585 let params = q.estimate_subdiv(sqrt_tol);
586 let n = ((0.5 * params.val / sqrt_tol).ceil() as usize).max(1);
587 let step = 1.0 / (n as f64);
588 for i in 1..n {
589 let u = (i as f64) * step;
590 let t = q.determine_subdiv_t(¶ms, u);
591 let p = q.eval(t);
592 callback(PathEl::LineTo(p));
593 }
594 callback(PathEl::LineTo(p2));
595 }
596 last_pt = Some(p2);
597 }
598 PathEl::CurveTo(p1, p2, p3) => {
599 if let Some(p0) = last_pt {
600 let c = CubicBez::new(p0, p1, p2, p3);
601
602 let iter = c.to_quads(tolerance * TO_QUAD_TOL);
607 quad_buf.clear();
608 quad_buf.reserve(iter.size_hint().0);
609 let sqrt_remain_tol = sqrt_tol * (1.0 - TO_QUAD_TOL).sqrt();
610 let mut sum = 0.0;
611 for (_, _, q) in iter {
612 let params = q.estimate_subdiv(sqrt_remain_tol);
613 sum += params.val;
614 quad_buf.push((q, params));
615 }
616 let n = ((0.5 * sum / sqrt_remain_tol).ceil() as usize).max(1);
617
618 let step = sum / (n as f64);
621 let mut i = 1;
622 let mut val_sum = 0.0;
623 for (q, params) in &quad_buf {
624 let mut target = (i as f64) * step;
625 let recip_val = params.val.recip();
626 while target < val_sum + params.val {
627 let u = (target - val_sum) * recip_val;
628 let t = q.determine_subdiv_t(params, u);
629 let p = q.eval(t);
630 callback(PathEl::LineTo(p));
631 i += 1;
632 if i == n + 1 {
633 break;
634 }
635 target = (i as f64) * step;
636 }
637 val_sum += params.val;
638 }
639 callback(PathEl::LineTo(p3));
640 }
641 last_pt = Some(p3);
642 }
643 PathEl::ClosePath => {
644 last_pt = None;
645 callback(PathEl::ClosePath);
646 }
647 }
648 }
649}
650
651impl Mul<PathEl> for Affine {
652 type Output = PathEl;
653
654 fn mul(self, other: PathEl) -> PathEl {
655 match other {
656 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
657 PathEl::LineTo(p) => PathEl::LineTo(self * p),
658 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
659 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
660 PathEl::ClosePath => PathEl::ClosePath,
661 }
662 }
663}
664
665impl Mul<PathSeg> for Affine {
666 type Output = PathSeg;
667
668 fn mul(self, other: PathSeg) -> PathSeg {
669 match other {
670 PathSeg::Line(line) => PathSeg::Line(self * line),
671 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
672 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
673 }
674 }
675}
676
677impl Mul<BezPath> for Affine {
678 type Output = BezPath;
679
680 fn mul(self, other: BezPath) -> BezPath {
681 BezPath(other.0.iter().map(|&el| self * el).collect())
682 }
683}
684
685impl Mul<&BezPath> for Affine {
686 type Output = BezPath;
687
688 fn mul(self, other: &BezPath) -> BezPath {
689 BezPath(other.0.iter().map(|&el| self * el).collect())
690 }
691}
692
693impl Mul<PathEl> for TranslateScale {
694 type Output = PathEl;
695
696 fn mul(self, other: PathEl) -> PathEl {
697 match other {
698 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
699 PathEl::LineTo(p) => PathEl::LineTo(self * p),
700 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
701 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
702 PathEl::ClosePath => PathEl::ClosePath,
703 }
704 }
705}
706
707impl Mul<PathSeg> for TranslateScale {
708 type Output = PathSeg;
709
710 fn mul(self, other: PathSeg) -> PathSeg {
711 match other {
712 PathSeg::Line(line) => PathSeg::Line(self * line),
713 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
714 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
715 }
716 }
717}
718
719impl Mul<BezPath> for TranslateScale {
720 type Output = BezPath;
721
722 fn mul(self, other: BezPath) -> BezPath {
723 BezPath(other.0.iter().map(|&el| self * el).collect())
724 }
725}
726
727impl Mul<&BezPath> for TranslateScale {
728 type Output = BezPath;
729
730 fn mul(self, other: &BezPath) -> BezPath {
731 BezPath(other.0.iter().map(|&el| self * el).collect())
732 }
733}
734
735pub fn segments<I>(elements: I) -> Segments<I::IntoIter>
742where
743 I: IntoIterator<Item = PathEl>,
744{
745 Segments {
746 elements: elements.into_iter(),
747 start_last: None,
748 }
749}
750
751#[derive(Clone)]
755pub struct Segments<I: Iterator<Item = PathEl>> {
756 elements: I,
757 start_last: Option<(Point, Point)>,
758}
759
760impl<I: Iterator<Item = PathEl>> Iterator for Segments<I> {
761 type Item = PathSeg;
762
763 fn next(&mut self) -> Option<PathSeg> {
764 for el in &mut self.elements {
765 let (start, last) = self.start_last.get_or_insert_with(|| {
768 let point = match el {
769 PathEl::MoveTo(p) => p,
770 PathEl::LineTo(p) => p,
771 PathEl::QuadTo(_, p2) => p2,
772 PathEl::CurveTo(_, _, p3) => p3,
773 PathEl::ClosePath => panic!("Can't start a segment on a ClosePath"),
774 };
775 (point, point)
776 });
777
778 return Some(match el {
779 PathEl::MoveTo(p) => {
780 *start = p;
781 *last = p;
782 continue;
783 }
784 PathEl::LineTo(p) => PathSeg::Line(Line::new(mem::replace(last, p), p)),
785 PathEl::QuadTo(p1, p2) => {
786 PathSeg::Quad(QuadBez::new(mem::replace(last, p2), p1, p2))
787 }
788 PathEl::CurveTo(p1, p2, p3) => {
789 PathSeg::Cubic(CubicBez::new(mem::replace(last, p3), p1, p2, p3))
790 }
791 PathEl::ClosePath => {
792 if *last != *start {
793 PathSeg::Line(Line::new(mem::replace(last, *start), *start))
794 } else {
795 continue;
796 }
797 }
798 });
799 }
800
801 None
802 }
803}
804
805impl<I: Iterator<Item = PathEl>> Segments<I> {
806 pub(crate) fn perimeter(self, accuracy: f64) -> f64 {
810 self.map(|seg| seg.arclen(accuracy)).sum()
811 }
812
813 pub(crate) fn area(self) -> f64 {
815 self.map(|seg| seg.signed_area()).sum()
816 }
817
818 pub(crate) fn winding(self, p: Point) -> i32 {
820 self.map(|seg| seg.winding(p)).sum()
821 }
822
823 pub(crate) fn bounding_box(self) -> Rect {
825 let mut bbox: Option<Rect> = None;
826 for seg in self {
827 let seg_bb = ParamCurveExtrema::bounding_box(&seg);
828 if let Some(bb) = bbox {
829 bbox = Some(bb.union(seg_bb));
830 } else {
831 bbox = Some(seg_bb);
832 }
833 }
834 bbox.unwrap_or_default()
835 }
836}
837
838impl ParamCurve for PathSeg {
839 fn eval(&self, t: f64) -> Point {
840 match *self {
841 PathSeg::Line(line) => line.eval(t),
842 PathSeg::Quad(quad) => quad.eval(t),
843 PathSeg::Cubic(cubic) => cubic.eval(t),
844 }
845 }
846
847 fn subsegment(&self, range: Range<f64>) -> PathSeg {
848 match *self {
849 PathSeg::Line(line) => PathSeg::Line(line.subsegment(range)),
850 PathSeg::Quad(quad) => PathSeg::Quad(quad.subsegment(range)),
851 PathSeg::Cubic(cubic) => PathSeg::Cubic(cubic.subsegment(range)),
852 }
853 }
854}
855
856impl ParamCurveArclen for PathSeg {
857 fn arclen(&self, accuracy: f64) -> f64 {
858 match *self {
859 PathSeg::Line(line) => line.arclen(accuracy),
860 PathSeg::Quad(quad) => quad.arclen(accuracy),
861 PathSeg::Cubic(cubic) => cubic.arclen(accuracy),
862 }
863 }
864
865 fn inv_arclen(&self, arclen: f64, accuracy: f64) -> f64 {
866 match *self {
867 PathSeg::Line(line) => line.inv_arclen(arclen, accuracy),
868 PathSeg::Quad(quad) => quad.inv_arclen(arclen, accuracy),
869 PathSeg::Cubic(cubic) => cubic.inv_arclen(arclen, accuracy),
870 }
871 }
872}
873
874impl ParamCurveArea for PathSeg {
875 fn signed_area(&self) -> f64 {
876 match *self {
877 PathSeg::Line(line) => line.signed_area(),
878 PathSeg::Quad(quad) => quad.signed_area(),
879 PathSeg::Cubic(cubic) => cubic.signed_area(),
880 }
881 }
882}
883
884impl ParamCurveNearest for PathSeg {
885 fn nearest(&self, p: Point, accuracy: f64) -> Nearest {
886 match *self {
887 PathSeg::Line(line) => line.nearest(p, accuracy),
888 PathSeg::Quad(quad) => quad.nearest(p, accuracy),
889 PathSeg::Cubic(cubic) => cubic.nearest(p, accuracy),
890 }
891 }
892}
893
894impl ParamCurveExtrema for PathSeg {
895 fn extrema(&self) -> ArrayVec<f64, MAX_EXTREMA> {
896 match *self {
897 PathSeg::Line(line) => line.extrema(),
898 PathSeg::Quad(quad) => quad.extrema(),
899 PathSeg::Cubic(cubic) => cubic.extrema(),
900 }
901 }
902}
903
904impl PathSeg {
905 pub fn as_path_el(&self) -> PathEl {
907 match self {
908 PathSeg::Line(line) => PathEl::LineTo(line.p1),
909 PathSeg::Quad(q) => PathEl::QuadTo(q.p1, q.p2),
910 PathSeg::Cubic(c) => PathEl::CurveTo(c.p1, c.p2, c.p3),
911 }
912 }
913
914 pub fn reverse(&self) -> PathSeg {
917 match self {
918 PathSeg::Line(Line { p0, p1 }) => PathSeg::Line(Line::new(*p1, *p0)),
919 PathSeg::Quad(q) => PathSeg::Quad(QuadBez::new(q.p2, q.p1, q.p0)),
920 PathSeg::Cubic(c) => PathSeg::Cubic(CubicBez::new(c.p3, c.p2, c.p1, c.p0)),
921 }
922 }
923
924 pub fn to_cubic(&self) -> CubicBez {
926 match *self {
927 PathSeg::Line(Line { p0, p1 }) => CubicBez::new(p0, p0, p1, p1),
928 PathSeg::Cubic(c) => c,
929 PathSeg::Quad(q) => q.raise(),
930 }
931 }
932
933 fn winding_inner(&self, p: Point) -> i32 {
935 let start = self.start();
936 let end = self.end();
937 let sign = if end.y > start.y {
938 if p.y < start.y || p.y >= end.y {
939 return 0;
940 }
941 -1
942 } else if end.y < start.y {
943 if p.y < end.y || p.y >= start.y {
944 return 0;
945 }
946 1
947 } else {
948 return 0;
949 };
950 match *self {
951 PathSeg::Line(_line) => {
952 if p.x < start.x.min(end.x) {
953 return 0;
954 }
955 if p.x >= start.x.max(end.x) {
956 return sign;
957 }
958 let a = end.y - start.y;
960 let b = start.x - end.x;
961 let c = a * start.x + b * start.y;
962 if (a * p.x + b * p.y - c) * (sign as f64) <= 0.0 {
963 sign
964 } else {
965 0
966 }
967 }
968 PathSeg::Quad(quad) => {
969 let p1 = quad.p1;
970 if p.x < start.x.min(end.x).min(p1.x) {
971 return 0;
972 }
973 if p.x >= start.x.max(end.x).max(p1.x) {
974 return sign;
975 }
976 let a = end.y - 2.0 * p1.y + start.y;
977 let b = 2.0 * (p1.y - start.y);
978 let c = start.y - p.y;
979 for t in solve_quadratic(c, b, a) {
980 if (0.0..=1.0).contains(&t) {
981 let x = quad.eval(t).x;
982 if p.x >= x {
983 return sign;
984 } else {
985 return 0;
986 }
987 }
988 }
989 0
990 }
991 PathSeg::Cubic(cubic) => {
992 let p1 = cubic.p1;
993 let p2 = cubic.p2;
994 if p.x < start.x.min(end.x).min(p1.x).min(p2.x) {
995 return 0;
996 }
997 if p.x >= start.x.max(end.x).max(p1.x).max(p2.x) {
998 return sign;
999 }
1000 let a = end.y - 3.0 * p2.y + 3.0 * p1.y - start.y;
1001 let b = 3.0 * (p2.y - 2.0 * p1.y + start.y);
1002 let c = 3.0 * (p1.y - start.y);
1003 let d = start.y - p.y;
1004 for t in solve_cubic(d, c, b, a) {
1005 if (0.0..=1.0).contains(&t) {
1006 let x = cubic.eval(t).x;
1007 if p.x >= x {
1008 return sign;
1009 } else {
1010 return 0;
1011 }
1012 }
1013 }
1014 0
1015 }
1016 }
1017 }
1018
1019 fn winding(&self, p: Point) -> i32 {
1023 self.extrema_ranges()
1024 .into_iter()
1025 .map(|range| self.subsegment(range).winding_inner(p))
1026 .sum()
1027 }
1028
1029 pub fn intersect_line(&self, line: Line) -> ArrayVec<LineIntersection, 3> {
1056 const EPSILON: f64 = 1e-9;
1057 let p0 = line.p0;
1058 let p1 = line.p1;
1059 let dx = p1.x - p0.x;
1060 let dy = p1.y - p0.y;
1061 let mut result = ArrayVec::new();
1062 match self {
1063 PathSeg::Line(l) => {
1064 let det = dx * (l.p1.y - l.p0.y) - dy * (l.p1.x - l.p0.x);
1065 if det.abs() < EPSILON {
1066 return result;
1068 }
1069 let t = dx * (p0.y - l.p0.y) - dy * (p0.x - l.p0.x);
1070 let t = t / det;
1072 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1073 let u =
1075 (l.p0.x - p0.x) * (l.p1.y - l.p0.y) - (l.p0.y - p0.y) * (l.p1.x - l.p0.x);
1076 let u = u / det;
1077 if (0.0..=1.0).contains(&u) {
1078 result.push(LineIntersection::new(u, t));
1079 }
1080 }
1081 }
1082 PathSeg::Quad(q) => {
1083 let (px0, px1, px2) = quadratic_bez_coefs(q.p0.x, q.p1.x, q.p2.x);
1088 let (py0, py1, py2) = quadratic_bez_coefs(q.p0.y, q.p1.y, q.p2.y);
1089 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1090 let c1 = dy * px1 - dx * py1;
1091 let c2 = dy * px2 - dx * py2;
1092 let invlen2 = (dx * dx + dy * dy).recip();
1093 for t in solve_quadratic(c0, c1, c2) {
1094 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1095 let x = px0 + t * px1 + t * t * px2;
1096 let y = py0 + t * py1 + t * t * py2;
1097 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1098 if (0.0..=1.0).contains(&u) {
1099 result.push(LineIntersection::new(u, t));
1100 }
1101 }
1102 }
1103 }
1104 PathSeg::Cubic(c) => {
1105 let (px0, px1, px2, px3) = cubic_bez_coefs(c.p0.x, c.p1.x, c.p2.x, c.p3.x);
1107 let (py0, py1, py2, py3) = cubic_bez_coefs(c.p0.y, c.p1.y, c.p2.y, c.p3.y);
1108 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1109 let c1 = dy * px1 - dx * py1;
1110 let c2 = dy * px2 - dx * py2;
1111 let c3 = dy * px3 - dx * py3;
1112 let invlen2 = (dx * dx + dy * dy).recip();
1113 for t in solve_cubic(c0, c1, c2, c3) {
1114 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1115 let x = px0 + t * px1 + t * t * px2 + t * t * t * px3;
1116 let y = py0 + t * py1 + t * t * py2 + t * t * t * py3;
1117 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1118 if (0.0..=1.0).contains(&u) {
1119 result.push(LineIntersection::new(u, t));
1120 }
1121 }
1122 }
1123 }
1124 }
1125 result
1126 }
1127
1128 #[inline]
1130 pub fn is_finite(&self) -> bool {
1131 match self {
1132 PathSeg::Line(line) => line.is_finite(),
1133 PathSeg::Quad(quad_bez) => quad_bez.is_finite(),
1134 PathSeg::Cubic(cubic_bez) => cubic_bez.is_finite(),
1135 }
1136 }
1137
1138 #[inline]
1140 pub fn is_nan(&self) -> bool {
1141 match self {
1142 PathSeg::Line(line) => line.is_nan(),
1143 PathSeg::Quad(quad_bez) => quad_bez.is_nan(),
1144 PathSeg::Cubic(cubic_bez) => cubic_bez.is_nan(),
1145 }
1146 }
1147
1148 #[inline]
1149 fn as_vec2_vec(&self) -> ArrayVec<Vec2, 4> {
1150 let mut a = ArrayVec::new();
1151 match self {
1152 PathSeg::Line(l) => {
1153 a.push(l.p0.to_vec2());
1154 a.push(l.p1.to_vec2());
1155 }
1156 PathSeg::Quad(q) => {
1157 a.push(q.p0.to_vec2());
1158 a.push(q.p1.to_vec2());
1159 a.push(q.p2.to_vec2());
1160 }
1161 PathSeg::Cubic(c) => {
1162 a.push(c.p0.to_vec2());
1163 a.push(c.p1.to_vec2());
1164 a.push(c.p2.to_vec2());
1165 a.push(c.p3.to_vec2());
1166 }
1167 }
1168 a
1169 }
1170
1171 pub fn min_dist(&self, other: PathSeg, accuracy: f64) -> MinDistance {
1177 let (distance, t1, t2) = crate::mindist::min_dist_param(
1178 &self.as_vec2_vec(),
1179 &other.as_vec2_vec(),
1180 (0.0, 1.0),
1181 (0.0, 1.0),
1182 accuracy,
1183 None,
1184 );
1185 MinDistance {
1186 distance: distance.sqrt(),
1187 t1,
1188 t2,
1189 }
1190 }
1191
1192 pub(crate) fn tangents(&self) -> (Vec2, Vec2) {
1196 const EPS: f64 = 1e-12;
1197 match self {
1198 PathSeg::Line(l) => {
1199 let d = l.p1 - l.p0;
1200 (d, d)
1201 }
1202 PathSeg::Quad(q) => {
1203 let d01 = q.p1 - q.p0;
1204 let d0 = if d01.hypot2() > EPS { d01 } else { q.p2 - q.p0 };
1205 let d12 = q.p2 - q.p1;
1206 let d1 = if d12.hypot2() > EPS { d12 } else { q.p2 - q.p0 };
1207 (d0, d1)
1208 }
1209 PathSeg::Cubic(c) => {
1210 let d01 = c.p1 - c.p0;
1211 let d0 = if d01.hypot2() > EPS {
1212 d01
1213 } else {
1214 let d02 = c.p2 - c.p0;
1215 if d02.hypot2() > EPS {
1216 d02
1217 } else {
1218 c.p3 - c.p0
1219 }
1220 };
1221 let d23 = c.p3 - c.p2;
1222 let d1 = if d23.hypot2() > EPS {
1223 d23
1224 } else {
1225 let d13 = c.p3 - c.p1;
1226 if d13.hypot2() > EPS {
1227 d13
1228 } else {
1229 c.p3 - c.p0
1230 }
1231 };
1232 (d0, d1)
1233 }
1234 }
1235 }
1236}
1237
1238impl LineIntersection {
1239 #[inline(always)]
1240 fn new(line_t: f64, segment_t: f64) -> Self {
1241 LineIntersection { line_t, segment_t }
1242 }
1243
1244 #[inline]
1246 pub fn is_finite(self) -> bool {
1247 self.line_t.is_finite() && self.segment_t.is_finite()
1248 }
1249
1250 #[inline]
1252 pub fn is_nan(self) -> bool {
1253 self.line_t.is_nan() || self.segment_t.is_nan()
1254 }
1255}
1256
1257fn quadratic_bez_coefs(x0: f64, x1: f64, x2: f64) -> (f64, f64, f64) {
1259 let p0 = x0;
1260 let p1 = 2.0 * x1 - 2.0 * x0;
1261 let p2 = x2 - 2.0 * x1 + x0;
1262 (p0, p1, p2)
1263}
1264
1265fn cubic_bez_coefs(x0: f64, x1: f64, x2: f64, x3: f64) -> (f64, f64, f64, f64) {
1267 let p0 = x0;
1268 let p1 = 3.0 * x1 - 3.0 * x0;
1269 let p2 = 3.0 * x2 - 6.0 * x1 + 3.0 * x0;
1270 let p3 = x3 - 3.0 * x2 + 3.0 * x1 - x0;
1271 (p0, p1, p2, p3)
1272}
1273
1274impl From<CubicBez> for PathSeg {
1275 #[inline(always)]
1276 fn from(cubic_bez: CubicBez) -> PathSeg {
1277 PathSeg::Cubic(cubic_bez)
1278 }
1279}
1280
1281impl From<Line> for PathSeg {
1282 #[inline(always)]
1283 fn from(line: Line) -> PathSeg {
1284 PathSeg::Line(line)
1285 }
1286}
1287
1288impl From<QuadBez> for PathSeg {
1289 #[inline(always)]
1290 fn from(quad_bez: QuadBez) -> PathSeg {
1291 PathSeg::Quad(quad_bez)
1292 }
1293}
1294
1295impl Shape for BezPath {
1296 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1297
1298 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1299 self.0.iter().copied()
1300 }
1301
1302 fn to_path(&self, _tolerance: f64) -> BezPath {
1303 self.clone()
1304 }
1305
1306 #[inline(always)]
1307 fn into_path(self, _tolerance: f64) -> BezPath {
1308 self
1309 }
1310
1311 fn area(&self) -> f64 {
1313 self.elements().area()
1314 }
1315
1316 fn perimeter(&self, accuracy: f64) -> f64 {
1317 self.elements().perimeter(accuracy)
1318 }
1319
1320 fn winding(&self, pt: Point) -> i32 {
1322 self.elements().winding(pt)
1323 }
1324
1325 fn bounding_box(&self) -> Rect {
1326 self.elements().bounding_box()
1327 }
1328
1329 #[inline(always)]
1330 fn as_path_slice(&self) -> Option<&[PathEl]> {
1331 Some(&self.0)
1332 }
1333}
1334
1335impl PathEl {
1336 #[inline]
1338 pub fn is_finite(&self) -> bool {
1339 match self {
1340 PathEl::MoveTo(p) => p.is_finite(),
1341 PathEl::LineTo(p) => p.is_finite(),
1342 PathEl::QuadTo(p, p2) => p.is_finite() && p2.is_finite(),
1343 PathEl::CurveTo(p, p2, p3) => p.is_finite() && p2.is_finite() && p3.is_finite(),
1344 PathEl::ClosePath => true,
1345 }
1346 }
1347
1348 #[inline]
1350 pub fn is_nan(&self) -> bool {
1351 match self {
1352 PathEl::MoveTo(p) => p.is_nan(),
1353 PathEl::LineTo(p) => p.is_nan(),
1354 PathEl::QuadTo(p, p2) => p.is_nan() || p2.is_nan(),
1355 PathEl::CurveTo(p, p2, p3) => p.is_nan() || p2.is_nan() || p3.is_nan(),
1356 PathEl::ClosePath => false,
1357 }
1358 }
1359
1360 pub fn end_point(&self) -> Option<Point> {
1362 match self {
1363 PathEl::MoveTo(p) => Some(*p),
1364 PathEl::LineTo(p1) => Some(*p1),
1365 PathEl::QuadTo(_, p2) => Some(*p2),
1366 PathEl::CurveTo(_, _, p3) => Some(*p3),
1367 PathEl::ClosePath => None,
1368 }
1369 }
1370}
1371
1372impl<'a> Shape for &'a [PathEl] {
1377 type PathElementsIter<'iter>
1378 = core::iter::Copied<core::slice::Iter<'a, PathEl>>
1379 where
1380 'a: 'iter;
1381
1382 #[inline]
1383 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1384 self.iter().copied()
1385 }
1386
1387 fn to_path(&self, _tolerance: f64) -> BezPath {
1388 BezPath::from_vec(self.to_vec())
1389 }
1390
1391 fn area(&self) -> f64 {
1393 segments(self.iter().copied()).area()
1394 }
1395
1396 fn perimeter(&self, accuracy: f64) -> f64 {
1397 segments(self.iter().copied()).perimeter(accuracy)
1398 }
1399
1400 fn winding(&self, pt: Point) -> i32 {
1402 segments(self.iter().copied()).winding(pt)
1403 }
1404
1405 fn bounding_box(&self) -> Rect {
1406 segments(self.iter().copied()).bounding_box()
1407 }
1408
1409 #[inline(always)]
1410 fn as_path_slice(&self) -> Option<&[PathEl]> {
1411 Some(self)
1412 }
1413}
1414
1415impl<const N: usize> Shape for [PathEl; N] {
1420 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1421
1422 #[inline]
1423 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1424 self.iter().copied()
1425 }
1426
1427 fn to_path(&self, _tolerance: f64) -> BezPath {
1428 BezPath::from_vec(self.to_vec())
1429 }
1430
1431 fn area(&self) -> f64 {
1433 segments(self.iter().copied()).area()
1434 }
1435
1436 fn perimeter(&self, accuracy: f64) -> f64 {
1437 segments(self.iter().copied()).perimeter(accuracy)
1438 }
1439
1440 fn winding(&self, pt: Point) -> i32 {
1442 segments(self.iter().copied()).winding(pt)
1443 }
1444
1445 fn bounding_box(&self) -> Rect {
1446 segments(self.iter().copied()).bounding_box()
1447 }
1448
1449 #[inline(always)]
1450 fn as_path_slice(&self) -> Option<&[PathEl]> {
1451 Some(self)
1452 }
1453}
1454
1455pub struct PathSegIter {
1457 seg: PathSeg,
1458 ix: usize,
1459}
1460
1461impl Shape for PathSeg {
1462 type PathElementsIter<'iter> = PathSegIter;
1463
1464 #[inline(always)]
1465 fn path_elements(&self, _tolerance: f64) -> PathSegIter {
1466 PathSegIter { seg: *self, ix: 0 }
1467 }
1468
1469 fn area(&self) -> f64 {
1473 self.signed_area()
1474 }
1475
1476 #[inline]
1477 fn perimeter(&self, accuracy: f64) -> f64 {
1478 self.arclen(accuracy)
1479 }
1480
1481 #[inline(always)]
1482 fn winding(&self, _pt: Point) -> i32 {
1483 0
1484 }
1485
1486 #[inline]
1487 fn bounding_box(&self) -> Rect {
1488 ParamCurveExtrema::bounding_box(self)
1489 }
1490
1491 fn as_line(&self) -> Option<Line> {
1492 if let PathSeg::Line(line) = self {
1493 Some(*line)
1494 } else {
1495 None
1496 }
1497 }
1498}
1499
1500impl Iterator for PathSegIter {
1501 type Item = PathEl;
1502
1503 fn next(&mut self) -> Option<PathEl> {
1504 self.ix += 1;
1505 match (self.ix, self.seg) {
1506 (1, PathSeg::Line(seg)) => Some(PathEl::MoveTo(seg.p0)),
1508 (1, PathSeg::Quad(seg)) => Some(PathEl::MoveTo(seg.p0)),
1509 (1, PathSeg::Cubic(seg)) => Some(PathEl::MoveTo(seg.p0)),
1510 (2, PathSeg::Line(seg)) => Some(PathEl::LineTo(seg.p1)),
1511 (2, PathSeg::Quad(seg)) => Some(PathEl::QuadTo(seg.p1, seg.p2)),
1512 (2, PathSeg::Cubic(seg)) => Some(PathEl::CurveTo(seg.p1, seg.p2, seg.p3)),
1513 _ => None,
1514 }
1515 }
1516}
1517
1518#[cfg(test)]
1519mod tests {
1520 use crate::{Circle, DEFAULT_ACCURACY};
1521
1522 use super::*;
1523
1524 fn assert_approx_eq(x: f64, y: f64) {
1525 assert!((x - y).abs() < 1e-8, "{x} != {y}");
1526 }
1527
1528 #[test]
1529 #[should_panic(expected = "uninitialized subpath")]
1530 fn test_elements_to_segments_starts_on_closepath() {
1531 let mut path = BezPath::new();
1532 path.close_path();
1533 path.segments().next();
1534 }
1535
1536 #[test]
1537 fn test_elements_to_segments_closepath_refers_to_last_moveto() {
1538 let mut path = BezPath::new();
1539 path.move_to((5.0, 5.0));
1540 path.line_to((15.0, 15.0));
1541 path.move_to((10.0, 10.0));
1542 path.line_to((15.0, 15.0));
1543 path.close_path();
1544 assert_eq!(
1545 path.segments().collect::<Vec<_>>().last(),
1546 Some(&Line::new((15.0, 15.0), (10.0, 10.0)).into()),
1547 );
1548 }
1549
1550 #[test]
1551 #[should_panic(expected = "uninitialized subpath")]
1552 fn test_must_not_start_on_quad() {
1553 let mut path = BezPath::new();
1554 path.quad_to((5.0, 5.0), (10.0, 10.0));
1555 path.line_to((15.0, 15.0));
1556 path.close_path();
1557 }
1558
1559 #[test]
1560 fn test_intersect_line() {
1561 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1562 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1563 let intersection = PathSeg::Line(h_line).intersect_line(v_line)[0];
1564 assert_approx_eq(intersection.segment_t, 0.1);
1565 assert_approx_eq(intersection.line_t, 0.5);
1566
1567 let v_line = Line::new((-10.0, -10.0), (-10.0, 10.0));
1568 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1569
1570 let v_line = Line::new((10.0, 10.0), (10.0, 20.0));
1571 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1572 }
1573
1574 #[test]
1575 fn test_intersect_qad() {
1576 let q = QuadBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -10.0));
1577 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1578 assert_eq!(PathSeg::Quad(q).intersect_line(v_line).len(), 1);
1579 let intersection = PathSeg::Quad(q).intersect_line(v_line)[0];
1580 assert_approx_eq(intersection.segment_t, 0.5);
1581 assert_approx_eq(intersection.line_t, 0.75);
1582
1583 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1584 assert_eq!(PathSeg::Quad(q).intersect_line(h_line).len(), 2);
1585 }
1586
1587 #[test]
1588 fn test_intersect_cubic() {
1589 let c = CubicBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -20.0), (30.0, 10.0));
1590 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1591 assert_eq!(PathSeg::Cubic(c).intersect_line(v_line).len(), 1);
1592 let intersection = PathSeg::Cubic(c).intersect_line(v_line)[0];
1593 assert_approx_eq(intersection.segment_t, 0.333333333);
1594 assert_approx_eq(intersection.line_t, 0.592592592);
1595
1596 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1597 assert_eq!(PathSeg::Cubic(c).intersect_line(h_line).len(), 3);
1598 }
1599
1600 #[test]
1601 fn test_contains() {
1602 let mut path = BezPath::new();
1603 path.move_to((0.0, 0.0));
1604 path.line_to((1.0, 1.0));
1605 path.line_to((2.0, 0.0));
1606 path.close_path();
1607 assert_eq!(path.winding(Point::new(1.0, 0.5)), -1);
1608 assert!(path.contains(Point::new(1.0, 0.5)));
1609 }
1610
1611 #[test]
1613 fn test_get_seg() {
1614 let circle = Circle::new((10.0, 10.0), 2.0).to_path(DEFAULT_ACCURACY);
1615 let segments = circle.path_segments(DEFAULT_ACCURACY).collect::<Vec<_>>();
1616 let get_segs = (1..usize::MAX)
1617 .map_while(|i| circle.get_seg(i))
1618 .collect::<Vec<_>>();
1619 assert_eq!(segments, get_segs);
1620 }
1621
1622 #[test]
1623 fn test_control_box() {
1624 let path = BezPath::from_svg("M200,300 C50,50 350,50 200,300").unwrap();
1627 assert_eq!(Rect::new(50.0, 50.0, 350.0, 300.0), path.control_box());
1628 assert!(path.control_box().area() > path.bounding_box().area());
1629 }
1630
1631 #[test]
1632 fn test_reverse_unclosed() {
1633 let path = BezPath::from_svg("M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60").unwrap();
1634 let reversed = path.reverse_subpaths();
1635 assert_eq!(
1636 "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10",
1637 reversed.to_svg()
1638 );
1639 }
1640
1641 #[test]
1642 fn test_reverse_closed_triangle() {
1643 let path = BezPath::from_svg("M100,100 L150,200 L50,200 Z").unwrap();
1644 let reversed = path.reverse_subpaths();
1645 assert_eq!("M50,200 L150,200 L100,100 Z", reversed.to_svg());
1646 }
1647
1648 #[test]
1649 fn test_reverse_closed_shape() {
1650 let path = BezPath::from_svg(
1651 "M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z",
1652 )
1653 .unwrap();
1654 let reversed = path.reverse_subpaths();
1655 assert_eq!(
1656 "M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z",
1657 reversed.to_svg()
1658 );
1659 }
1660
1661 #[test]
1662 fn test_reverse_multiple_subpaths() {
1663 let svg = "M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60 M100,100 L150,200 L50,200 Z M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z";
1664 let expected_svg = "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10 M50,200 L150,200 L100,100 Z M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z";
1665 let path = BezPath::from_svg(svg).unwrap();
1666 let reversed = path.reverse_subpaths();
1667 assert_eq!(expected_svg, reversed.to_svg());
1668 }
1669
1670 #[test]
1672 fn test_reverse_lines() {
1673 let mut path = BezPath::new();
1674 path.move_to((0.0, 0.0));
1675 path.line_to((1.0, 1.0));
1676 path.line_to((2.0, 2.0));
1677 path.line_to((3.0, 3.0));
1678 path.close_path();
1679 let rev = path.reverse_subpaths();
1680 assert_eq!("M3,3 L2,2 L1,1 L0,0 Z", rev.to_svg());
1681 }
1682
1683 #[test]
1684 fn test_reverse_multiple_moves() {
1685 reverse_test_helper(
1686 vec![
1687 PathEl::MoveTo((2.0, 2.0).into()),
1688 PathEl::MoveTo((3.0, 3.0).into()),
1689 PathEl::ClosePath,
1690 PathEl::MoveTo((4.0, 4.0).into()),
1691 ],
1692 vec![
1693 PathEl::MoveTo((2.0, 2.0).into()),
1694 PathEl::MoveTo((3.0, 3.0).into()),
1695 PathEl::ClosePath,
1696 PathEl::MoveTo((4.0, 4.0).into()),
1697 ],
1698 );
1699 }
1700
1701 #[test]
1708 fn test_reverse_closed_last_line_not_on_move() {
1709 reverse_test_helper(
1710 vec![
1711 PathEl::MoveTo((0.0, 0.0).into()),
1712 PathEl::LineTo((1.0, 1.0).into()),
1713 PathEl::LineTo((2.0, 2.0).into()),
1714 PathEl::LineTo((3.0, 3.0).into()),
1715 PathEl::ClosePath,
1716 ],
1717 vec![
1718 PathEl::MoveTo((3.0, 3.0).into()),
1719 PathEl::LineTo((2.0, 2.0).into()),
1720 PathEl::LineTo((1.0, 1.0).into()),
1721 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1723 ],
1724 );
1725 }
1726
1727 #[test]
1728 fn test_reverse_closed_last_line_overlaps_move() {
1729 reverse_test_helper(
1730 vec![
1731 PathEl::MoveTo((0.0, 0.0).into()),
1732 PathEl::LineTo((1.0, 1.0).into()),
1733 PathEl::LineTo((2.0, 2.0).into()),
1734 PathEl::LineTo((0.0, 0.0).into()),
1735 PathEl::ClosePath,
1736 ],
1737 vec![
1738 PathEl::MoveTo((0.0, 0.0).into()),
1739 PathEl::LineTo((2.0, 2.0).into()),
1740 PathEl::LineTo((1.0, 1.0).into()),
1741 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1743 ],
1744 );
1745 }
1746
1747 #[test]
1748 fn test_reverse_closed_duplicate_line_following_move() {
1749 reverse_test_helper(
1750 vec![
1751 PathEl::MoveTo((0.0, 0.0).into()),
1752 PathEl::LineTo((0.0, 0.0).into()),
1753 PathEl::LineTo((1.0, 1.0).into()),
1754 PathEl::LineTo((2.0, 2.0).into()),
1755 PathEl::ClosePath,
1756 ],
1757 vec![
1758 PathEl::MoveTo((2.0, 2.0).into()),
1759 PathEl::LineTo((1.0, 1.0).into()),
1760 PathEl::LineTo((0.0, 0.0).into()), PathEl::LineTo((0.0, 0.0).into()),
1762 PathEl::ClosePath,
1763 ],
1764 );
1765 }
1766
1767 #[test]
1768 fn test_reverse_closed_two_lines() {
1769 reverse_test_helper(
1770 vec![
1771 PathEl::MoveTo((0.0, 0.0).into()),
1772 PathEl::LineTo((1.0, 1.0).into()),
1773 PathEl::ClosePath,
1774 ],
1775 vec![
1776 PathEl::MoveTo((1.0, 1.0).into()),
1777 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1779 ],
1780 );
1781 }
1782
1783 #[test]
1784 fn test_reverse_closed_last_curve_overlaps_move() {
1785 reverse_test_helper(
1786 vec![
1787 PathEl::MoveTo((0.0, 0.0).into()),
1788 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1789 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (0.0, 0.0).into()),
1790 PathEl::ClosePath,
1791 ],
1792 vec![
1793 PathEl::MoveTo((0.0, 0.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1795 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1796 PathEl::ClosePath,
1797 ],
1798 );
1799 }
1800
1801 #[test]
1802 fn test_reverse_closed_last_curve_not_on_move() {
1803 reverse_test_helper(
1804 vec![
1805 PathEl::MoveTo((0.0, 0.0).into()),
1806 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1807 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (6.0, 6.0).into()),
1808 PathEl::ClosePath,
1809 ],
1810 vec![
1811 PathEl::MoveTo((6.0, 6.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1813 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1814 PathEl::ClosePath,
1815 ],
1816 );
1817 }
1818
1819 #[test]
1820 fn test_reverse_closed_line_curve_line() {
1821 reverse_test_helper(
1822 vec![
1823 PathEl::MoveTo((0.0, 0.0).into()),
1824 PathEl::LineTo((1.0, 1.0).into()), PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
1826 PathEl::CurveTo((5.0, 5.0).into(), (6.0, 6.0).into(), (7.0, 7.0).into()),
1827 PathEl::ClosePath,
1828 ],
1829 vec![
1830 PathEl::MoveTo((7.0, 7.0).into()),
1831 PathEl::CurveTo((6.0, 6.0).into(), (5.0, 5.0).into(), (4.0, 4.0).into()),
1832 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
1833 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1835 ],
1836 );
1837 }
1838
1839 #[test]
1840 fn test_reverse_closed_last_quad_overlaps_move() {
1841 reverse_test_helper(
1842 vec![
1843 PathEl::MoveTo((0.0, 0.0).into()),
1844 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1845 PathEl::QuadTo((3.0, 3.0).into(), (0.0, 0.0).into()),
1846 PathEl::ClosePath,
1847 ],
1848 vec![
1849 PathEl::MoveTo((0.0, 0.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1851 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1852 PathEl::ClosePath,
1853 ],
1854 );
1855 }
1856
1857 #[test]
1858 fn test_reverse_closed_last_quad_not_on_move() {
1859 reverse_test_helper(
1860 vec![
1861 PathEl::MoveTo((0.0, 0.0).into()),
1862 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1863 PathEl::QuadTo((3.0, 3.0).into(), (4.0, 4.0).into()),
1864 PathEl::ClosePath,
1865 ],
1866 vec![
1867 PathEl::MoveTo((4.0, 4.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1869 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1870 PathEl::ClosePath,
1871 ],
1872 );
1873 }
1874
1875 #[test]
1876 fn test_reverse_closed_line_quad_line() {
1877 reverse_test_helper(
1878 vec![
1879 PathEl::MoveTo((0.0, 0.0).into()),
1880 PathEl::LineTo((1.0, 1.0).into()), PathEl::QuadTo((2.0, 2.0).into(), (3.0, 3.0).into()),
1882 PathEl::ClosePath,
1883 ],
1884 vec![
1885 PathEl::MoveTo((3.0, 3.0).into()),
1886 PathEl::QuadTo((2.0, 2.0).into(), (1.0, 1.0).into()),
1887 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1889 ],
1890 );
1891 }
1892
1893 #[test]
1894 fn test_reverse_empty() {
1895 reverse_test_helper(vec![], vec![]);
1896 }
1897
1898 #[test]
1899 fn test_reverse_single_point() {
1900 reverse_test_helper(
1901 vec![PathEl::MoveTo((0.0, 0.0).into())],
1902 vec![PathEl::MoveTo((0.0, 0.0).into())],
1903 );
1904 }
1905
1906 #[test]
1907 fn test_reverse_single_point_closed() {
1908 reverse_test_helper(
1909 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
1910 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
1911 );
1912 }
1913
1914 #[test]
1915 fn test_reverse_single_line_open() {
1916 reverse_test_helper(
1917 vec![
1918 PathEl::MoveTo((0.0, 0.0).into()),
1919 PathEl::LineTo((1.0, 1.0).into()),
1920 ],
1921 vec![
1922 PathEl::MoveTo((1.0, 1.0).into()),
1923 PathEl::LineTo((0.0, 0.0).into()),
1924 ],
1925 );
1926 }
1927
1928 #[test]
1929 fn test_reverse_single_curve_open() {
1930 reverse_test_helper(
1931 vec![
1932 PathEl::MoveTo((0.0, 0.0).into()),
1933 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1934 ],
1935 vec![
1936 PathEl::MoveTo((3.0, 3.0).into()),
1937 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1938 ],
1939 );
1940 }
1941
1942 #[test]
1943 fn test_reverse_curve_line_open() {
1944 reverse_test_helper(
1945 vec![
1946 PathEl::MoveTo((0.0, 0.0).into()),
1947 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1948 PathEl::LineTo((4.0, 4.0).into()),
1949 ],
1950 vec![
1951 PathEl::MoveTo((4.0, 4.0).into()),
1952 PathEl::LineTo((3.0, 3.0).into()),
1953 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1954 ],
1955 );
1956 }
1957
1958 #[test]
1959 fn test_reverse_line_curve_open() {
1960 reverse_test_helper(
1961 vec![
1962 PathEl::MoveTo((0.0, 0.0).into()),
1963 PathEl::LineTo((1.0, 1.0).into()),
1964 PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
1965 ],
1966 vec![
1967 PathEl::MoveTo((4.0, 4.0).into()),
1968 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
1969 PathEl::LineTo((0.0, 0.0).into()),
1970 ],
1971 );
1972 }
1973
1974 #[test]
1975 fn test_reverse_duplicate_point_after_move() {
1976 reverse_test_helper(
1979 vec![
1980 PathEl::MoveTo((848.0, 348.0).into()),
1981 PathEl::LineTo((848.0, 348.0).into()),
1982 PathEl::QuadTo((848.0, 526.0).into(), (449.0, 704.0).into()),
1983 PathEl::QuadTo((848.0, 171.0).into(), (848.0, 348.0).into()),
1984 PathEl::ClosePath,
1985 ],
1986 vec![
1987 PathEl::MoveTo((848.0, 348.0).into()),
1988 PathEl::QuadTo((848.0, 171.0).into(), (449.0, 704.0).into()),
1989 PathEl::QuadTo((848.0, 526.0).into(), (848.0, 348.0).into()),
1990 PathEl::LineTo((848.0, 348.0).into()),
1991 PathEl::ClosePath,
1992 ],
1993 );
1994 }
1995
1996 #[test]
1997 fn test_reverse_duplicate_point_at_end() {
1998 reverse_test_helper(
2000 vec![
2001 PathEl::MoveTo((0.0, 651.0).into()),
2002 PathEl::LineTo((0.0, 101.0).into()),
2003 PathEl::LineTo((0.0, 101.0).into()),
2004 PathEl::LineTo((0.0, 651.0).into()),
2005 PathEl::LineTo((0.0, 651.0).into()),
2006 PathEl::ClosePath,
2007 ],
2008 vec![
2009 PathEl::MoveTo((0.0, 651.0).into()),
2010 PathEl::LineTo((0.0, 651.0).into()),
2011 PathEl::LineTo((0.0, 101.0).into()),
2012 PathEl::LineTo((0.0, 101.0).into()),
2013 PathEl::LineTo((0.0, 651.0).into()),
2014 PathEl::ClosePath,
2015 ],
2016 );
2017 }
2018
2019 fn reverse_test_helper(contour: Vec<PathEl>, expected: Vec<PathEl>) {
2020 assert_eq!(BezPath(contour).reverse_subpaths().0, expected);
2021 }
2022}