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::MAX_EXTREMA;
17use crate::common::{solve_cubic, solve_quadratic};
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),
124 CurveTo(Point, Point, Point),
128 ClosePath,
130}
131
132#[derive(Clone, Copy, Debug, PartialEq)]
134#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub enum PathSeg {
137 Line(Line),
139 Quad(QuadBez),
141 Cubic(CubicBez),
143}
144
145#[derive(Debug, Clone, Copy)]
149pub struct LineIntersection {
150 pub line_t: f64,
154
155 pub segment_t: f64,
160}
161
162pub struct MinDistance {
164 pub distance: f64,
166 pub t1: f64,
172 pub t2: f64,
178}
179
180impl BezPath {
181 #[inline(always)]
183 pub fn new() -> BezPath {
184 BezPath::default()
185 }
186
187 pub fn with_capacity(capacity: usize) -> BezPath {
192 BezPath(Vec::with_capacity(capacity))
193 }
194
195 pub fn from_vec(v: Vec<PathEl>) -> BezPath {
208 debug_assert!(
209 v.is_empty() || matches!(v.first(), Some(PathEl::MoveTo(_))),
210 "BezPath must begin with MoveTo"
211 );
212 BezPath(v)
213 }
214
215 pub fn pop(&mut self) -> Option<PathEl> {
217 self.0.pop()
218 }
219
220 pub fn push(&mut self, el: PathEl) {
222 self.0.push(el);
223 debug_assert!(
224 matches!(self.0.first(), Some(PathEl::MoveTo(_))),
225 "BezPath must begin with MoveTo"
226 );
227 }
228
229 pub fn move_to<P: Into<Point>>(&mut self, p: P) {
231 self.push(PathEl::MoveTo(p.into()));
232 }
233
234 pub fn line_to<P: Into<Point>>(&mut self, p: P) {
242 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
243 self.push(PathEl::LineTo(p.into()));
244 }
245
246 pub fn quad_to<P: Into<Point>>(&mut self, p1: P, p2: P) {
254 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
255 self.push(PathEl::QuadTo(p1.into(), p2.into()));
256 }
257
258 pub fn curve_to<P: Into<Point>>(&mut self, p1: P, p2: P, p3: P) {
266 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
267 self.push(PathEl::CurveTo(p1.into(), p2.into(), p3.into()));
268 }
269
270 pub fn close_path(&mut self) {
275 debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
276 self.push(PathEl::ClosePath);
277 }
278
279 #[inline(always)]
281 pub fn into_elements(self) -> Vec<PathEl> {
282 self.0
283 }
284
285 #[inline(always)]
291 pub fn elements(&self) -> &[PathEl] {
292 &self.0
293 }
294
295 #[inline(always)]
297 pub fn elements_mut(&mut self) -> &mut [PathEl] {
298 &mut self.0
299 }
300
301 pub fn iter(&self) -> impl Iterator<Item = PathEl> + Clone + '_ {
303 self.0.iter().copied()
304 }
305
306 pub fn segments(&self) -> impl Iterator<Item = PathSeg> + Clone + '_ {
308 segments(self.iter())
309 }
310
311 pub fn truncate(&mut self, len: usize) {
313 self.0.truncate(len);
314 }
315
316 pub fn get_seg(&self, ix: usize) -> Option<PathSeg> {
327 if ix == 0 || ix >= self.0.len() {
328 return None;
329 }
330 let last = match self.0[ix - 1] {
331 PathEl::MoveTo(p) => p,
332 PathEl::LineTo(p) => p,
333 PathEl::QuadTo(_, p2) => p2,
334 PathEl::CurveTo(_, _, p3) => p3,
335 PathEl::ClosePath => return None,
336 };
337 match self.0[ix] {
338 PathEl::LineTo(p) => Some(PathSeg::Line(Line::new(last, p))),
339 PathEl::QuadTo(p1, p2) => Some(PathSeg::Quad(QuadBez::new(last, p1, p2))),
340 PathEl::CurveTo(p1, p2, p3) => Some(PathSeg::Cubic(CubicBez::new(last, p1, p2, p3))),
341 PathEl::ClosePath => self.0[..ix].iter().rev().find_map(|el| match *el {
342 PathEl::MoveTo(start) if start != last => {
343 Some(PathSeg::Line(Line::new(last, start)))
344 }
345 _ => None,
346 }),
347 PathEl::MoveTo(_) => None,
348 }
349 }
350
351 pub fn is_empty(&self) -> bool {
353 self.0
354 .iter()
355 .all(|el| matches!(el, PathEl::MoveTo(..) | PathEl::ClosePath))
356 }
357
358 pub fn apply_affine(&mut self, affine: Affine) {
360 for el in self.0.iter_mut() {
361 *el = affine * (*el);
362 }
363 }
364
365 #[inline]
367 pub fn is_finite(&self) -> bool {
368 self.0.iter().all(|v| v.is_finite())
369 }
370
371 #[inline]
373 pub fn is_nan(&self) -> bool {
374 self.0.iter().any(|v| v.is_nan())
375 }
376
377 pub fn control_box(&self) -> Rect {
382 let mut cbox: Option<Rect> = None;
383 let mut add_pts = |pts: &[Point]| {
384 for pt in pts {
385 cbox = match cbox {
386 Some(cbox) => Some(cbox.union_pt(*pt)),
387 _ => Some(Rect::from_points(*pt, *pt)),
388 };
389 }
390 };
391 for &el in self.elements() {
392 match el {
393 PathEl::MoveTo(p0) | PathEl::LineTo(p0) => add_pts(&[p0]),
394 PathEl::QuadTo(p0, p1) => add_pts(&[p0, p1]),
395 PathEl::CurveTo(p0, p1, p2) => add_pts(&[p0, p1, p2]),
396 PathEl::ClosePath => {}
397 }
398 }
399 cbox.unwrap_or_default()
400 }
401
402 pub fn current_position(&self) -> Option<Point> {
408 match self.0.last()? {
409 PathEl::MoveTo(p) => Some(*p),
410 PathEl::LineTo(p1) => Some(*p1),
411 PathEl::QuadTo(_, p2) => Some(*p2),
412 PathEl::CurveTo(_, _, p3) => Some(*p3),
413 PathEl::ClosePath => self
414 .elements()
415 .iter()
416 .rev()
417 .skip(1)
418 .take_while(|el| !matches!(el, PathEl::ClosePath))
419 .last()
420 .and_then(|el| el.end_point()),
421 }
422 }
423
424 pub fn subpaths(&self) -> impl Iterator<Item = &[PathEl]> {
426 let elements = self.elements();
427 let mut i = 0;
428
429 core::iter::from_fn(move || {
430 if i >= elements.len() {
431 return None;
432 }
433 let start = i;
434 i += 1;
435 while i < elements.len() && !matches!(elements[i], PathEl::MoveTo(_)) {
436 i += 1;
437 }
438 Some(&elements[start..i])
439 })
440 }
441
442 pub fn reverse_subpaths(&self) -> BezPath {
444 let elements = self.elements();
445 let mut start_ix = 1;
446 let mut start_pt = Point::default();
447 let mut reversed = BezPath(Vec::with_capacity(elements.len()));
448 let mut pending_move = false;
451 for (ix, el) in elements.iter().enumerate() {
452 match el {
453 PathEl::MoveTo(pt) => {
454 if pending_move {
455 reversed.push(PathEl::MoveTo(start_pt));
456 }
457 if start_ix < ix {
458 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
459 }
460 pending_move = true;
461 start_pt = *pt;
462 start_ix = ix + 1;
463 }
464 PathEl::ClosePath => {
465 if start_ix <= ix {
466 reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
467 }
468 reversed.push(PathEl::ClosePath);
469 start_ix = ix + 1;
470 pending_move = false;
471 }
472 _ => {
473 pending_move = false;
474 }
475 }
476 }
477 if start_ix < elements.len() {
478 reverse_subpath(start_pt, &elements[start_ix..], &mut reversed);
479 } else if pending_move {
480 reversed.push(PathEl::MoveTo(start_pt));
481 }
482 reversed
483 }
484}
485
486fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) {
490 let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt);
491 reversed.push(PathEl::MoveTo(end_pt));
492 for (ix, el) in els.iter().enumerate().rev() {
493 let end_pt = if ix > 0 {
494 els[ix - 1].end_point().unwrap()
495 } else {
496 start_pt
497 };
498 match el {
499 PathEl::LineTo(_) => reversed.push(PathEl::LineTo(end_pt)),
500 PathEl::QuadTo(c0, _) => reversed.push(PathEl::QuadTo(*c0, end_pt)),
501 PathEl::CurveTo(c0, c1, _) => reversed.push(PathEl::CurveTo(*c1, *c0, end_pt)),
502 _ => panic!("reverse_subpath expects MoveTo and ClosePath to be removed"),
503 }
504 }
505}
506
507impl FromIterator<PathEl> for BezPath {
508 fn from_iter<T: IntoIterator<Item = PathEl>>(iter: T) -> Self {
509 let el_vec: Vec<_> = iter.into_iter().collect();
510 BezPath::from_vec(el_vec)
511 }
512}
513
514impl<'a> IntoIterator for &'a BezPath {
519 type Item = PathEl;
520 type IntoIter = core::iter::Cloned<core::slice::Iter<'a, PathEl>>;
521
522 fn into_iter(self) -> Self::IntoIter {
523 self.elements().iter().cloned()
524 }
525}
526
527impl IntoIterator for BezPath {
528 type Item = PathEl;
529 type IntoIter = alloc::vec::IntoIter<PathEl>;
530
531 fn into_iter(self) -> Self::IntoIter {
532 self.0.into_iter()
533 }
534}
535
536impl Extend<PathEl> for BezPath {
537 fn extend<I: IntoIterator<Item = PathEl>>(&mut self, iter: I) {
550 self.0.extend(iter);
551 }
552}
553
554const TO_QUAD_TOL: f64 = 0.1;
556
557pub fn flatten(
623 path: impl IntoIterator<Item = PathEl>,
624 tolerance: f64,
625 mut callback: impl FnMut(PathEl),
626) {
627 let sqrt_tol = tolerance.sqrt();
628 let mut last_pt = None;
629 let mut quad_buf = Vec::new();
630 for el in path {
631 match el {
632 PathEl::MoveTo(p) => {
633 last_pt = Some(p);
634 callback(PathEl::MoveTo(p));
635 }
636 PathEl::LineTo(p) => {
637 last_pt = Some(p);
638 callback(PathEl::LineTo(p));
639 }
640 PathEl::QuadTo(p1, p2) => {
641 if let Some(p0) = last_pt {
642 let q = QuadBez::new(p0, p1, p2);
643 let params = q.estimate_subdiv(sqrt_tol);
644 let n = ((0.5 * params.val / sqrt_tol).ceil() as usize).max(1);
645 let step = 1.0 / (n as f64);
646 for i in 1..n {
647 let u = (i as f64) * step;
648 let t = q.determine_subdiv_t(¶ms, u);
649 let p = q.eval(t);
650 callback(PathEl::LineTo(p));
651 }
652 callback(PathEl::LineTo(p2));
653 }
654 last_pt = Some(p2);
655 }
656 PathEl::CurveTo(p1, p2, p3) => {
657 if let Some(p0) = last_pt {
658 let c = CubicBez::new(p0, p1, p2, p3);
659
660 let iter = c.to_quads(tolerance * TO_QUAD_TOL);
665 quad_buf.clear();
666 quad_buf.reserve(iter.size_hint().0);
667 let sqrt_remain_tol = sqrt_tol * (1.0 - TO_QUAD_TOL).sqrt();
668 let mut sum = 0.0;
669 for (_, _, q) in iter {
670 let params = q.estimate_subdiv(sqrt_remain_tol);
671 sum += params.val;
672 quad_buf.push((q, params));
673 }
674 let n = ((0.5 * sum / sqrt_remain_tol).ceil() as usize).max(1);
675
676 let step = sum / (n as f64);
679 let mut i = 1;
680 let mut val_sum = 0.0;
681 for (q, params) in &quad_buf {
682 let mut target = (i as f64) * step;
683 let recip_val = params.val.recip();
684 while target < val_sum + params.val {
685 let u = (target - val_sum) * recip_val;
686 let t = q.determine_subdiv_t(params, u);
687 let p = q.eval(t);
688 callback(PathEl::LineTo(p));
689 i += 1;
690 if i == n + 1 {
691 break;
692 }
693 target = (i as f64) * step;
694 }
695 val_sum += params.val;
696 }
697 callback(PathEl::LineTo(p3));
698 }
699 last_pt = Some(p3);
700 }
701 PathEl::ClosePath => {
702 last_pt = None;
703 callback(PathEl::ClosePath);
704 }
705 }
706 }
707}
708
709impl Mul<PathEl> for Affine {
710 type Output = PathEl;
711
712 #[inline(always)]
715 fn mul(self, other: PathEl) -> PathEl {
716 match other {
717 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
718 PathEl::LineTo(p) => PathEl::LineTo(self * p),
719 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
720 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
721 PathEl::ClosePath => PathEl::ClosePath,
722 }
723 }
724}
725
726impl Mul<PathSeg> for Affine {
727 type Output = PathSeg;
728
729 fn mul(self, other: PathSeg) -> PathSeg {
730 match other {
731 PathSeg::Line(line) => PathSeg::Line(self * line),
732 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
733 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
734 }
735 }
736}
737
738impl Mul<BezPath> for Affine {
739 type Output = BezPath;
740
741 fn mul(self, other: BezPath) -> BezPath {
742 BezPath(other.0.iter().map(|&el| self * el).collect())
743 }
744}
745
746impl Mul<&BezPath> for Affine {
747 type Output = BezPath;
748
749 fn mul(self, other: &BezPath) -> BezPath {
750 BezPath(other.0.iter().map(|&el| self * el).collect())
751 }
752}
753
754impl Mul<PathEl> for TranslateScale {
755 type Output = PathEl;
756
757 fn mul(self, other: PathEl) -> PathEl {
758 match other {
759 PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
760 PathEl::LineTo(p) => PathEl::LineTo(self * p),
761 PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
762 PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
763 PathEl::ClosePath => PathEl::ClosePath,
764 }
765 }
766}
767
768impl Mul<PathSeg> for TranslateScale {
769 type Output = PathSeg;
770
771 fn mul(self, other: PathSeg) -> PathSeg {
772 match other {
773 PathSeg::Line(line) => PathSeg::Line(self * line),
774 PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
775 PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
776 }
777 }
778}
779
780impl Mul<BezPath> for TranslateScale {
781 type Output = BezPath;
782
783 fn mul(self, other: BezPath) -> BezPath {
784 BezPath(other.0.iter().map(|&el| self * el).collect())
785 }
786}
787
788impl Mul<&BezPath> for TranslateScale {
789 type Output = BezPath;
790
791 fn mul(self, other: &BezPath) -> BezPath {
792 BezPath(other.0.iter().map(|&el| self * el).collect())
793 }
794}
795
796pub(crate) fn close_subpaths<I>(elements: I) -> CloseSubpaths<I::IntoIter>
798where
799 I: IntoIterator<Item = PathEl>,
800{
801 CloseSubpaths {
802 elements: elements.into_iter(),
803 state: CloseSubpathState::Start,
804 }
805}
806
807#[derive(Clone)]
811pub(crate) struct CloseSubpaths<I: Iterator<Item = PathEl>> {
812 elements: I,
813 state: CloseSubpathState,
814}
815
816#[derive(Clone)]
817enum CloseSubpathState {
818 Start,
819 InSubpath,
820 ClosedLast,
821 PendingMoveTo(Point),
822}
823
824impl<I: Iterator<Item = PathEl>> Iterator for CloseSubpaths<I> {
825 type Item = PathEl;
826
827 fn next(&mut self) -> Option<PathEl> {
828 match self.state {
829 CloseSubpathState::Start => {
830 let el = self.elements.next();
831 if !matches!(el, Some(PathEl::ClosePath) | None) {
832 self.state = CloseSubpathState::InSubpath;
833 }
834 el
835 }
836 CloseSubpathState::InSubpath => {
837 let el = self.elements.next();
838 match el {
839 None => {
840 self.state = CloseSubpathState::ClosedLast;
841 Some(PathEl::ClosePath)
842 }
843 Some(PathEl::MoveTo(point)) => {
844 self.state = CloseSubpathState::PendingMoveTo(point);
845 Some(PathEl::ClosePath)
846 }
847 Some(PathEl::ClosePath) => {
848 self.state = CloseSubpathState::Start;
849 el
850 }
851 _ => el,
852 }
853 }
854 CloseSubpathState::ClosedLast => None,
855 CloseSubpathState::PendingMoveTo(point) => {
856 self.state = CloseSubpathState::Start;
857 Some(PathEl::MoveTo(point))
858 }
859 }
860 }
861}
862
863pub fn segments<I>(elements: I) -> Segments<I::IntoIter>
870where
871 I: IntoIterator<Item = PathEl>,
872{
873 Segments {
874 elements: elements.into_iter(),
875 start_last: None,
876 }
877}
878
879#[derive(Clone)]
883pub struct Segments<I: Iterator<Item = PathEl>> {
884 elements: I,
885 start_last: Option<(Point, Point)>,
886}
887
888impl<I: Iterator<Item = PathEl>> Iterator for Segments<I> {
889 type Item = PathSeg;
890
891 #[inline]
892 fn next(&mut self) -> Option<PathSeg> {
893 for el in &mut self.elements {
894 let (start, last) = self.start_last.get_or_insert_with(|| {
897 let point = match el {
898 PathEl::MoveTo(p) => p,
899 PathEl::LineTo(p) => p,
900 PathEl::QuadTo(_, p2) => p2,
901 PathEl::CurveTo(_, _, p3) => p3,
902 PathEl::ClosePath => panic!("Can't start a segment on a ClosePath"),
903 };
904 (point, point)
905 });
906
907 return Some(match el {
908 PathEl::MoveTo(p) => {
909 *start = p;
910 *last = p;
911 continue;
912 }
913 PathEl::LineTo(p) => PathSeg::Line(Line::new(mem::replace(last, p), p)),
914 PathEl::QuadTo(p1, p2) => {
915 PathSeg::Quad(QuadBez::new(mem::replace(last, p2), p1, p2))
916 }
917 PathEl::CurveTo(p1, p2, p3) => {
918 PathSeg::Cubic(CubicBez::new(mem::replace(last, p3), p1, p2, p3))
919 }
920 PathEl::ClosePath => {
921 if *last != *start {
922 PathSeg::Line(Line::new(mem::replace(last, *start), *start))
923 } else {
924 continue;
925 }
926 }
927 });
928 }
929
930 None
931 }
932}
933
934impl<I: Iterator<Item = PathEl>> Segments<I> {
935 pub(crate) fn perimeter(self, accuracy: f64) -> f64 {
939 self.map(|seg| seg.arclen(accuracy)).sum()
940 }
941
942 pub(crate) fn area(self) -> f64 {
944 self.map(|seg| seg.signed_area()).sum()
945 }
946
947 pub(crate) fn winding(self, p: Point) -> i32 {
949 self.map(|seg| seg.winding(p)).sum()
950 }
951
952 pub(crate) fn bounding_box(self) -> Rect {
954 let mut bbox: Option<Rect> = None;
955 for seg in self {
956 let seg_bb = ParamCurveExtrema::bounding_box(&seg);
957 if let Some(bb) = bbox {
958 bbox = Some(bb.union(seg_bb));
959 } else {
960 bbox = Some(seg_bb);
961 }
962 }
963 bbox.unwrap_or_default()
964 }
965}
966
967impl ParamCurve for PathSeg {
968 fn eval(&self, t: f64) -> Point {
969 match *self {
970 PathSeg::Line(line) => line.eval(t),
971 PathSeg::Quad(quad) => quad.eval(t),
972 PathSeg::Cubic(cubic) => cubic.eval(t),
973 }
974 }
975
976 fn subsegment(&self, range: Range<f64>) -> PathSeg {
977 match *self {
978 PathSeg::Line(line) => PathSeg::Line(line.subsegment(range)),
979 PathSeg::Quad(quad) => PathSeg::Quad(quad.subsegment(range)),
980 PathSeg::Cubic(cubic) => PathSeg::Cubic(cubic.subsegment(range)),
981 }
982 }
983
984 fn start(&self) -> Point {
985 match *self {
986 PathSeg::Line(line) => line.start(),
987 PathSeg::Quad(quad) => quad.start(),
988 PathSeg::Cubic(cubic) => cubic.start(),
989 }
990 }
991
992 fn end(&self) -> Point {
993 match *self {
994 PathSeg::Line(line) => line.end(),
995 PathSeg::Quad(quad) => quad.end(),
996 PathSeg::Cubic(cubic) => cubic.end(),
997 }
998 }
999}
1000
1001impl ParamCurveArclen for PathSeg {
1002 fn arclen(&self, accuracy: f64) -> f64 {
1003 match *self {
1004 PathSeg::Line(line) => line.arclen(accuracy),
1005 PathSeg::Quad(quad) => quad.arclen(accuracy),
1006 PathSeg::Cubic(cubic) => cubic.arclen(accuracy),
1007 }
1008 }
1009
1010 fn inv_arclen(&self, arclen: f64, accuracy: f64) -> f64 {
1011 match *self {
1012 PathSeg::Line(line) => line.inv_arclen(arclen, accuracy),
1013 PathSeg::Quad(quad) => quad.inv_arclen(arclen, accuracy),
1014 PathSeg::Cubic(cubic) => cubic.inv_arclen(arclen, accuracy),
1015 }
1016 }
1017}
1018
1019impl ParamCurveArea for PathSeg {
1020 fn signed_area(&self) -> f64 {
1021 match *self {
1022 PathSeg::Line(line) => line.signed_area(),
1023 PathSeg::Quad(quad) => quad.signed_area(),
1024 PathSeg::Cubic(cubic) => cubic.signed_area(),
1025 }
1026 }
1027}
1028
1029impl ParamCurveNearest for PathSeg {
1030 fn nearest(&self, p: Point, accuracy: f64) -> Nearest {
1031 match *self {
1032 PathSeg::Line(line) => line.nearest(p, accuracy),
1033 PathSeg::Quad(quad) => quad.nearest(p, accuracy),
1034 PathSeg::Cubic(cubic) => cubic.nearest(p, accuracy),
1035 }
1036 }
1037}
1038
1039impl ParamCurveExtrema for PathSeg {
1040 fn extrema(&self) -> ArrayVec<f64, MAX_EXTREMA> {
1041 match *self {
1042 PathSeg::Line(line) => line.extrema(),
1043 PathSeg::Quad(quad) => quad.extrema(),
1044 PathSeg::Cubic(cubic) => cubic.extrema(),
1045 }
1046 }
1047}
1048
1049impl PathSeg {
1050 pub fn as_path_el(&self) -> PathEl {
1052 match self {
1053 PathSeg::Line(line) => PathEl::LineTo(line.p1),
1054 PathSeg::Quad(q) => PathEl::QuadTo(q.p1, q.p2),
1055 PathSeg::Cubic(c) => PathEl::CurveTo(c.p1, c.p2, c.p3),
1056 }
1057 }
1058
1059 pub fn reverse(&self) -> PathSeg {
1062 match self {
1063 PathSeg::Line(Line { p0, p1 }) => PathSeg::Line(Line::new(*p1, *p0)),
1064 PathSeg::Quad(q) => PathSeg::Quad(QuadBez::new(q.p2, q.p1, q.p0)),
1065 PathSeg::Cubic(c) => PathSeg::Cubic(CubicBez::new(c.p3, c.p2, c.p1, c.p0)),
1066 }
1067 }
1068
1069 pub fn to_cubic(&self) -> CubicBez {
1071 match *self {
1072 PathSeg::Line(Line { p0, p1 }) => CubicBez::new(p0, p0, p1, p1),
1073 PathSeg::Cubic(c) => c,
1074 PathSeg::Quad(q) => q.raise(),
1075 }
1076 }
1077
1078 fn winding_inner(&self, p: Point) -> i32 {
1089 let start = self.start();
1090 let end = self.end();
1091 let sign = if end.y > start.y {
1092 if p.y < start.y || p.y >= end.y {
1093 return 0;
1094 }
1095 -1
1096 } else if end.y < start.y {
1097 if p.y < end.y || p.y >= start.y {
1098 return 0;
1099 }
1100 1
1101 } else {
1102 return 0;
1103 };
1104 match *self {
1105 PathSeg::Line(_line) => {
1106 if p.x < start.x.min(end.x) {
1107 return 0;
1108 }
1109 if p.x >= start.x.max(end.x) {
1110 return sign;
1111 }
1112 let a = end.y - start.y;
1114 let b = start.x - end.x;
1115 let c = a * start.x + b * start.y;
1116 if (a * p.x + b * p.y - c) * (sign as f64) <= 0.0 {
1117 sign
1118 } else {
1119 0
1120 }
1121 }
1122 PathSeg::Quad(quad) => {
1123 let p1 = quad.p1;
1124 if p.x < start.x.min(end.x).min(p1.x) {
1125 return 0;
1126 }
1127 if p.x >= start.x.max(end.x).max(p1.x) {
1128 return sign;
1129 }
1130 let t = quad.solve_monotonic_for_y(p.y);
1131 let x = quad.eval(t).x;
1132 if p.x >= x { sign } else { 0 }
1133 }
1134 PathSeg::Cubic(cubic) => {
1135 let p1 = cubic.p1;
1136 let p2 = cubic.p2;
1137 if p.x < start.x.min(end.x).min(p1.x).min(p2.x) {
1138 return 0;
1139 }
1140 if p.x >= start.x.max(end.x).max(p1.x).max(p2.x) {
1141 return sign;
1142 }
1143 let t = cubic.solve_monotonic_for_y(p.y);
1144 let x = cubic.eval(t).x;
1145 if p.x >= x { sign } else { 0 }
1146 }
1147 }
1148 }
1149
1150 fn winding(&self, p: Point) -> i32 {
1154 self.extrema_ranges()
1155 .into_iter()
1156 .map(|range| self.subsegment(range).winding_inner(p))
1157 .sum()
1158 }
1159
1160 pub fn intersect_line(&self, line: Line) -> ArrayVec<LineIntersection, 3> {
1187 const EPSILON: f64 = 1e-9;
1188 let p0 = line.p0;
1189 let p1 = line.p1;
1190 let dx = p1.x - p0.x;
1191 let dy = p1.y - p0.y;
1192 let mut result = ArrayVec::new();
1193 match self {
1194 PathSeg::Line(l) => {
1195 let det = dx * (l.p1.y - l.p0.y) - dy * (l.p1.x - l.p0.x);
1196 if det.abs() < EPSILON {
1197 return result;
1199 }
1200 let t = dx * (p0.y - l.p0.y) - dy * (p0.x - l.p0.x);
1201 let t = t / det;
1203 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1204 let u =
1206 (l.p0.x - p0.x) * (l.p1.y - l.p0.y) - (l.p0.y - p0.y) * (l.p1.x - l.p0.x);
1207 let u = u / det;
1208 if (0.0..=1.0).contains(&u) {
1209 result.push(LineIntersection::new(u, t));
1210 }
1211 }
1212 }
1213 PathSeg::Quad(q) => {
1214 let (px0, px1, px2) = quadratic_bez_coefs(q.p0.x, q.p1.x, q.p2.x);
1219 let (py0, py1, py2) = quadratic_bez_coefs(q.p0.y, q.p1.y, q.p2.y);
1220 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1221 let c1 = dy * px1 - dx * py1;
1222 let c2 = dy * px2 - dx * py2;
1223 let invlen2 = (dx * dx + dy * dy).recip();
1224 for t in solve_quadratic(c0, c1, c2) {
1225 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1226 let x = px0 + t * px1 + t * t * px2;
1227 let y = py0 + t * py1 + t * t * py2;
1228 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1229 if (0.0..=1.0).contains(&u) {
1230 result.push(LineIntersection::new(u, t));
1231 }
1232 }
1233 }
1234 }
1235 PathSeg::Cubic(c) => {
1236 let (px0, px1, px2, px3) = cubic_bez_coefs(c.p0.x, c.p1.x, c.p2.x, c.p3.x);
1238 let (py0, py1, py2, py3) = cubic_bez_coefs(c.p0.y, c.p1.y, c.p2.y, c.p3.y);
1239 let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1240 let c1 = dy * px1 - dx * py1;
1241 let c2 = dy * px2 - dx * py2;
1242 let c3 = dy * px3 - dx * py3;
1243 let invlen2 = (dx * dx + dy * dy).recip();
1244 for t in solve_cubic(c0, c1, c2, c3) {
1245 if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1246 let x = px0 + t * px1 + t * t * px2 + t * t * t * px3;
1247 let y = py0 + t * py1 + t * t * py2 + t * t * t * py3;
1248 let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1249 if (0.0..=1.0).contains(&u) {
1250 result.push(LineIntersection::new(u, t));
1251 }
1252 }
1253 }
1254 }
1255 }
1256 result
1257 }
1258
1259 #[inline]
1261 pub fn is_finite(&self) -> bool {
1262 match self {
1263 PathSeg::Line(line) => line.is_finite(),
1264 PathSeg::Quad(quad_bez) => quad_bez.is_finite(),
1265 PathSeg::Cubic(cubic_bez) => cubic_bez.is_finite(),
1266 }
1267 }
1268
1269 #[inline]
1271 pub fn is_nan(&self) -> bool {
1272 match self {
1273 PathSeg::Line(line) => line.is_nan(),
1274 PathSeg::Quad(quad_bez) => quad_bez.is_nan(),
1275 PathSeg::Cubic(cubic_bez) => cubic_bez.is_nan(),
1276 }
1277 }
1278
1279 #[inline]
1280 fn as_vec2_vec(&self) -> ArrayVec<Vec2, 4> {
1281 let mut a = ArrayVec::new();
1282 match self {
1283 PathSeg::Line(l) => {
1284 a.push(l.p0.to_vec2());
1285 a.push(l.p1.to_vec2());
1286 }
1287 PathSeg::Quad(q) => {
1288 a.push(q.p0.to_vec2());
1289 a.push(q.p1.to_vec2());
1290 a.push(q.p2.to_vec2());
1291 }
1292 PathSeg::Cubic(c) => {
1293 a.push(c.p0.to_vec2());
1294 a.push(c.p1.to_vec2());
1295 a.push(c.p2.to_vec2());
1296 a.push(c.p3.to_vec2());
1297 }
1298 }
1299 a
1300 }
1301
1302 pub fn min_dist(&self, other: PathSeg, accuracy: f64) -> MinDistance {
1308 let (distance, t1, t2) = crate::mindist::min_dist_param(
1309 &self.as_vec2_vec(),
1310 &other.as_vec2_vec(),
1311 (0.0, 1.0),
1312 (0.0, 1.0),
1313 accuracy,
1314 None,
1315 );
1316 MinDistance {
1317 distance: distance.sqrt(),
1318 t1,
1319 t2,
1320 }
1321 }
1322
1323 pub(crate) fn tangents(&self) -> (Vec2, Vec2) {
1327 const EPS: f64 = 1e-12;
1328 match self {
1329 PathSeg::Line(l) => {
1330 let d = l.p1 - l.p0;
1331 (d, d)
1332 }
1333 PathSeg::Quad(q) => {
1334 let d01 = q.p1 - q.p0;
1335 let d0 = if d01.hypot2() > EPS { d01 } else { q.p2 - q.p0 };
1336 let d12 = q.p2 - q.p1;
1337 let d1 = if d12.hypot2() > EPS { d12 } else { q.p2 - q.p0 };
1338 (d0, d1)
1339 }
1340 PathSeg::Cubic(c) => {
1341 let d01 = c.p1 - c.p0;
1342 let d0 = if d01.hypot2() > EPS {
1343 d01
1344 } else {
1345 let d02 = c.p2 - c.p0;
1346 if d02.hypot2() > EPS { d02 } else { c.p3 - c.p0 }
1347 };
1348 let d23 = c.p3 - c.p2;
1349 let d1 = if d23.hypot2() > EPS {
1350 d23
1351 } else {
1352 let d13 = c.p3 - c.p1;
1353 if d13.hypot2() > EPS { d13 } else { c.p3 - c.p0 }
1354 };
1355 (d0, d1)
1356 }
1357 }
1358 }
1359}
1360
1361impl LineIntersection {
1362 #[inline(always)]
1363 fn new(line_t: f64, segment_t: f64) -> Self {
1364 LineIntersection { line_t, segment_t }
1365 }
1366
1367 #[inline]
1369 pub fn is_finite(self) -> bool {
1370 self.line_t.is_finite() && self.segment_t.is_finite()
1371 }
1372
1373 #[inline]
1375 pub fn is_nan(self) -> bool {
1376 self.line_t.is_nan() || self.segment_t.is_nan()
1377 }
1378}
1379
1380fn quadratic_bez_coefs(x0: f64, x1: f64, x2: f64) -> (f64, f64, f64) {
1382 let p0 = x0;
1383 let p1 = 2.0 * x1 - 2.0 * x0;
1384 let p2 = x2 - 2.0 * x1 + x0;
1385 (p0, p1, p2)
1386}
1387
1388fn cubic_bez_coefs(x0: f64, x1: f64, x2: f64, x3: f64) -> (f64, f64, f64, f64) {
1390 let p0 = x0;
1391 let p1 = 3.0 * x1 - 3.0 * x0;
1392 let p2 = 3.0 * x2 - 6.0 * x1 + 3.0 * x0;
1393 let p3 = x3 - 3.0 * x2 + 3.0 * x1 - x0;
1394 (p0, p1, p2, p3)
1395}
1396
1397impl From<CubicBez> for PathSeg {
1398 #[inline(always)]
1399 fn from(cubic_bez: CubicBez) -> PathSeg {
1400 PathSeg::Cubic(cubic_bez)
1401 }
1402}
1403
1404impl From<Line> for PathSeg {
1405 #[inline(always)]
1406 fn from(line: Line) -> PathSeg {
1407 PathSeg::Line(line)
1408 }
1409}
1410
1411impl From<QuadBez> for PathSeg {
1412 #[inline(always)]
1413 fn from(quad_bez: QuadBez) -> PathSeg {
1414 PathSeg::Quad(quad_bez)
1415 }
1416}
1417
1418impl Shape for BezPath {
1419 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1420
1421 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1422 self.0.iter().copied()
1423 }
1424
1425 fn to_path(&self, _tolerance: f64) -> BezPath {
1426 self.clone()
1427 }
1428
1429 #[inline(always)]
1430 fn into_path(self, _tolerance: f64) -> BezPath {
1431 self
1432 }
1433
1434 fn area(&self) -> f64 {
1436 self.elements().area()
1437 }
1438
1439 fn perimeter(&self, accuracy: f64) -> f64 {
1440 self.elements().perimeter(accuracy)
1441 }
1442
1443 fn winding(&self, pt: Point) -> i32 {
1445 self.elements().winding(pt)
1446 }
1447
1448 fn bounding_box(&self) -> Rect {
1449 self.elements().bounding_box()
1450 }
1451
1452 #[inline(always)]
1453 fn as_path_slice(&self) -> Option<&[PathEl]> {
1454 Some(&self.0)
1455 }
1456}
1457
1458impl PathEl {
1459 #[inline]
1461 pub fn is_finite(&self) -> bool {
1462 match self {
1463 PathEl::MoveTo(p) => p.is_finite(),
1464 PathEl::LineTo(p) => p.is_finite(),
1465 PathEl::QuadTo(p, p2) => p.is_finite() && p2.is_finite(),
1466 PathEl::CurveTo(p, p2, p3) => p.is_finite() && p2.is_finite() && p3.is_finite(),
1467 PathEl::ClosePath => true,
1468 }
1469 }
1470
1471 #[inline]
1473 pub fn is_nan(&self) -> bool {
1474 match self {
1475 PathEl::MoveTo(p) => p.is_nan(),
1476 PathEl::LineTo(p) => p.is_nan(),
1477 PathEl::QuadTo(p, p2) => p.is_nan() || p2.is_nan(),
1478 PathEl::CurveTo(p, p2, p3) => p.is_nan() || p2.is_nan() || p3.is_nan(),
1479 PathEl::ClosePath => false,
1480 }
1481 }
1482
1483 pub fn end_point(&self) -> Option<Point> {
1485 match self {
1486 PathEl::MoveTo(p) => Some(*p),
1487 PathEl::LineTo(p1) => Some(*p1),
1488 PathEl::QuadTo(_, p2) => Some(*p2),
1489 PathEl::CurveTo(_, _, p3) => Some(*p3),
1490 PathEl::ClosePath => None,
1491 }
1492 }
1493}
1494
1495impl<'a> Shape for &'a [PathEl] {
1500 type PathElementsIter<'iter>
1501 = core::iter::Copied<core::slice::Iter<'a, PathEl>>
1502 where
1503 'a: 'iter;
1504
1505 #[inline]
1506 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1507 self.iter().copied()
1508 }
1509
1510 fn to_path(&self, _tolerance: f64) -> BezPath {
1511 BezPath::from_vec(self.to_vec())
1512 }
1513
1514 fn area(&self) -> f64 {
1516 segments(self.iter().copied()).area()
1517 }
1518
1519 fn perimeter(&self, accuracy: f64) -> f64 {
1520 segments(self.iter().copied()).perimeter(accuracy)
1521 }
1522
1523 fn winding(&self, pt: Point) -> i32 {
1525 segments(self.iter().copied()).winding(pt)
1526 }
1527
1528 fn bounding_box(&self) -> Rect {
1529 segments(self.iter().copied()).bounding_box()
1530 }
1531
1532 #[inline(always)]
1533 fn as_path_slice(&self) -> Option<&[PathEl]> {
1534 Some(self)
1535 }
1536}
1537
1538impl<const N: usize> Shape for [PathEl; N] {
1543 type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1544
1545 #[inline]
1546 fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1547 self.iter().copied()
1548 }
1549
1550 fn to_path(&self, _tolerance: f64) -> BezPath {
1551 BezPath::from_vec(self.to_vec())
1552 }
1553
1554 fn area(&self) -> f64 {
1556 segments(self.iter().copied()).area()
1557 }
1558
1559 fn perimeter(&self, accuracy: f64) -> f64 {
1560 segments(self.iter().copied()).perimeter(accuracy)
1561 }
1562
1563 fn winding(&self, pt: Point) -> i32 {
1565 segments(self.iter().copied()).winding(pt)
1566 }
1567
1568 fn bounding_box(&self) -> Rect {
1569 segments(self.iter().copied()).bounding_box()
1570 }
1571
1572 #[inline(always)]
1573 fn as_path_slice(&self) -> Option<&[PathEl]> {
1574 Some(self)
1575 }
1576}
1577
1578pub struct PathSegIter {
1580 seg: PathSeg,
1581 ix: usize,
1582}
1583
1584impl Shape for PathSeg {
1585 type PathElementsIter<'iter> = PathSegIter;
1586
1587 #[inline(always)]
1588 fn path_elements(&self, _tolerance: f64) -> PathSegIter {
1589 PathSegIter { seg: *self, ix: 0 }
1590 }
1591
1592 fn area(&self) -> f64 {
1596 self.signed_area()
1597 }
1598
1599 #[inline]
1600 fn perimeter(&self, accuracy: f64) -> f64 {
1601 self.arclen(accuracy)
1602 }
1603
1604 #[inline(always)]
1605 fn winding(&self, _pt: Point) -> i32 {
1606 0
1607 }
1608
1609 #[inline]
1610 fn bounding_box(&self) -> Rect {
1611 ParamCurveExtrema::bounding_box(self)
1612 }
1613
1614 fn as_line(&self) -> Option<Line> {
1615 if let PathSeg::Line(line) = self {
1616 Some(*line)
1617 } else {
1618 None
1619 }
1620 }
1621}
1622
1623impl Iterator for PathSegIter {
1624 type Item = PathEl;
1625
1626 fn next(&mut self) -> Option<PathEl> {
1627 self.ix += 1;
1628 match (self.ix, self.seg) {
1629 (1, PathSeg::Line(seg)) => Some(PathEl::MoveTo(seg.p0)),
1631 (1, PathSeg::Quad(seg)) => Some(PathEl::MoveTo(seg.p0)),
1632 (1, PathSeg::Cubic(seg)) => Some(PathEl::MoveTo(seg.p0)),
1633 (2, PathSeg::Line(seg)) => Some(PathEl::LineTo(seg.p1)),
1634 (2, PathSeg::Quad(seg)) => Some(PathEl::QuadTo(seg.p1, seg.p2)),
1635 (2, PathSeg::Cubic(seg)) => Some(PathEl::CurveTo(seg.p1, seg.p2, seg.p3)),
1636 _ => None,
1637 }
1638 }
1639}
1640
1641#[cfg(test)]
1642mod tests {
1643 use crate::{Circle, DEFAULT_ACCURACY};
1644
1645 use super::*;
1646
1647 fn assert_approx_eq(x: f64, y: f64) {
1648 assert!((x - y).abs() < 1e-8, "{x} != {y}");
1649 }
1650
1651 #[test]
1652 #[should_panic(expected = "uninitialized subpath")]
1653 fn test_elements_to_segments_starts_on_closepath() {
1654 let mut path = BezPath::new();
1655 path.close_path();
1656 path.segments().next();
1657 }
1658
1659 #[test]
1660 fn test_elements_to_segments_closepath_refers_to_last_moveto() {
1661 let mut path = BezPath::new();
1662 path.move_to((5.0, 5.0));
1663 path.line_to((15.0, 15.0));
1664 path.move_to((10.0, 10.0));
1665 path.line_to((15.0, 15.0));
1666 path.close_path();
1667 assert_eq!(
1668 path.segments().collect::<Vec<_>>().last(),
1669 Some(&Line::new((15.0, 15.0), (10.0, 10.0)).into()),
1670 );
1671 }
1672
1673 #[test]
1674 #[should_panic(expected = "uninitialized subpath")]
1675 fn test_must_not_start_on_quad() {
1676 let mut path = BezPath::new();
1677 path.quad_to((5.0, 5.0), (10.0, 10.0));
1678 path.line_to((15.0, 15.0));
1679 path.close_path();
1680 }
1681
1682 #[test]
1683 fn test_intersect_line() {
1684 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1685 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1686 let intersection = PathSeg::Line(h_line).intersect_line(v_line)[0];
1687 assert_approx_eq(intersection.segment_t, 0.1);
1688 assert_approx_eq(intersection.line_t, 0.5);
1689
1690 let v_line = Line::new((-10.0, -10.0), (-10.0, 10.0));
1691 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1692
1693 let v_line = Line::new((10.0, 10.0), (10.0, 20.0));
1694 assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1695 }
1696
1697 #[test]
1698 fn test_intersect_qad() {
1699 let q = QuadBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -10.0));
1700 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1701 assert_eq!(PathSeg::Quad(q).intersect_line(v_line).len(), 1);
1702 let intersection = PathSeg::Quad(q).intersect_line(v_line)[0];
1703 assert_approx_eq(intersection.segment_t, 0.5);
1704 assert_approx_eq(intersection.line_t, 0.75);
1705
1706 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1707 assert_eq!(PathSeg::Quad(q).intersect_line(h_line).len(), 2);
1708 }
1709
1710 #[test]
1711 fn test_intersect_cubic() {
1712 let c = CubicBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -20.0), (30.0, 10.0));
1713 let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1714 assert_eq!(PathSeg::Cubic(c).intersect_line(v_line).len(), 1);
1715 let intersection = PathSeg::Cubic(c).intersect_line(v_line)[0];
1716 assert_approx_eq(intersection.segment_t, 0.333333333);
1717 assert_approx_eq(intersection.line_t, 0.592592592);
1718
1719 let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1720 assert_eq!(PathSeg::Cubic(c).intersect_line(h_line).len(), 3);
1721 }
1722
1723 #[test]
1724 fn test_contains() {
1725 let mut path = BezPath::new();
1726 path.move_to((0.0, 0.0));
1727 path.line_to((1.0, 1.0));
1728 path.line_to((2.0, 0.0));
1729 path.close_path();
1730 assert_eq!(path.winding(Point::new(1.0, 0.5)), -1);
1731 assert!(path.contains(Point::new(1.0, 0.5)));
1732 }
1733
1734 #[test]
1736 fn test_get_seg() {
1737 let circle = Circle::new((10.0, 10.0), 2.0).to_path(DEFAULT_ACCURACY);
1738 let segments = circle.path_segments(DEFAULT_ACCURACY).collect::<Vec<_>>();
1739 let get_segs = (1..usize::MAX)
1740 .map_while(|i| circle.get_seg(i))
1741 .collect::<Vec<_>>();
1742 assert_eq!(segments, get_segs);
1743 }
1744
1745 #[test]
1746 fn test_control_box() {
1747 let path = BezPath::from_svg("M200,300 C50,50 350,50 200,300").unwrap();
1750 assert_eq!(Rect::new(50.0, 50.0, 350.0, 300.0), path.control_box());
1751 assert!(path.control_box().area() > path.bounding_box().area());
1752 }
1753
1754 #[test]
1755 fn test_subpaths() {
1756 let path = BezPath::from_svg("M10,10 L0,10 L0,0 L10,0 Z M100,100 M30,0 Q35,10,40,0 L30,0")
1757 .unwrap();
1758 assert_eq!(
1759 vec![
1760 BezPath::from_svg("M10,10 L0,10 L0,0 L10,0 Z").unwrap(),
1761 BezPath::from_svg("M100,100").unwrap(),
1762 BezPath::from_svg("M30,0 Q35,10,40,0 L30,0").unwrap(),
1763 ],
1764 path.subpaths()
1765 .map(|sp| BezPath::from_vec(sp.to_vec()))
1766 .collect::<Vec<_>>()
1767 );
1768 }
1769
1770 #[test]
1771 fn test_reverse_unclosed() {
1772 let path = BezPath::from_svg("M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60").unwrap();
1773 let reversed = path.reverse_subpaths();
1774 assert_eq!(
1775 "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10",
1776 reversed.to_svg()
1777 );
1778 }
1779
1780 #[test]
1781 fn test_reverse_closed_triangle() {
1782 let path = BezPath::from_svg("M100,100 L150,200 L50,200 Z").unwrap();
1783 let reversed = path.reverse_subpaths();
1784 assert_eq!("M50,200 L150,200 L100,100 Z", reversed.to_svg());
1785 }
1786
1787 #[test]
1788 fn test_reverse_closed_shape() {
1789 let path = BezPath::from_svg(
1790 "M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z",
1791 )
1792 .unwrap();
1793 let reversed = path.reverse_subpaths();
1794 assert_eq!(
1795 "M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z",
1796 reversed.to_svg()
1797 );
1798 }
1799
1800 #[test]
1801 fn test_reverse_multiple_subpaths() {
1802 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";
1803 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";
1804 let path = BezPath::from_svg(svg).unwrap();
1805 let reversed = path.reverse_subpaths();
1806 assert_eq!(expected_svg, reversed.to_svg());
1807 }
1808
1809 #[test]
1811 fn test_reverse_lines() {
1812 let mut path = BezPath::new();
1813 path.move_to((0.0, 0.0));
1814 path.line_to((1.0, 1.0));
1815 path.line_to((2.0, 2.0));
1816 path.line_to((3.0, 3.0));
1817 path.close_path();
1818 let rev = path.reverse_subpaths();
1819 assert_eq!("M3,3 L2,2 L1,1 L0,0 Z", rev.to_svg());
1820 }
1821
1822 #[test]
1823 fn test_reverse_multiple_moves() {
1824 reverse_test_helper(
1825 vec![
1826 PathEl::MoveTo((2.0, 2.0).into()),
1827 PathEl::MoveTo((3.0, 3.0).into()),
1828 PathEl::ClosePath,
1829 PathEl::MoveTo((4.0, 4.0).into()),
1830 ],
1831 vec![
1832 PathEl::MoveTo((2.0, 2.0).into()),
1833 PathEl::MoveTo((3.0, 3.0).into()),
1834 PathEl::ClosePath,
1835 PathEl::MoveTo((4.0, 4.0).into()),
1836 ],
1837 );
1838 }
1839
1840 #[test]
1847 fn test_reverse_closed_last_line_not_on_move() {
1848 reverse_test_helper(
1849 vec![
1850 PathEl::MoveTo((0.0, 0.0).into()),
1851 PathEl::LineTo((1.0, 1.0).into()),
1852 PathEl::LineTo((2.0, 2.0).into()),
1853 PathEl::LineTo((3.0, 3.0).into()),
1854 PathEl::ClosePath,
1855 ],
1856 vec![
1857 PathEl::MoveTo((3.0, 3.0).into()),
1858 PathEl::LineTo((2.0, 2.0).into()),
1859 PathEl::LineTo((1.0, 1.0).into()),
1860 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1862 ],
1863 );
1864 }
1865
1866 #[test]
1867 fn test_reverse_closed_last_line_overlaps_move() {
1868 reverse_test_helper(
1869 vec![
1870 PathEl::MoveTo((0.0, 0.0).into()),
1871 PathEl::LineTo((1.0, 1.0).into()),
1872 PathEl::LineTo((2.0, 2.0).into()),
1873 PathEl::LineTo((0.0, 0.0).into()),
1874 PathEl::ClosePath,
1875 ],
1876 vec![
1877 PathEl::MoveTo((0.0, 0.0).into()),
1878 PathEl::LineTo((2.0, 2.0).into()),
1879 PathEl::LineTo((1.0, 1.0).into()),
1880 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1882 ],
1883 );
1884 }
1885
1886 #[test]
1887 fn test_reverse_closed_duplicate_line_following_move() {
1888 reverse_test_helper(
1889 vec![
1890 PathEl::MoveTo((0.0, 0.0).into()),
1891 PathEl::LineTo((0.0, 0.0).into()),
1892 PathEl::LineTo((1.0, 1.0).into()),
1893 PathEl::LineTo((2.0, 2.0).into()),
1894 PathEl::ClosePath,
1895 ],
1896 vec![
1897 PathEl::MoveTo((2.0, 2.0).into()),
1898 PathEl::LineTo((1.0, 1.0).into()),
1899 PathEl::LineTo((0.0, 0.0).into()), PathEl::LineTo((0.0, 0.0).into()),
1901 PathEl::ClosePath,
1902 ],
1903 );
1904 }
1905
1906 #[test]
1907 fn test_reverse_closed_two_lines() {
1908 reverse_test_helper(
1909 vec![
1910 PathEl::MoveTo((0.0, 0.0).into()),
1911 PathEl::LineTo((1.0, 1.0).into()),
1912 PathEl::ClosePath,
1913 ],
1914 vec![
1915 PathEl::MoveTo((1.0, 1.0).into()),
1916 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1918 ],
1919 );
1920 }
1921
1922 #[test]
1923 fn test_reverse_closed_last_curve_overlaps_move() {
1924 reverse_test_helper(
1925 vec![
1926 PathEl::MoveTo((0.0, 0.0).into()),
1927 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1928 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (0.0, 0.0).into()),
1929 PathEl::ClosePath,
1930 ],
1931 vec![
1932 PathEl::MoveTo((0.0, 0.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1934 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1935 PathEl::ClosePath,
1936 ],
1937 );
1938 }
1939
1940 #[test]
1941 fn test_reverse_closed_last_curve_not_on_move() {
1942 reverse_test_helper(
1943 vec![
1944 PathEl::MoveTo((0.0, 0.0).into()),
1945 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1946 PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (6.0, 6.0).into()),
1947 PathEl::ClosePath,
1948 ],
1949 vec![
1950 PathEl::MoveTo((6.0, 6.0).into()), PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1952 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1953 PathEl::ClosePath,
1954 ],
1955 );
1956 }
1957
1958 #[test]
1959 fn test_reverse_closed_line_curve_line() {
1960 reverse_test_helper(
1961 vec![
1962 PathEl::MoveTo((0.0, 0.0).into()),
1963 PathEl::LineTo((1.0, 1.0).into()), PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
1965 PathEl::CurveTo((5.0, 5.0).into(), (6.0, 6.0).into(), (7.0, 7.0).into()),
1966 PathEl::ClosePath,
1967 ],
1968 vec![
1969 PathEl::MoveTo((7.0, 7.0).into()),
1970 PathEl::CurveTo((6.0, 6.0).into(), (5.0, 5.0).into(), (4.0, 4.0).into()),
1971 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
1972 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
1974 ],
1975 );
1976 }
1977
1978 #[test]
1979 fn test_reverse_closed_last_quad_overlaps_move() {
1980 reverse_test_helper(
1981 vec![
1982 PathEl::MoveTo((0.0, 0.0).into()),
1983 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1984 PathEl::QuadTo((3.0, 3.0).into(), (0.0, 0.0).into()),
1985 PathEl::ClosePath,
1986 ],
1987 vec![
1988 PathEl::MoveTo((0.0, 0.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1990 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1991 PathEl::ClosePath,
1992 ],
1993 );
1994 }
1995
1996 #[test]
1997 fn test_reverse_closed_last_quad_not_on_move() {
1998 reverse_test_helper(
1999 vec![
2000 PathEl::MoveTo((0.0, 0.0).into()),
2001 PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
2002 PathEl::QuadTo((3.0, 3.0).into(), (4.0, 4.0).into()),
2003 PathEl::ClosePath,
2004 ],
2005 vec![
2006 PathEl::MoveTo((4.0, 4.0).into()), PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
2008 PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
2009 PathEl::ClosePath,
2010 ],
2011 );
2012 }
2013
2014 #[test]
2015 fn test_reverse_closed_line_quad_line() {
2016 reverse_test_helper(
2017 vec![
2018 PathEl::MoveTo((0.0, 0.0).into()),
2019 PathEl::LineTo((1.0, 1.0).into()), PathEl::QuadTo((2.0, 2.0).into(), (3.0, 3.0).into()),
2021 PathEl::ClosePath,
2022 ],
2023 vec![
2024 PathEl::MoveTo((3.0, 3.0).into()),
2025 PathEl::QuadTo((2.0, 2.0).into(), (1.0, 1.0).into()),
2026 PathEl::LineTo((0.0, 0.0).into()), PathEl::ClosePath,
2028 ],
2029 );
2030 }
2031
2032 #[test]
2033 fn test_reverse_empty() {
2034 reverse_test_helper(vec![], vec![]);
2035 }
2036
2037 #[test]
2038 fn test_reverse_single_point() {
2039 reverse_test_helper(
2040 vec![PathEl::MoveTo((0.0, 0.0).into())],
2041 vec![PathEl::MoveTo((0.0, 0.0).into())],
2042 );
2043 }
2044
2045 #[test]
2046 fn test_reverse_single_point_closed() {
2047 reverse_test_helper(
2048 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
2049 vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
2050 );
2051 }
2052
2053 #[test]
2054 fn test_reverse_single_line_open() {
2055 reverse_test_helper(
2056 vec![
2057 PathEl::MoveTo((0.0, 0.0).into()),
2058 PathEl::LineTo((1.0, 1.0).into()),
2059 ],
2060 vec![
2061 PathEl::MoveTo((1.0, 1.0).into()),
2062 PathEl::LineTo((0.0, 0.0).into()),
2063 ],
2064 );
2065 }
2066
2067 #[test]
2068 fn test_reverse_single_curve_open() {
2069 reverse_test_helper(
2070 vec![
2071 PathEl::MoveTo((0.0, 0.0).into()),
2072 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
2073 ],
2074 vec![
2075 PathEl::MoveTo((3.0, 3.0).into()),
2076 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
2077 ],
2078 );
2079 }
2080
2081 #[test]
2082 fn test_reverse_curve_line_open() {
2083 reverse_test_helper(
2084 vec![
2085 PathEl::MoveTo((0.0, 0.0).into()),
2086 PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
2087 PathEl::LineTo((4.0, 4.0).into()),
2088 ],
2089 vec![
2090 PathEl::MoveTo((4.0, 4.0).into()),
2091 PathEl::LineTo((3.0, 3.0).into()),
2092 PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
2093 ],
2094 );
2095 }
2096
2097 #[test]
2098 fn test_reverse_line_curve_open() {
2099 reverse_test_helper(
2100 vec![
2101 PathEl::MoveTo((0.0, 0.0).into()),
2102 PathEl::LineTo((1.0, 1.0).into()),
2103 PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
2104 ],
2105 vec![
2106 PathEl::MoveTo((4.0, 4.0).into()),
2107 PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
2108 PathEl::LineTo((0.0, 0.0).into()),
2109 ],
2110 );
2111 }
2112
2113 #[test]
2114 fn test_reverse_duplicate_point_after_move() {
2115 reverse_test_helper(
2118 vec![
2119 PathEl::MoveTo((848.0, 348.0).into()),
2120 PathEl::LineTo((848.0, 348.0).into()),
2121 PathEl::QuadTo((848.0, 526.0).into(), (449.0, 704.0).into()),
2122 PathEl::QuadTo((848.0, 171.0).into(), (848.0, 348.0).into()),
2123 PathEl::ClosePath,
2124 ],
2125 vec![
2126 PathEl::MoveTo((848.0, 348.0).into()),
2127 PathEl::QuadTo((848.0, 171.0).into(), (449.0, 704.0).into()),
2128 PathEl::QuadTo((848.0, 526.0).into(), (848.0, 348.0).into()),
2129 PathEl::LineTo((848.0, 348.0).into()),
2130 PathEl::ClosePath,
2131 ],
2132 );
2133 }
2134
2135 #[test]
2136 fn test_reverse_duplicate_point_at_end() {
2137 reverse_test_helper(
2139 vec![
2140 PathEl::MoveTo((0.0, 651.0).into()),
2141 PathEl::LineTo((0.0, 101.0).into()),
2142 PathEl::LineTo((0.0, 101.0).into()),
2143 PathEl::LineTo((0.0, 651.0).into()),
2144 PathEl::LineTo((0.0, 651.0).into()),
2145 PathEl::ClosePath,
2146 ],
2147 vec![
2148 PathEl::MoveTo((0.0, 651.0).into()),
2149 PathEl::LineTo((0.0, 651.0).into()),
2150 PathEl::LineTo((0.0, 101.0).into()),
2151 PathEl::LineTo((0.0, 101.0).into()),
2152 PathEl::LineTo((0.0, 651.0).into()),
2153 PathEl::ClosePath,
2154 ],
2155 );
2156 }
2157
2158 fn reverse_test_helper(contour: Vec<PathEl>, expected: Vec<PathEl>) {
2159 assert_eq!(BezPath(contour).reverse_subpaths().0, expected);
2160 }
2161
2162 #[test]
2163 fn test_rect_segments() {
2164 let x0 = 25.189500810000002;
2167 let x1 = 568.18950081;
2168 let y0 = -105.0;
2169 let y1 = 176.0;
2170 let r = Rect::from_points((x0, y0), (x1, y1));
2171
2172 let path0 = r.into_path(0.0);
2173 assert!(
2174 path0
2175 .elements()
2176 .iter()
2177 .skip(1)
2178 .all(|el| !matches!(el, PathEl::MoveTo(_)))
2179 );
2180
2181 let path1 = BezPath::from_path_segments(path0.segments());
2182 assert!(
2183 path1
2184 .elements()
2185 .iter()
2186 .skip(1)
2187 .all(|el| !matches!(el, PathEl::MoveTo(_)))
2188 );
2189 }
2190
2191 #[test]
2192 fn test_current_position() {
2193 let mut path = BezPath::new();
2194 assert_eq!(path.current_position(), None);
2195 path.move_to((0., 0.));
2196 assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2197 path.line_to((10., 10.));
2198 assert_eq!(path.current_position(), Some(Point::new(10., 10.)));
2199 path.line_to((10., 0.));
2200 assert_eq!(path.current_position(), Some(Point::new(10., 0.)));
2201 path.close_path();
2202 assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2203
2204 path.close_path();
2205 assert_eq!(path.current_position(), None);
2206
2207 path.move_to((0., 10.));
2208 assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2209 path.close_path();
2210 assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2211 path.close_path();
2212 assert_eq!(path.current_position(), None);
2213 }
2214
2215 #[test]
2223 fn winding_endpoints() {
2224 let bez = BezPath::from_vec(vec![
2225 PathEl::MoveTo((200.0, 410.0).into()),
2226 PathEl::CurveTo(
2227 (139.0, 410.0).into(),
2228 (90.0, 360.8772277832031).into(),
2229 (90.0, 300.0).into(),
2230 ),
2231 PathEl::CurveTo(
2232 (90.0, 239.0).into(),
2233 (139.0, 190.0).into(),
2234 (200.0, 190.0).into(),
2235 ),
2236 PathEl::CurveTo(
2237 (150.0, 210.0).into(),
2238 (110.0, 250.0).into(),
2239 (110.0, 300.0).into(),
2240 ),
2241 PathEl::CurveTo(
2242 (110.0, 349.0).into(),
2243 (150.0, 390.0).into(),
2244 (200.0, 390.0).into(),
2245 ),
2246 PathEl::ClosePath,
2247 ]);
2248
2249 assert!(bez.contains((100.0, 300.1).into()));
2250 assert!(bez.contains((100.0, 299.9).into()));
2251 assert!(bez.contains((100.0, 300.0).into()));
2252 }
2253
2254 #[test]
2255 fn check_close_subpaths() {
2256 let mut bez = BezPath::new();
2257 bez.move_to((10.0, 10.0));
2258 bez.line_to((100.0, 20.0));
2259 bez.line_to((60.0, 100.0));
2260 let elements = close_subpaths(&bez).collect::<Vec<_>>();
2261 bez.close_path();
2262 assert_eq!(&elements, bez.elements());
2263
2264 let mut bez2 = BezPath::new();
2266 bez2.move_to((10.0, 10.0));
2267 bez2.line_to((100.0, 20.0));
2268 bez2.line_to((60.0, 100.0));
2269 bez2.move_to((110.0, 10.0));
2270 bez2.line_to((200.0, 20.0));
2271 bez2.line_to((160.0, 100.0));
2272 let elements2 = close_subpaths(&bez2).collect::<Vec<_>>();
2273 let mut bez3 = BezPath::new();
2274 bez3.move_to((10.0, 10.0));
2275 bez3.line_to((100.0, 20.0));
2276 bez3.line_to((60.0, 100.0));
2277 bez3.close_path();
2278 bez3.move_to((110.0, 10.0));
2279 bez3.line_to((200.0, 20.0));
2280 bez3.line_to((160.0, 100.0));
2281 bez3.close_path();
2282 assert_eq!(&elements2, bez3.elements());
2283 }
2284
2285 #[test]
2286 fn close_subpaths_does_not_duplicate_existing_closepath() {
2287 let path = BezPath::from_vec(vec![
2288 PathEl::MoveTo((0.0, 0.0).into()),
2289 PathEl::LineTo((10.0, 0.0).into()),
2290 PathEl::LineTo((10.0, 10.0).into()),
2291 PathEl::ClosePath,
2292 PathEl::MoveTo((20.0, 0.0).into()),
2293 PathEl::LineTo((30.0, 0.0).into()),
2294 ]);
2295
2296 let closed = close_subpaths(path.iter()).collect::<Vec<_>>();
2297
2298 assert_eq!(
2299 closed,
2300 vec![
2301 PathEl::MoveTo((0.0, 0.0).into()),
2302 PathEl::LineTo((10.0, 0.0).into()),
2303 PathEl::LineTo((10.0, 10.0).into()),
2304 PathEl::ClosePath,
2305 PathEl::MoveTo((20.0, 0.0).into()),
2306 PathEl::LineTo((30.0, 0.0).into()),
2307 PathEl::ClosePath,
2308 ]
2309 );
2310 }
2311}