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 into_elements(self) -> Vec<PathEl> {
278 self.0
279 }
280
281 #[inline(always)]
287 pub fn elements(&self) -> &[PathEl] {
288 &self.0
289 }
290
291 #[inline(always)]
293 pub fn elements_mut(&mut self) -> &mut [PathEl] {
294 &mut self.0
295 }
296
297 pub fn iter(&self) -> impl Iterator<Item = PathEl> + Clone + '_ {
299 self.0.iter().copied()
300 }
301
302 pub fn segments(&self) -> impl Iterator<Item = PathSeg> + Clone + '_ {
304 segments(self.iter())
305 }
306
307 pub fn truncate(&mut self, len: usize) {
309 self.0.truncate(len);
310 }
311
312 pub fn get_seg(&self, ix: usize) -> Option<PathSeg> {
323 if ix == 0 || ix >= self.0.len() {
324 return None;
325 }
326 let last = match self.0[ix - 1] {
327 PathEl::MoveTo(p) => p,
328 PathEl::LineTo(p) => p,
329 PathEl::QuadTo(_, p2) => p2,
330 PathEl::CurveTo(_, _, p3) => p3,
331 PathEl::ClosePath => return None,
332 };
333 match self.0[ix] {
334 PathEl::LineTo(p) => Some(PathSeg::Line(Line::new(last, p))),
335 PathEl::QuadTo(p1, p2) => Some(PathSeg::Quad(QuadBez::new(last, p1, p2))),
336 PathEl::CurveTo(p1, p2, p3) => Some(PathSeg::Cubic(CubicBez::new(last, p1, p2, p3))),
337 PathEl::ClosePath => self.0[..ix].iter().rev().find_map(|el| match *el {
338 PathEl::MoveTo(start) if start != last => {
339 Some(PathSeg::Line(Line::new(last, start)))
340 }
341 _ => None,
342 }),
343 PathEl::MoveTo(_) => None,
344 }
345 }
346
347 pub fn is_empty(&self) -> bool {
349 self.0
350 .iter()
351 .all(|el| matches!(el, PathEl::MoveTo(..) | PathEl::ClosePath))
352 }
353
354 pub fn apply_affine(&mut self, affine: Affine) {
356 for el in self.0.iter_mut() {
357 *el = affine * (*el);
358 }
359 }
360
361 #[inline]
363 pub fn is_finite(&self) -> bool {
364 self.0.iter().all(|v| v.is_finite())
365 }
366
367 #[inline]
369 pub fn is_nan(&self) -> bool {
370 self.0.iter().any(|v| v.is_nan())
371 }
372
373 pub fn control_box(&self) -> Rect {
378 let mut cbox: Option<Rect> = None;
379 let mut add_pts = |pts: &[Point]| {
380 for pt in pts {
381 cbox = match cbox {
382 Some(cbox) => Some(cbox.union_pt(*pt)),
383 _ => Some(Rect::from_points(*pt, *pt)),
384 };
385 }
386 };
387 for &el in self.elements() {
388 match el {
389 PathEl::MoveTo(p0) | PathEl::LineTo(p0) => add_pts(&[p0]),
390 PathEl::QuadTo(p0, p1) => add_pts(&[p0, p1]),
391 PathEl::CurveTo(p0, p1, p2) => add_pts(&[p0, p1, p2]),
392 PathEl::ClosePath => {}
393 }
394 }
395 cbox.unwrap_or_default()
396 }
397
398 pub fn current_position(&self) -> Option<Point> {
404 match self.0.last()? {
405 PathEl::MoveTo(p) => Some(*p),
406 PathEl::LineTo(p1) => Some(*p1),
407 PathEl::QuadTo(_, p2) => Some(*p2),
408 PathEl::CurveTo(_, _, p3) => Some(*p3),
409 PathEl::ClosePath => self
410 .elements()
411 .iter()
412 .rev()
413 .skip(1)
414 .take_while(|el| !matches!(el, PathEl::ClosePath))
415 .last()
416 .and_then(|el| el.end_point()),
417 }
418 }
419
420 pub fn reverse_subpaths(&self) -> BezPath {
422 let elements = self.elements();
423 let mut start_ix = 1;
424 let mut start_pt = Point::default();
425 let mut reversed = BezPath(Vec::with_capacity(elements.len()));
426 let mut pending_move = false;
429 for (ix, el) in elements.iter().enumerate() {
430 match el {
431 PathEl::MoveTo(pt) => {
432 if pending_move {
433 reversed.push(PathEl::MoveTo(start_pt));
434 }
435 if start_ix < ix {
436 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
437 }
438 pending_move = true;
439 start_pt = *pt;
440 start_ix = ix + 1;
441 }
442 PathEl::ClosePath => {
443 if start_ix <= ix {
444 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
445 }
446 reversed.push(PathEl::ClosePath);
447 start_ix = ix + 1;
448 pending_move = false;
449 }
450 _ => {
451 pending_move = false;
452 }
453 }
454 }
455 if start_ix < elements.len() {
456 reverse_subpath(start_pt, &elements[start_ix..], &mut reversed);
457 } else if pending_move {
458 reversed.push(PathEl::MoveTo(start_pt));
459 }
460 reversed
461 }
462}
463
464fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) {
468 let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt);
469 reversed.push(PathEl::MoveTo(end_pt));
470 for (ix, el) in els.iter().enumerate().rev() {
471 let end_pt = if ix > 0 {
472 els[ix - 1].end_point().unwrap()
473 } else {
474 start_pt
475 };
476 match el {
477 PathEl::LineTo(_) => reversed.push(PathEl::LineTo(end_pt)),
478 PathEl::QuadTo(c0, _) => reversed.push(PathEl::QuadTo(*c0, end_pt)),
479 PathEl::CurveTo(c0, c1, _) => reversed.push(PathEl::CurveTo(*c1, *c0, end_pt)),
480 _ => panic!("reverse_subpath expects MoveTo and ClosePath to be removed"),
481 }
482 }
483}
484
485impl FromIterator<PathEl> for BezPath {
486 fn from_iter<T: IntoIterator<Item = PathEl>>(iter: T) -> Self {
487 let el_vec: Vec<_> = iter.into_iter().collect();
488 BezPath::from_vec(el_vec)
489 }
490}
491
492impl<'a> IntoIterator for &'a BezPath {
497 type Item = PathEl;
498 type IntoIter = core::iter::Cloned<core::slice::Iter<'a, PathEl>>;
499
500 fn into_iter(self) -> Self::IntoIter {
501 self.elements().iter().cloned()
502 }
503}
504
505impl IntoIterator for BezPath {
506 type Item = PathEl;
507 type IntoIter = alloc::vec::IntoIter<PathEl>;
508
509 fn into_iter(self) -> Self::IntoIter {
510 self.0.into_iter()
511 }
512}
513
514impl Extend<PathEl> for BezPath {
515 fn extend<I: IntoIterator<Item = PathEl>>(&mut self, iter: I) {
528 self.0.extend(iter);
529 }
530}
531
532const TO_QUAD_TOL: f64 = 0.1;
534
535pub fn flatten(
601 path: impl IntoIterator<Item = PathEl>,
602 tolerance: f64,
603 mut callback: impl FnMut(PathEl),
604) {
605 let sqrt_tol = tolerance.sqrt();
606 let mut last_pt = None;
607 let mut quad_buf = Vec::new();
608 for el in path {
609 match el {
610 PathEl::MoveTo(p) => {
611 last_pt = Some(p);
612 callback(PathEl::MoveTo(p));
613 }
614 PathEl::LineTo(p) => {
615 last_pt = Some(p);
616 callback(PathEl::LineTo(p));
617 }
618 PathEl::QuadTo(p1, p2) => {
619 if let Some(p0) = last_pt {
620 let q = QuadBez::new(p0, p1, p2);
621 let params = q.estimate_subdiv(sqrt_tol);
622 let n = ((0.5 * params.val / sqrt_tol).ceil() as usize).max(1);
623 let step = 1.0 / (n as f64);
624 for i in 1..n {
625 let u = (i as f64) * step;
626 let t = q.determine_subdiv_t(¶ms, u);
627 let p = q.eval(t);
628 callback(PathEl::LineTo(p));
629 }
630 callback(PathEl::LineTo(p2));
631 }
632 last_pt = Some(p2);
633 }
634 PathEl::CurveTo(p1, p2, p3) => {
635 if let Some(p0) = last_pt {
636 let c = CubicBez::new(p0, p1, p2, p3);
637
638 let iter = c.to_quads(tolerance * TO_QUAD_TOL);
643 quad_buf.clear();
644 quad_buf.reserve(iter.size_hint().0);
645 let sqrt_remain_tol = sqrt_tol * (1.0 - TO_QUAD_TOL).sqrt();
646 let mut sum = 0.0;
647 for (_, _, q) in iter {
648 let params = q.estimate_subdiv(sqrt_remain_tol);
649 sum += params.val;
650 quad_buf.push((q, params));
651 }
652 let n = ((0.5 * sum / sqrt_remain_tol).ceil() as usize).max(1);
653
654 let step = sum / (n as f64);
657 let mut i = 1;
658 let mut val_sum = 0.0;
659 for (q, params) in &quad_buf {
660 let mut target = (i as f64) * step;
661 let recip_val = params.val.recip();
662 while target < val_sum + params.val {
663 let u = (target - val_sum) * recip_val;
664 let t = q.determine_subdiv_t(params, u);
665 let p = q.eval(t);
666 callback(PathEl::LineTo(p));
667 i += 1;
668 if i == n + 1 {
669 break;
670 }
671 target = (i as f64) * step;
672 }
673 val_sum += params.val;
674 }
675 callback(PathEl::LineTo(p3));
676 }
677 last_pt = Some(p3);
678 }
679 PathEl::ClosePath => {
680 last_pt = None;
681 callback(PathEl::ClosePath);
682 }
683 }
684 }
685}
686
687impl Mul<PathEl> for Affine {
688 type Output = PathEl;
689
690 #[inline(always)]
693 fn mul(self, other: PathEl) -> PathEl {
694 match other {
695 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
696 PathEl::LineTo(p) => PathEl::LineTo(self * p),
697 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
698 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
699 PathEl::ClosePath => PathEl::ClosePath,
700 }
701 }
702}
703
704impl Mul<PathSeg> for Affine {
705 type Output = PathSeg;
706
707 fn mul(self, other: PathSeg) -> PathSeg {
708 match other {
709 PathSeg::Line(line) => PathSeg::Line(self * line),
710 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
711 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
712 }
713 }
714}
715
716impl Mul<BezPath> for Affine {
717 type Output = BezPath;
718
719 fn mul(self, other: BezPath) -> BezPath {
720 BezPath(other.0.iter().map(|&el| self * el).collect())
721 }
722}
723
724impl Mul<&BezPath> for Affine {
725 type Output = BezPath;
726
727 fn mul(self, other: &BezPath) -> BezPath {
728 BezPath(other.0.iter().map(|&el| self * el).collect())
729 }
730}
731
732impl Mul<PathEl> for TranslateScale {
733 type Output = PathEl;
734
735 fn mul(self, other: PathEl) -> PathEl {
736 match other {
737 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
738 PathEl::LineTo(p) => PathEl::LineTo(self * p),
739 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
740 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
741 PathEl::ClosePath => PathEl::ClosePath,
742 }
743 }
744}
745
746impl Mul<PathSeg> for TranslateScale {
747 type Output = PathSeg;
748
749 fn mul(self, other: PathSeg) -> PathSeg {
750 match other {
751 PathSeg::Line(line) => PathSeg::Line(self * line),
752 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
753 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
754 }
755 }
756}
757
758impl Mul<BezPath> for TranslateScale {
759 type Output = BezPath;
760
761 fn mul(self, other: BezPath) -> BezPath {
762 BezPath(other.0.iter().map(|&el| self * el).collect())
763 }
764}
765
766impl Mul<&BezPath> for TranslateScale {
767 type Output = BezPath;
768
769 fn mul(self, other: &BezPath) -> BezPath {
770 BezPath(other.0.iter().map(|&el| self * el).collect())
771 }
772}
773
774pub fn segments<I>(elements: I) -> Segments<I::IntoIter>
781where
782 I: IntoIterator<Item = PathEl>,
783{
784 Segments {
785 elements: elements.into_iter(),
786 start_last: None,
787 }
788}
789
790#[derive(Clone)]
794pub struct Segments<I: Iterator<Item = PathEl>> {
795 elements: I,
796 start_last: Option<(Point, Point)>,
797}
798
799impl<I: Iterator<Item = PathEl>> Iterator for Segments<I> {
800 type Item = PathSeg;
801
802 #[inline]
803 fn next(&mut self) -> Option<PathSeg> {
804 for el in &mut self.elements {
805 let (start, last) = self.start_last.get_or_insert_with(|| {
808 let point = match el {
809 PathEl::MoveTo(p) => p,
810 PathEl::LineTo(p) => p,
811 PathEl::QuadTo(_, p2) => p2,
812 PathEl::CurveTo(_, _, p3) => p3,
813 PathEl::ClosePath => panic!("Can't start a segment on a ClosePath"),
814 };
815 (point, point)
816 });
817
818 return Some(match el {
819 PathEl::MoveTo(p) => {
820 *start = p;
821 *last = p;
822 continue;
823 }
824 PathEl::LineTo(p) => PathSeg::Line(Line::new(mem::replace(last, p), p)),
825 PathEl::QuadTo(p1, p2) => {
826 PathSeg::Quad(QuadBez::new(mem::replace(last, p2), p1, p2))
827 }
828 PathEl::CurveTo(p1, p2, p3) => {
829 PathSeg::Cubic(CubicBez::new(mem::replace(last, p3), p1, p2, p3))
830 }
831 PathEl::ClosePath => {
832 if *last != *start {
833 PathSeg::Line(Line::new(mem::replace(last, *start), *start))
834 } else {
835 continue;
836 }
837 }
838 });
839 }
840
841 None
842 }
843}
844
845impl<I: Iterator<Item = PathEl>> Segments<I> {
846 pub(crate) fn perimeter(self, accuracy: f64) -> f64 {
850 self.map(|seg| seg.arclen(accuracy)).sum()
851 }
852
853 pub(crate) fn area(self) -> f64 {
855 self.map(|seg| seg.signed_area()).sum()
856 }
857
858 pub(crate) fn winding(self, p: Point) -> i32 {
860 self.map(|seg| seg.winding(p)).sum()
861 }
862
863 pub(crate) fn bounding_box(self) -> Rect {
865 let mut bbox: Option<Rect> = None;
866 for seg in self {
867 let seg_bb = ParamCurveExtrema::bounding_box(&seg);
868 if let Some(bb) = bbox {
869 bbox = Some(bb.union(seg_bb));
870 } else {
871 bbox = Some(seg_bb);
872 }
873 }
874 bbox.unwrap_or_default()
875 }
876}
877
878impl ParamCurve for PathSeg {
879 fn eval(&self, t: f64) -> Point {
880 match *self {
881 PathSeg::Line(line) => line.eval(t),
882 PathSeg::Quad(quad) => quad.eval(t),
883 PathSeg::Cubic(cubic) => cubic.eval(t),
884 }
885 }
886
887 fn subsegment(&self, range: Range<f64>) -> PathSeg {
888 match *self {
889 PathSeg::Line(line) => PathSeg::Line(line.subsegment(range)),
890 PathSeg::Quad(quad) => PathSeg::Quad(quad.subsegment(range)),
891 PathSeg::Cubic(cubic) => PathSeg::Cubic(cubic.subsegment(range)),
892 }
893 }
894
895 fn start(&self) -> Point {
896 match *self {
897 PathSeg::Line(line) => line.start(),
898 PathSeg::Quad(quad) => quad.start(),
899 PathSeg::Cubic(cubic) => cubic.start(),
900 }
901 }
902
903 fn end(&self) -> Point {
904 match *self {
905 PathSeg::Line(line) => line.end(),
906 PathSeg::Quad(quad) => quad.end(),
907 PathSeg::Cubic(cubic) => cubic.end(),
908 }
909 }
910}
911
912impl ParamCurveArclen for PathSeg {
913 fn arclen(&self, accuracy: f64) -> f64 {
914 match *self {
915 PathSeg::Line(line) => line.arclen(accuracy),
916 PathSeg::Quad(quad) => quad.arclen(accuracy),
917 PathSeg::Cubic(cubic) => cubic.arclen(accuracy),
918 }
919 }
920
921 fn inv_arclen(&self, arclen: f64, accuracy: f64) -> f64 {
922 match *self {
923 PathSeg::Line(line) => line.inv_arclen(arclen, accuracy),
924 PathSeg::Quad(quad) => quad.inv_arclen(arclen, accuracy),
925 PathSeg::Cubic(cubic) => cubic.inv_arclen(arclen, accuracy),
926 }
927 }
928}
929
930impl ParamCurveArea for PathSeg {
931 fn signed_area(&self) -> f64 {
932 match *self {
933 PathSeg::Line(line) => line.signed_area(),
934 PathSeg::Quad(quad) => quad.signed_area(),
935 PathSeg::Cubic(cubic) => cubic.signed_area(),
936 }
937 }
938}
939
940impl ParamCurveNearest for PathSeg {
941 fn nearest(&self, p: Point, accuracy: f64) -> Nearest {
942 match *self {
943 PathSeg::Line(line) => line.nearest(p, accuracy),
944 PathSeg::Quad(quad) => quad.nearest(p, accuracy),
945 PathSeg::Cubic(cubic) => cubic.nearest(p, accuracy),
946 }
947 }
948}
949
950impl ParamCurveExtrema for PathSeg {
951 fn extrema(&self) -> ArrayVec<f64, MAX_EXTREMA> {
952 match *self {
953 PathSeg::Line(line) => line.extrema(),
954 PathSeg::Quad(quad) => quad.extrema(),
955 PathSeg::Cubic(cubic) => cubic.extrema(),
956 }
957 }
958}
959
960impl PathSeg {
961 pub fn as_path_el(&self) -> PathEl {
963 match self {
964 PathSeg::Line(line) => PathEl::LineTo(line.p1),
965 PathSeg::Quad(q) => PathEl::QuadTo(q.p1, q.p2),
966 PathSeg::Cubic(c) => PathEl::CurveTo(c.p1, c.p2, c.p3),
967 }
968 }
969
970 pub fn reverse(&self) -> PathSeg {
973 match self {
974 PathSeg::Line(Line { p0, p1 }) => PathSeg::Line(Line::new(*p1, *p0)),
975 PathSeg::Quad(q) => PathSeg::Quad(QuadBez::new(q.p2, q.p1, q.p0)),
976 PathSeg::Cubic(c) => PathSeg::Cubic(CubicBez::new(c.p3, c.p2, c.p1, c.p0)),
977 }
978 }
979
980 pub fn to_cubic(&self) -> CubicBez {
982 match *self {
983 PathSeg::Line(Line { p0, p1 }) => CubicBez::new(p0, p0, p1, p1),
984 PathSeg::Cubic(c) => c,
985 PathSeg::Quad(q) => q.raise(),
986 }
987 }
988
989 fn winding_inner(&self, p: Point) -> i32 {
991 let start = self.start();
992 let end = self.end();
993 let sign = if end.y > start.y {
994 if p.y < start.y || p.y >= end.y {
995 return 0;
996 }
997 -1
998 } else if end.y < start.y {
999 if p.y < end.y || p.y >= start.y {
1000 return 0;
1001 }
1002 1
1003 } else {
1004 return 0;
1005 };
1006 match *self {
1007 PathSeg::Line(_line) => {
1008 if p.x < start.x.min(end.x) {
1009 return 0;
1010 }
1011 if p.x >= start.x.max(end.x) {
1012 return sign;
1013 }
1014 let a = end.y - start.y;
1016 let b = start.x - end.x;
1017 let c = a * start.x + b * start.y;
1018 if (a * p.x + b * p.y - c) * (sign as f64) <= 0.0 {
1019 sign
1020 } else {
1021 0
1022 }
1023 }
1024 PathSeg::Quad(quad) => {
1025 let p1 = quad.p1;
1026 if p.x < start.x.min(end.x).min(p1.x) {
1027 return 0;
1028 }
1029 if p.x >= start.x.max(end.x).max(p1.x) {
1030 return sign;
1031 }
1032 let a = end.y - 2.0 * p1.y + start.y;
1033 let b = 2.0 * (p1.y - start.y);
1034 let c = start.y - p.y;
1035 for t in solve_quadratic(c, b, a) {
1036 if (0.0..=1.0).contains(&t) {
1037 let x = quad.eval(t).x;
1038 if p.x >= x {
1039 return sign;
1040 } else {
1041 return 0;
1042 }
1043 }
1044 }
1045 0
1046 }
1047 PathSeg::Cubic(cubic) => {
1048 let p1 = cubic.p1;
1049 let p2 = cubic.p2;
1050 if p.x < start.x.min(end.x).min(p1.x).min(p2.x) {
1051 return 0;
1052 }
1053 if p.x >= start.x.max(end.x).max(p1.x).max(p2.x) {
1054 return sign;
1055 }
1056 let a = end.y - 3.0 * p2.y + 3.0 * p1.y - start.y;
1057 let b = 3.0 * (p2.y - 2.0 * p1.y + start.y);
1058 let c = 3.0 * (p1.y - start.y);
1059 let d = start.y - p.y;
1060 for t in solve_cubic(d, c, b, a) {
1061 if (0.0..=1.0).contains(&t) {
1062 let x = cubic.eval(t).x;
1063 if p.x >= x {
1064 return sign;
1065 } else {
1066 return 0;
1067 }
1068 }
1069 }
1070 0
1071 }
1072 }
1073 }
1074
1075 fn winding(&self, p: Point) -> i32 {
1079 self.extrema_ranges()
1080 .into_iter()
1081 .map(|range| self.subsegment(range).winding_inner(p))
1082 .sum()
1083 }
1084
1085 pub fn intersect_line(&self, line: Line) -> ArrayVec<LineIntersection, 3> {
1112 const EPSILON: f64 = 1e-9;
1113 let p0 = line.p0;
1114 let p1 = line.p1;
1115 let dx = p1.x - p0.x;
1116 let dy = p1.y - p0.y;
1117 let mut result = ArrayVec::new();
1118 match self {
1119 PathSeg::Line(l) => {
1120 let det = dx * (l.p1.y - l.p0.y) - dy * (l.p1.x - l.p0.x);
1121 if det.abs() < EPSILON {
1122 return result;
1124 }
1125 let t = dx * (p0.y - l.p0.y) - dy * (p0.x - l.p0.x);
1126 let t = t / det;
1128 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1129 let u =
1131 (l.p0.x - p0.x) * (l.p1.y - l.p0.y) - (l.p0.y - p0.y) * (l.p1.x - l.p0.x);
1132 let u = u / det;
1133 if (0.0..=1.0).contains(&u) {
1134 result.push(LineIntersection::new(u, t));
1135 }
1136 }
1137 }
1138 PathSeg::Quad(q) => {
1139 let (px0, px1, px2) = quadratic_bez_coefs(q.p0.x, q.p1.x, q.p2.x);
1144 let (py0, py1, py2) = quadratic_bez_coefs(q.p0.y, q.p1.y, q.p2.y);
1145 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1146 let c1 = dy * px1 - dx * py1;
1147 let c2 = dy * px2 - dx * py2;
1148 let invlen2 = (dx * dx + dy * dy).recip();
1149 for t in solve_quadratic(c0, c1, c2) {
1150 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1151 let x = px0 + t * px1 + t * t * px2;
1152 let y = py0 + t * py1 + t * t * py2;
1153 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1154 if (0.0..=1.0).contains(&u) {
1155 result.push(LineIntersection::new(u, t));
1156 }
1157 }
1158 }
1159 }
1160 PathSeg::Cubic(c) => {
1161 let (px0, px1, px2, px3) = cubic_bez_coefs(c.p0.x, c.p1.x, c.p2.x, c.p3.x);
1163 let (py0, py1, py2, py3) = cubic_bez_coefs(c.p0.y, c.p1.y, c.p2.y, c.p3.y);
1164 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1165 let c1 = dy * px1 - dx * py1;
1166 let c2 = dy * px2 - dx * py2;
1167 let c3 = dy * px3 - dx * py3;
1168 let invlen2 = (dx * dx + dy * dy).recip();
1169 for t in solve_cubic(c0, c1, c2, c3) {
1170 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1171 let x = px0 + t * px1 + t * t * px2 + t * t * t * px3;
1172 let y = py0 + t * py1 + t * t * py2 + t * t * t * py3;
1173 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1174 if (0.0..=1.0).contains(&u) {
1175 result.push(LineIntersection::new(u, t));
1176 }
1177 }
1178 }
1179 }
1180 }
1181 result
1182 }
1183
1184 #[inline]
1186 pub fn is_finite(&self) -> bool {
1187 match self {
1188 PathSeg::Line(line) => line.is_finite(),
1189 PathSeg::Quad(quad_bez) => quad_bez.is_finite(),
1190 PathSeg::Cubic(cubic_bez) => cubic_bez.is_finite(),
1191 }
1192 }
1193
1194 #[inline]
1196 pub fn is_nan(&self) -> bool {
1197 match self {
1198 PathSeg::Line(line) => line.is_nan(),
1199 PathSeg::Quad(quad_bez) => quad_bez.is_nan(),
1200 PathSeg::Cubic(cubic_bez) => cubic_bez.is_nan(),
1201 }
1202 }
1203
1204 #[inline]
1205 fn as_vec2_vec(&self) -> ArrayVec<Vec2, 4> {
1206 let mut a = ArrayVec::new();
1207 match self {
1208 PathSeg::Line(l) => {
1209 a.push(l.p0.to_vec2());
1210 a.push(l.p1.to_vec2());
1211 }
1212 PathSeg::Quad(q) => {
1213 a.push(q.p0.to_vec2());
1214 a.push(q.p1.to_vec2());
1215 a.push(q.p2.to_vec2());
1216 }
1217 PathSeg::Cubic(c) => {
1218 a.push(c.p0.to_vec2());
1219 a.push(c.p1.to_vec2());
1220 a.push(c.p2.to_vec2());
1221 a.push(c.p3.to_vec2());
1222 }
1223 }
1224 a
1225 }
1226
1227 pub fn min_dist(&self, other: PathSeg, accuracy: f64) -> MinDistance {
1233 let (distance, t1, t2) = crate::mindist::min_dist_param(
1234 &self.as_vec2_vec(),
1235 &other.as_vec2_vec(),
1236 (0.0, 1.0),
1237 (0.0, 1.0),
1238 accuracy,
1239 None,
1240 );
1241 MinDistance {
1242 distance: distance.sqrt(),
1243 t1,
1244 t2,
1245 }
1246 }
1247
1248 pub(crate) fn tangents(&self) -> (Vec2, Vec2) {
1252 const EPS: f64 = 1e-12;
1253 match self {
1254 PathSeg::Line(l) => {
1255 let d = l.p1 - l.p0;
1256 (d, d)
1257 }
1258 PathSeg::Quad(q) => {
1259 let d01 = q.p1 - q.p0;
1260 let d0 = if d01.hypot2() > EPS { d01 } else { q.p2 - q.p0 };
1261 let d12 = q.p2 - q.p1;
1262 let d1 = if d12.hypot2() > EPS { d12 } else { q.p2 - q.p0 };
1263 (d0, d1)
1264 }
1265 PathSeg::Cubic(c) => {
1266 let d01 = c.p1 - c.p0;
1267 let d0 = if d01.hypot2() > EPS {
1268 d01
1269 } else {
1270 let d02 = c.p2 - c.p0;
1271 if d02.hypot2() > EPS {
1272 d02
1273 } else {
1274 c.p3 - c.p0
1275 }
1276 };
1277 let d23 = c.p3 - c.p2;
1278 let d1 = if d23.hypot2() > EPS {
1279 d23
1280 } else {
1281 let d13 = c.p3 - c.p1;
1282 if d13.hypot2() > EPS {
1283 d13
1284 } else {
1285 c.p3 - c.p0
1286 }
1287 };
1288 (d0, d1)
1289 }
1290 }
1291 }
1292}
1293
1294impl LineIntersection {
1295 #[inline(always)]
1296 fn new(line_t: f64, segment_t: f64) -> Self {
1297 LineIntersection { line_t, segment_t }
1298 }
1299
1300 #[inline]
1302 pub fn is_finite(self) -> bool {
1303 self.line_t.is_finite() && self.segment_t.is_finite()
1304 }
1305
1306 #[inline]
1308 pub fn is_nan(self) -> bool {
1309 self.line_t.is_nan() || self.segment_t.is_nan()
1310 }
1311}
1312
1313fn quadratic_bez_coefs(x0: f64, x1: f64, x2: f64) -> (f64, f64, f64) {
1315 let p0 = x0;
1316 let p1 = 2.0 * x1 - 2.0 * x0;
1317 let p2 = x2 - 2.0 * x1 + x0;
1318 (p0, p1, p2)
1319}
1320
1321fn cubic_bez_coefs(x0: f64, x1: f64, x2: f64, x3: f64) -> (f64, f64, f64, f64) {
1323 let p0 = x0;
1324 let p1 = 3.0 * x1 - 3.0 * x0;
1325 let p2 = 3.0 * x2 - 6.0 * x1 + 3.0 * x0;
1326 let p3 = x3 - 3.0 * x2 + 3.0 * x1 - x0;
1327 (p0, p1, p2, p3)
1328}
1329
1330impl From<CubicBez> for PathSeg {
1331 #[inline(always)]
1332 fn from(cubic_bez: CubicBez) -> PathSeg {
1333 PathSeg::Cubic(cubic_bez)
1334 }
1335}
1336
1337impl From<Line> for PathSeg {
1338 #[inline(always)]
1339 fn from(line: Line) -> PathSeg {
1340 PathSeg::Line(line)
1341 }
1342}
1343
1344impl From<QuadBez> for PathSeg {
1345 #[inline(always)]
1346 fn from(quad_bez: QuadBez) -> PathSeg {
1347 PathSeg::Quad(quad_bez)
1348 }
1349}
1350
1351impl Shape for BezPath {
1352 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1353
1354 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1355 self.0.iter().copied()
1356 }
1357
1358 fn to_path(&self, _tolerance: f64) -> BezPath {
1359 self.clone()
1360 }
1361
1362 #[inline(always)]
1363 fn into_path(self, _tolerance: f64) -> BezPath {
1364 self
1365 }
1366
1367 fn area(&self) -> f64 {
1369 self.elements().area()
1370 }
1371
1372 fn perimeter(&self, accuracy: f64) -> f64 {
1373 self.elements().perimeter(accuracy)
1374 }
1375
1376 fn winding(&self, pt: Point) -> i32 {
1378 self.elements().winding(pt)
1379 }
1380
1381 fn bounding_box(&self) -> Rect {
1382 self.elements().bounding_box()
1383 }
1384
1385 #[inline(always)]
1386 fn as_path_slice(&self) -> Option<&[PathEl]> {
1387 Some(&self.0)
1388 }
1389}
1390
1391impl PathEl {
1392 #[inline]
1394 pub fn is_finite(&self) -> bool {
1395 match self {
1396 PathEl::MoveTo(p) => p.is_finite(),
1397 PathEl::LineTo(p) => p.is_finite(),
1398 PathEl::QuadTo(p, p2) => p.is_finite() && p2.is_finite(),
1399 PathEl::CurveTo(p, p2, p3) => p.is_finite() && p2.is_finite() && p3.is_finite(),
1400 PathEl::ClosePath => true,
1401 }
1402 }
1403
1404 #[inline]
1406 pub fn is_nan(&self) -> bool {
1407 match self {
1408 PathEl::MoveTo(p) => p.is_nan(),
1409 PathEl::LineTo(p) => p.is_nan(),
1410 PathEl::QuadTo(p, p2) => p.is_nan() || p2.is_nan(),
1411 PathEl::CurveTo(p, p2, p3) => p.is_nan() || p2.is_nan() || p3.is_nan(),
1412 PathEl::ClosePath => false,
1413 }
1414 }
1415
1416 pub fn end_point(&self) -> Option<Point> {
1418 match self {
1419 PathEl::MoveTo(p) => Some(*p),
1420 PathEl::LineTo(p1) => Some(*p1),
1421 PathEl::QuadTo(_, p2) => Some(*p2),
1422 PathEl::CurveTo(_, _, p3) => Some(*p3),
1423 PathEl::ClosePath => None,
1424 }
1425 }
1426}
1427
1428impl<'a> Shape for &'a [PathEl] {
1433 type PathElementsIter<'iter>
1434 = core::iter::Copied<core::slice::Iter<'a, PathEl>>
1435 where
1436 'a: 'iter;
1437
1438 #[inline]
1439 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1440 self.iter().copied()
1441 }
1442
1443 fn to_path(&self, _tolerance: f64) -> BezPath {
1444 BezPath::from_vec(self.to_vec())
1445 }
1446
1447 fn area(&self) -> f64 {
1449 segments(self.iter().copied()).area()
1450 }
1451
1452 fn perimeter(&self, accuracy: f64) -> f64 {
1453 segments(self.iter().copied()).perimeter(accuracy)
1454 }
1455
1456 fn winding(&self, pt: Point) -> i32 {
1458 segments(self.iter().copied()).winding(pt)
1459 }
1460
1461 fn bounding_box(&self) -> Rect {
1462 segments(self.iter().copied()).bounding_box()
1463 }
1464
1465 #[inline(always)]
1466 fn as_path_slice(&self) -> Option<&[PathEl]> {
1467 Some(self)
1468 }
1469}
1470
1471impl<const N: usize> Shape for [PathEl; N] {
1476 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1477
1478 #[inline]
1479 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1480 self.iter().copied()
1481 }
1482
1483 fn to_path(&self, _tolerance: f64) -> BezPath {
1484 BezPath::from_vec(self.to_vec())
1485 }
1486
1487 fn area(&self) -> f64 {
1489 segments(self.iter().copied()).area()
1490 }
1491
1492 fn perimeter(&self, accuracy: f64) -> f64 {
1493 segments(self.iter().copied()).perimeter(accuracy)
1494 }
1495
1496 fn winding(&self, pt: Point) -> i32 {
1498 segments(self.iter().copied()).winding(pt)
1499 }
1500
1501 fn bounding_box(&self) -> Rect {
1502 segments(self.iter().copied()).bounding_box()
1503 }
1504
1505 #[inline(always)]
1506 fn as_path_slice(&self) -> Option<&[PathEl]> {
1507 Some(self)
1508 }
1509}
1510
1511pub struct PathSegIter {
1513 seg: PathSeg,
1514 ix: usize,
1515}
1516
1517impl Shape for PathSeg {
1518 type PathElementsIter<'iter> = PathSegIter;
1519
1520 #[inline(always)]
1521 fn path_elements(&self, _tolerance: f64) -> PathSegIter {
1522 PathSegIter { seg: *self, ix: 0 }
1523 }
1524
1525 fn area(&self) -> f64 {
1529 self.signed_area()
1530 }
1531
1532 #[inline]
1533 fn perimeter(&self, accuracy: f64) -> f64 {
1534 self.arclen(accuracy)
1535 }
1536
1537 #[inline(always)]
1538 fn winding(&self, _pt: Point) -> i32 {
1539 0
1540 }
1541
1542 #[inline]
1543 fn bounding_box(&self) -> Rect {
1544 ParamCurveExtrema::bounding_box(self)
1545 }
1546
1547 fn as_line(&self) -> Option<Line> {
1548 if let PathSeg::Line(line) = self {
1549 Some(*line)
1550 } else {
1551 None
1552 }
1553 }
1554}
1555
1556impl Iterator for PathSegIter {
1557 type Item = PathEl;
1558
1559 fn next(&mut self) -> Option<PathEl> {
1560 self.ix += 1;
1561 match (self.ix, self.seg) {
1562 (1, PathSeg::Line(seg)) => Some(PathEl::MoveTo(seg.p0)),
1564 (1, PathSeg::Quad(seg)) => Some(PathEl::MoveTo(seg.p0)),
1565 (1, PathSeg::Cubic(seg)) => Some(PathEl::MoveTo(seg.p0)),
1566 (2, PathSeg::Line(seg)) => Some(PathEl::LineTo(seg.p1)),
1567 (2, PathSeg::Quad(seg)) => Some(PathEl::QuadTo(seg.p1, seg.p2)),
1568 (2, PathSeg::Cubic(seg)) => Some(PathEl::CurveTo(seg.p1, seg.p2, seg.p3)),
1569 _ => None,
1570 }
1571 }
1572}
1573
1574#[cfg(test)]
1575mod tests {
1576 use crate::{Circle, DEFAULT_ACCURACY};
1577
1578 use super::*;
1579
1580 fn assert_approx_eq(x: f64, y: f64) {
1581 assert!((x - y).abs() < 1e-8, "{x} != {y}");
1582 }
1583
1584 #[test]
1585 #[should_panic(expected = "uninitialized subpath")]
1586 fn test_elements_to_segments_starts_on_closepath() {
1587 let mut path = BezPath::new();
1588 path.close_path();
1589 path.segments().next();
1590 }
1591
1592 #[test]
1593 fn test_elements_to_segments_closepath_refers_to_last_moveto() {
1594 let mut path = BezPath::new();
1595 path.move_to((5.0, 5.0));
1596 path.line_to((15.0, 15.0));
1597 path.move_to((10.0, 10.0));
1598 path.line_to((15.0, 15.0));
1599 path.close_path();
1600 assert_eq!(
1601 path.segments().collect::<Vec<_>>().last(),
1602 Some(&Line::new((15.0, 15.0), (10.0, 10.0)).into()),
1603 );
1604 }
1605
1606 #[test]
1607 #[should_panic(expected = "uninitialized subpath")]
1608 fn test_must_not_start_on_quad() {
1609 let mut path = BezPath::new();
1610 path.quad_to((5.0, 5.0), (10.0, 10.0));
1611 path.line_to((15.0, 15.0));
1612 path.close_path();
1613 }
1614
1615 #[test]
1616 fn test_intersect_line() {
1617 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1618 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1619 let intersection = PathSeg::Line(h_line).intersect_line(v_line)[0];
1620 assert_approx_eq(intersection.segment_t, 0.1);
1621 assert_approx_eq(intersection.line_t, 0.5);
1622
1623 let v_line = Line::new((-10.0, -10.0), (-10.0, 10.0));
1624 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1625
1626 let v_line = Line::new((10.0, 10.0), (10.0, 20.0));
1627 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1628 }
1629
1630 #[test]
1631 fn test_intersect_qad() {
1632 let q = QuadBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -10.0));
1633 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1634 assert_eq!(PathSeg::Quad(q).intersect_line(v_line).len(), 1);
1635 let intersection = PathSeg::Quad(q).intersect_line(v_line)[0];
1636 assert_approx_eq(intersection.segment_t, 0.5);
1637 assert_approx_eq(intersection.line_t, 0.75);
1638
1639 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1640 assert_eq!(PathSeg::Quad(q).intersect_line(h_line).len(), 2);
1641 }
1642
1643 #[test]
1644 fn test_intersect_cubic() {
1645 let c = CubicBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -20.0), (30.0, 10.0));
1646 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1647 assert_eq!(PathSeg::Cubic(c).intersect_line(v_line).len(), 1);
1648 let intersection = PathSeg::Cubic(c).intersect_line(v_line)[0];
1649 assert_approx_eq(intersection.segment_t, 0.333333333);
1650 assert_approx_eq(intersection.line_t, 0.592592592);
1651
1652 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1653 assert_eq!(PathSeg::Cubic(c).intersect_line(h_line).len(), 3);
1654 }
1655
1656 #[test]
1657 fn test_contains() {
1658 let mut path = BezPath::new();
1659 path.move_to((0.0, 0.0));
1660 path.line_to((1.0, 1.0));
1661 path.line_to((2.0, 0.0));
1662 path.close_path();
1663 assert_eq!(path.winding(Point::new(1.0, 0.5)), -1);
1664 assert!(path.contains(Point::new(1.0, 0.5)));
1665 }
1666
1667 #[test]
1669 fn test_get_seg() {
1670 let circle = Circle::new((10.0, 10.0), 2.0).to_path(DEFAULT_ACCURACY);
1671 let segments = circle.path_segments(DEFAULT_ACCURACY).collect::<Vec<_>>();
1672 let get_segs = (1..usize::MAX)
1673 .map_while(|i| circle.get_seg(i))
1674 .collect::<Vec<_>>();
1675 assert_eq!(segments, get_segs);
1676 }
1677
1678 #[test]
1679 fn test_control_box() {
1680 let path = BezPath::from_svg("M200,300 C50,50 350,50 200,300").unwrap();
1683 assert_eq!(Rect::new(50.0, 50.0, 350.0, 300.0), path.control_box());
1684 assert!(path.control_box().area() > path.bounding_box().area());
1685 }
1686
1687 #[test]
1688 fn test_reverse_unclosed() {
1689 let path = BezPath::from_svg("M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60").unwrap();
1690 let reversed = path.reverse_subpaths();
1691 assert_eq!(
1692 "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10",
1693 reversed.to_svg()
1694 );
1695 }
1696
1697 #[test]
1698 fn test_reverse_closed_triangle() {
1699 let path = BezPath::from_svg("M100,100 L150,200 L50,200 Z").unwrap();
1700 let reversed = path.reverse_subpaths();
1701 assert_eq!("M50,200 L150,200 L100,100 Z", reversed.to_svg());
1702 }
1703
1704 #[test]
1705 fn test_reverse_closed_shape() {
1706 let path = BezPath::from_svg(
1707 "M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z",
1708 )
1709 .unwrap();
1710 let reversed = path.reverse_subpaths();
1711 assert_eq!(
1712 "M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z",
1713 reversed.to_svg()
1714 );
1715 }
1716
1717 #[test]
1718 fn test_reverse_multiple_subpaths() {
1719 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";
1720 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";
1721 let path = BezPath::from_svg(svg).unwrap();
1722 let reversed = path.reverse_subpaths();
1723 assert_eq!(expected_svg, reversed.to_svg());
1724 }
1725
1726 #[test]
1728 fn test_reverse_lines() {
1729 let mut path = BezPath::new();
1730 path.move_to((0.0, 0.0));
1731 path.line_to((1.0, 1.0));
1732 path.line_to((2.0, 2.0));
1733 path.line_to((3.0, 3.0));
1734 path.close_path();
1735 let rev = path.reverse_subpaths();
1736 assert_eq!("M3,3 L2,2 L1,1 L0,0 Z", rev.to_svg());
1737 }
1738
1739 #[test]
1740 fn test_reverse_multiple_moves() {
1741 reverse_test_helper(
1742 vec![
1743 PathEl::MoveTo((2.0, 2.0).into()),
1744 PathEl::MoveTo((3.0, 3.0).into()),
1745 PathEl::ClosePath,
1746 PathEl::MoveTo((4.0, 4.0).into()),
1747 ],
1748 vec![
1749 PathEl::MoveTo((2.0, 2.0).into()),
1750 PathEl::MoveTo((3.0, 3.0).into()),
1751 PathEl::ClosePath,
1752 PathEl::MoveTo((4.0, 4.0).into()),
1753 ],
1754 );
1755 }
1756
1757 #[test]
1764 fn test_reverse_closed_last_line_not_on_move() {
1765 reverse_test_helper(
1766 vec![
1767 PathEl::MoveTo((0.0, 0.0).into()),
1768 PathEl::LineTo((1.0, 1.0).into()),
1769 PathEl::LineTo((2.0, 2.0).into()),
1770 PathEl::LineTo((3.0, 3.0).into()),
1771 PathEl::ClosePath,
1772 ],
1773 vec![
1774 PathEl::MoveTo((3.0, 3.0).into()),
1775 PathEl::LineTo((2.0, 2.0).into()),
1776 PathEl::LineTo((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_line_overlaps_move() {
1785 reverse_test_helper(
1786 vec![
1787 PathEl::MoveTo((0.0, 0.0).into()),
1788 PathEl::LineTo((1.0, 1.0).into()),
1789 PathEl::LineTo((2.0, 2.0).into()),
1790 PathEl::LineTo((0.0, 0.0).into()),
1791 PathEl::ClosePath,
1792 ],
1793 vec![
1794 PathEl::MoveTo((0.0, 0.0).into()),
1795 PathEl::LineTo((2.0, 2.0).into()),
1796 PathEl::LineTo((1.0, 1.0).into()),
1797 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1799 ],
1800 );
1801 }
1802
1803 #[test]
1804 fn test_reverse_closed_duplicate_line_following_move() {
1805 reverse_test_helper(
1806 vec![
1807 PathEl::MoveTo((0.0, 0.0).into()),
1808 PathEl::LineTo((0.0, 0.0).into()),
1809 PathEl::LineTo((1.0, 1.0).into()),
1810 PathEl::LineTo((2.0, 2.0).into()),
1811 PathEl::ClosePath,
1812 ],
1813 vec![
1814 PathEl::MoveTo((2.0, 2.0).into()),
1815 PathEl::LineTo((1.0, 1.0).into()),
1816 PathEl::LineTo((0.0, 0.0).into()), PathEl::LineTo((0.0, 0.0).into()),
1818 PathEl::ClosePath,
1819 ],
1820 );
1821 }
1822
1823 #[test]
1824 fn test_reverse_closed_two_lines() {
1825 reverse_test_helper(
1826 vec![
1827 PathEl::MoveTo((0.0, 0.0).into()),
1828 PathEl::LineTo((1.0, 1.0).into()),
1829 PathEl::ClosePath,
1830 ],
1831 vec![
1832 PathEl::MoveTo((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_curve_overlaps_move() {
1841 reverse_test_helper(
1842 vec![
1843 PathEl::MoveTo((0.0, 0.0).into()),
1844 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1845 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (0.0, 0.0).into()),
1846 PathEl::ClosePath,
1847 ],
1848 vec![
1849 PathEl::MoveTo((0.0, 0.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1851 PathEl::CurveTo((2.0, 2.0).into(), (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_curve_not_on_move() {
1859 reverse_test_helper(
1860 vec![
1861 PathEl::MoveTo((0.0, 0.0).into()),
1862 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1863 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (6.0, 6.0).into()),
1864 PathEl::ClosePath,
1865 ],
1866 vec![
1867 PathEl::MoveTo((6.0, 6.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1869 PathEl::CurveTo((2.0, 2.0).into(), (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_curve_line() {
1877 reverse_test_helper(
1878 vec![
1879 PathEl::MoveTo((0.0, 0.0).into()),
1880 PathEl::LineTo((1.0, 1.0).into()), PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
1882 PathEl::CurveTo((5.0, 5.0).into(), (6.0, 6.0).into(), (7.0, 7.0).into()),
1883 PathEl::ClosePath,
1884 ],
1885 vec![
1886 PathEl::MoveTo((7.0, 7.0).into()),
1887 PathEl::CurveTo((6.0, 6.0).into(), (5.0, 5.0).into(), (4.0, 4.0).into()),
1888 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
1889 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1891 ],
1892 );
1893 }
1894
1895 #[test]
1896 fn test_reverse_closed_last_quad_overlaps_move() {
1897 reverse_test_helper(
1898 vec![
1899 PathEl::MoveTo((0.0, 0.0).into()),
1900 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1901 PathEl::QuadTo((3.0, 3.0).into(), (0.0, 0.0).into()),
1902 PathEl::ClosePath,
1903 ],
1904 vec![
1905 PathEl::MoveTo((0.0, 0.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1907 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1908 PathEl::ClosePath,
1909 ],
1910 );
1911 }
1912
1913 #[test]
1914 fn test_reverse_closed_last_quad_not_on_move() {
1915 reverse_test_helper(
1916 vec![
1917 PathEl::MoveTo((0.0, 0.0).into()),
1918 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1919 PathEl::QuadTo((3.0, 3.0).into(), (4.0, 4.0).into()),
1920 PathEl::ClosePath,
1921 ],
1922 vec![
1923 PathEl::MoveTo((4.0, 4.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1925 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1926 PathEl::ClosePath,
1927 ],
1928 );
1929 }
1930
1931 #[test]
1932 fn test_reverse_closed_line_quad_line() {
1933 reverse_test_helper(
1934 vec![
1935 PathEl::MoveTo((0.0, 0.0).into()),
1936 PathEl::LineTo((1.0, 1.0).into()), PathEl::QuadTo((2.0, 2.0).into(), (3.0, 3.0).into()),
1938 PathEl::ClosePath,
1939 ],
1940 vec![
1941 PathEl::MoveTo((3.0, 3.0).into()),
1942 PathEl::QuadTo((2.0, 2.0).into(), (1.0, 1.0).into()),
1943 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1945 ],
1946 );
1947 }
1948
1949 #[test]
1950 fn test_reverse_empty() {
1951 reverse_test_helper(vec![], vec![]);
1952 }
1953
1954 #[test]
1955 fn test_reverse_single_point() {
1956 reverse_test_helper(
1957 vec![PathEl::MoveTo((0.0, 0.0).into())],
1958 vec![PathEl::MoveTo((0.0, 0.0).into())],
1959 );
1960 }
1961
1962 #[test]
1963 fn test_reverse_single_point_closed() {
1964 reverse_test_helper(
1965 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
1966 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
1967 );
1968 }
1969
1970 #[test]
1971 fn test_reverse_single_line_open() {
1972 reverse_test_helper(
1973 vec![
1974 PathEl::MoveTo((0.0, 0.0).into()),
1975 PathEl::LineTo((1.0, 1.0).into()),
1976 ],
1977 vec![
1978 PathEl::MoveTo((1.0, 1.0).into()),
1979 PathEl::LineTo((0.0, 0.0).into()),
1980 ],
1981 );
1982 }
1983
1984 #[test]
1985 fn test_reverse_single_curve_open() {
1986 reverse_test_helper(
1987 vec![
1988 PathEl::MoveTo((0.0, 0.0).into()),
1989 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1990 ],
1991 vec![
1992 PathEl::MoveTo((3.0, 3.0).into()),
1993 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1994 ],
1995 );
1996 }
1997
1998 #[test]
1999 fn test_reverse_curve_line_open() {
2000 reverse_test_helper(
2001 vec![
2002 PathEl::MoveTo((0.0, 0.0).into()),
2003 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
2004 PathEl::LineTo((4.0, 4.0).into()),
2005 ],
2006 vec![
2007 PathEl::MoveTo((4.0, 4.0).into()),
2008 PathEl::LineTo((3.0, 3.0).into()),
2009 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
2010 ],
2011 );
2012 }
2013
2014 #[test]
2015 fn test_reverse_line_curve_open() {
2016 reverse_test_helper(
2017 vec![
2018 PathEl::MoveTo((0.0, 0.0).into()),
2019 PathEl::LineTo((1.0, 1.0).into()),
2020 PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
2021 ],
2022 vec![
2023 PathEl::MoveTo((4.0, 4.0).into()),
2024 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
2025 PathEl::LineTo((0.0, 0.0).into()),
2026 ],
2027 );
2028 }
2029
2030 #[test]
2031 fn test_reverse_duplicate_point_after_move() {
2032 reverse_test_helper(
2035 vec![
2036 PathEl::MoveTo((848.0, 348.0).into()),
2037 PathEl::LineTo((848.0, 348.0).into()),
2038 PathEl::QuadTo((848.0, 526.0).into(), (449.0, 704.0).into()),
2039 PathEl::QuadTo((848.0, 171.0).into(), (848.0, 348.0).into()),
2040 PathEl::ClosePath,
2041 ],
2042 vec![
2043 PathEl::MoveTo((848.0, 348.0).into()),
2044 PathEl::QuadTo((848.0, 171.0).into(), (449.0, 704.0).into()),
2045 PathEl::QuadTo((848.0, 526.0).into(), (848.0, 348.0).into()),
2046 PathEl::LineTo((848.0, 348.0).into()),
2047 PathEl::ClosePath,
2048 ],
2049 );
2050 }
2051
2052 #[test]
2053 fn test_reverse_duplicate_point_at_end() {
2054 reverse_test_helper(
2056 vec![
2057 PathEl::MoveTo((0.0, 651.0).into()),
2058 PathEl::LineTo((0.0, 101.0).into()),
2059 PathEl::LineTo((0.0, 101.0).into()),
2060 PathEl::LineTo((0.0, 651.0).into()),
2061 PathEl::LineTo((0.0, 651.0).into()),
2062 PathEl::ClosePath,
2063 ],
2064 vec![
2065 PathEl::MoveTo((0.0, 651.0).into()),
2066 PathEl::LineTo((0.0, 651.0).into()),
2067 PathEl::LineTo((0.0, 101.0).into()),
2068 PathEl::LineTo((0.0, 101.0).into()),
2069 PathEl::LineTo((0.0, 651.0).into()),
2070 PathEl::ClosePath,
2071 ],
2072 );
2073 }
2074
2075 fn reverse_test_helper(contour: Vec<PathEl>, expected: Vec<PathEl>) {
2076 assert_eq!(BezPath(contour).reverse_subpaths().0, expected);
2077 }
2078
2079 #[test]
2080 fn test_rect_segments() {
2081 let x0 = 25.189500810000002;
2084 let x1 = 568.18950081;
2085 let y0 = -105.0;
2086 let y1 = 176.0;
2087 let r = Rect::from_points((x0, y0), (x1, y1));
2088
2089 let path0 = r.into_path(0.0);
2090 assert!(path0
2091 .elements()
2092 .iter()
2093 .skip(1)
2094 .all(|el| !matches!(el, PathEl::MoveTo(_))));
2095
2096 let path1 = BezPath::from_path_segments(path0.segments());
2097 assert!(path1
2098 .elements()
2099 .iter()
2100 .skip(1)
2101 .all(|el| !matches!(el, PathEl::MoveTo(_))));
2102 }
2103
2104 #[test]
2105 fn test_current_position() {
2106 let mut path = BezPath::new();
2107 assert_eq!(path.current_position(), None);
2108 path.move_to((0., 0.));
2109 assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2110 path.line_to((10., 10.));
2111 assert_eq!(path.current_position(), Some(Point::new(10., 10.)));
2112 path.line_to((10., 0.));
2113 assert_eq!(path.current_position(), Some(Point::new(10., 0.)));
2114 path.close_path();
2115 assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2116
2117 path.close_path();
2118 assert_eq!(path.current_position(), None);
2119
2120 path.move_to((0., 10.));
2121 assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2122 path.close_path();
2123 assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2124 path.close_path();
2125 assert_eq!(path.current_position(), None);
2126 }
2127}