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