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