rasterize/
path.rs

1use crate::{
2    BBox, Cubic, Curve, EPSILON, EllipArc, ImageMut, LinColor, Line, Paint, Point, Quad, Scalar,
3    ScalarFormatter, Segment, Size, SvgParserError, SvgPathParser, Transform, curve::line_offset,
4    rasterize::Rasterizer, utils::clamp,
5};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::{
9    fmt,
10    io::{Cursor, Read, Write},
11    str::FromStr,
12};
13
14/// Default flatness used during rasterization.
15/// Value of 0.05px gives good accuracy tradeoff.
16pub const DEFAULT_FLATNESS: Scalar = 0.05;
17
18/// The algorithm to use to determine the inside part of a shape, when filling it.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub enum FillRule {
22    /// Fill area with non-zero winding number
23    #[default]
24    #[cfg_attr(feature = "serde", serde(rename = "nonzero"))]
25    NonZero,
26    /// Fill area with odd winding number
27    #[cfg_attr(feature = "serde", serde(rename = "evenodd"))]
28    EvenOdd,
29}
30
31impl FillRule {
32    pub fn alpha_from_winding(&self, winding: Scalar) -> Scalar {
33        match self {
34            FillRule::EvenOdd => ((winding + 1.0).rem_euclid(2.0) - 1.0).abs(),
35            FillRule::NonZero => {
36                let value = winding.abs();
37                if value >= 1.0 {
38                    1.0
39                } else if value < 1e-6 {
40                    0.0
41                } else {
42                    value
43                }
44            }
45        }
46    }
47}
48
49impl FromStr for FillRule {
50    type Err = SvgParserError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        match s {
54            "nonzero" => Ok(FillRule::NonZero),
55            "evenodd" => Ok(FillRule::EvenOdd),
56            _ => Err(SvgParserError::InvalidFillRule),
57        }
58    }
59}
60
61impl fmt::Display for FillRule {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        match self {
64            FillRule::NonZero => "nonzero".fmt(f),
65            FillRule::EvenOdd => "evenodd".fmt(f),
66        }
67    }
68}
69
70/// `LineJoin` defines the shape to be used at the corners of paths when they are stroked.
71/// See [SVG specification](https://www.w3.org/TR/SVG2/painting.html#LineJoin) for more details.
72#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
73#[cfg_attr(
74    feature = "serde",
75    derive(Serialize, Deserialize),
76    serde(rename_all = "lowercase")
77)]
78pub enum LineJoin {
79    /// Continue path segments with lines until they intersect. But only
80    /// if `miter_length = stroke-width / sin(0.5 * eta)` is less than the miter argument.
81    Miter(Scalar),
82    /// Connect path segments with straight line.
83    Bevel,
84    /// Round corner is to be used to join path segments.
85    /// The corner is a circular sector centered on the join point.
86    Round,
87}
88
89impl Default for LineJoin {
90    fn default() -> Self {
91        Self::Miter(4.0)
92    }
93}
94
95/// `LineCap` specifies the shape to be used at the end of open sub-paths when they are stroked.
96/// See [SVG specification](https://www.w3.org/TR/SVG2/painting.html#LineCaps) for more details.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
98#[cfg_attr(
99    feature = "serde",
100    derive(Serialize, Deserialize),
101    serde(rename_all = "lowercase")
102)]
103pub enum LineCap {
104    /// Connect path segments with straight line.
105    Butt,
106    /// Add half-square to the end of the segments
107    Square,
108    /// Add half-circle to the end of the segments
109    Round,
110}
111
112impl Default for LineCap {
113    fn default() -> Self {
114        Self::Butt
115    }
116}
117
118/// Style used to generate stroke
119#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
120#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
121pub struct StrokeStyle {
122    /// Width of the stroke
123    pub width: Scalar,
124    /// How to join offset segments
125    #[cfg_attr(
126        feature = "serde",
127        serde(default, skip_serializing_if = "crate::utils::is_default")
128    )]
129    pub line_join: LineJoin,
130    /// How to join segments at the ends of the path
131    #[cfg_attr(
132        feature = "serde",
133        serde(default, skip_serializing_if = "crate::utils::is_default")
134    )]
135    pub line_cap: LineCap,
136}
137
138/// Non-empty collections of segments where end of each segments coincides with the start of the next one.
139#[derive(Clone, Copy, PartialEq)]
140pub struct SubPath<'a> {
141    /// List of segments representing SubPath
142    segments: &'a [Segment],
143    /// Whether SubPath contains an implicit line segment connecting start and the end of it.
144    closed: bool,
145}
146
147impl fmt::Debug for SubPath<'_> {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        for segment in self.segments.iter() {
150            writeln!(f, "{:?}", segment)?;
151        }
152        if self.closed {
153            writeln!(f, "Close")?;
154        } else {
155            writeln!(f, "End")?
156        }
157        Ok(())
158    }
159}
160
161impl<'a> SubPath<'a> {
162    pub fn new(segments: &'a [Segment], closed: bool) -> Option<Self> {
163        if segments.is_empty() {
164            None
165        } else {
166            Some(Self { segments, closed })
167        }
168    }
169
170    /// Whether sub-path is closed or not
171    pub fn is_closed(&self) -> bool {
172        self.closed
173    }
174
175    pub fn segments(&self) -> &[Segment] {
176        self.segments
177    }
178
179    /// First segment in the sub-path
180    pub fn first(&self) -> Segment {
181        *self.segments.first().expect("SubPath is never emtpy")
182    }
183
184    /// Last segment in the sub-path
185    pub fn last(&self) -> Segment {
186        *self.segments.last().expect("SubPath is never empty")
187    }
188
189    pub fn flatten(
190        &self,
191        tr: Transform,
192        flatness: Scalar,
193        close: bool,
194    ) -> impl Iterator<Item = Line> + '_ {
195        let last = if self.closed || close {
196            Some(Line::new(self.end(), self.start()).transform(tr))
197        } else {
198            None
199        };
200        self.segments
201            .iter()
202            .flat_map(move |segment| segment.flatten(tr, flatness))
203            .chain(last)
204    }
205
206    /// Start point of the sub-path
207    pub fn start(&self) -> Point {
208        self.first().start()
209    }
210
211    /// End point of the sub-path
212    pub fn end(&self) -> Point {
213        self.last().end()
214    }
215
216    /// Bounding box of the sub-path
217    pub fn bbox(&self, init: Option<BBox>, tr: Transform) -> BBox {
218        self.segments
219            .iter()
220            .fold(init, |bbox, seg| Some(seg.transform(tr).bbox(bbox)))
221            .expect("SubPath is never empty")
222    }
223}
224
225/// Collection of the SubPath treated as a single unit. Represents the same concept
226/// as an [SVG path](https://www.w3.org/TR/SVG11/paths.html)
227#[derive(Clone, PartialEq, Default)]
228pub struct Path {
229    segments: Vec<Segment>,
230    /// segments[subpath[i]..subpath[i+1]] represents i-th subpath
231    subpaths: Vec<usize>,
232    closed: Vec<bool>,
233}
234
235impl Path {
236    pub fn new(segments: Vec<Segment>, subpaths: Vec<usize>, closed: Vec<bool>) -> Self {
237        Self {
238            segments,
239            subpaths,
240            closed,
241        }
242    }
243
244    /// Create empty path
245    pub fn empty() -> Self {
246        Self {
247            segments: Vec::new(),
248            subpaths: Vec::new(),
249            closed: Vec::new(),
250        }
251    }
252
253    /// Check if the path is empty
254    pub fn is_empty(&self) -> bool {
255        self.subpaths.is_empty()
256    }
257
258    /// Number of sub-paths in the path
259    pub fn len(&self) -> usize {
260        if self.subpaths.len() < 2 {
261            0
262        } else {
263            self.subpaths.len() - 1
264        }
265    }
266
267    /// Calculate winding number of a point
268    pub fn winding_at(&self, point: impl Into<Point>) -> i32 {
269        let point = point.into();
270        // We are using horizontal line `y = point.y` to calculate winding number
271        // - Find all segments that can potentially intersect this line.
272        //   If all control points are on one side of the line then it is not going to
273        //   intersect it as bezier curve is always bound by all its control points.
274        // - Find intersection and based on tangent direction assign 1 or -1, throw away
275        //   all intersections with `x > point.x`
276        let y = point.y();
277        let tr = Transform::new_translate(0.0, -y);
278        let mut winding = 0;
279        for subpath in self.subpaths() {
280            let last = if subpath.closed {
281                Some(Line::new(subpath.end(), subpath.start()).into())
282            } else {
283                None
284            };
285            for segment in subpath.segments().iter().chain(&last) {
286                let points: &[Point] = match segment {
287                    Segment::Line(Line(points)) => points,
288                    Segment::Quad(Quad(points)) => points,
289                    Segment::Cubic(Cubic(points)) => points,
290                };
291                let (above, below) = points.iter().fold((false, false), |(above, below), ctrl| {
292                    if ctrl.y() > point.y() {
293                        (true, below)
294                    } else {
295                        (above, true)
296                    }
297                });
298                if !above || !below {
299                    // will not intersect horizontal line
300                    continue;
301                }
302                for root_t in segment.transform(tr).roots() {
303                    let root = segment.at(root_t);
304                    if root.x() > point.x() {
305                        continue;
306                    }
307                    let deriv = segment.deriv().at(root_t);
308                    if deriv.y() > 0.0 {
309                        winding += 1;
310                    } else if deriv.y() < 0.0 {
311                        winding -= 1;
312                    }
313                }
314            }
315        }
316        winding
317    }
318
319    /// Get sub-path
320    pub fn get(&self, index: usize) -> Option<SubPath<'_>> {
321        let start = *self.subpaths.get(index)?;
322        let end = *self.subpaths.get(index + 1)?;
323        let segments = self.segments.get(start..end)?;
324        let closed = *self.closed.get(index)?;
325        Some(SubPath { segments, closed })
326    }
327
328    /// List of sub-paths
329    pub fn subpaths(&self) -> PathIter<'_> {
330        PathIter {
331            path: self,
332            index: 0,
333        }
334    }
335
336    pub fn push(&mut self, segments: &[Segment], closed: bool) {
337        if segments.is_empty() {
338            return;
339        }
340        if self.subpaths.is_empty() {
341            self.subpaths.push(0);
342        }
343        self.segments.extend(segments.iter().copied());
344        self.subpaths.push(self.segments.len());
345        self.closed.push(closed);
346    }
347
348    /// Convenience method to create [`PathBuilder`]
349    pub fn builder() -> PathBuilder {
350        PathBuilder::new()
351    }
352
353    /// Convert path into a path builder so it can be extended
354    pub fn into_builder(self) -> PathBuilder {
355        PathBuilder::from_path(self)
356    }
357
358    /// Apply transformation to the path in place
359    pub fn transform(&mut self, tr: Transform) {
360        self.segments.iter_mut().for_each(|segment| {
361            *segment = segment.transform(tr);
362        });
363    }
364
365    /// Number of segments in the path
366    pub fn segments_count(&self) -> usize {
367        self.segments.len()
368    }
369
370    /// Stroke path
371    ///
372    /// Stroked path is the path constructed from original by offsetting by `distance/2` and
373    /// joining it with the path offset by `-distance/2`.
374    pub fn stroke(&self, style: StrokeStyle) -> Path {
375        let mut result = Path::empty();
376        let mut segments = Vec::new();
377        for subpath in self.subpaths() {
378            // forward
379            for segment in subpath.segments() {
380                stroke_segment(&mut segments, *segment, style, Segment::line_join);
381            }
382            let mut backward = subpath.segments.iter().rev().map(Segment::reverse);
383            // close subpath
384            if subpath.is_closed() {
385                stroke_close(subpath, &mut segments, style, true);
386                result.push(&segments, true);
387                segments.clear();
388            } else {
389                // cap
390                if let Some(segment) = backward.next() {
391                    stroke_segment(&mut segments, segment, style, Segment::line_cap);
392                }
393            }
394            // backward
395            for segment in backward {
396                stroke_segment(&mut segments, segment, style, Segment::line_join);
397            }
398            // close subpath
399            if subpath.is_closed() {
400                stroke_close(subpath, &mut segments, style, false);
401                result.push(&segments, true);
402                segments.clear();
403            } else {
404                // cap
405                let last = segments.last().copied();
406                let first = segments.first().copied();
407                if let (Some(last), Some(first)) = (last, first) {
408                    segments.extend(last.line_cap(first, style));
409                }
410                result.push(&segments, true);
411                segments.clear();
412            }
413        }
414        result
415    }
416
417    /// Convert path to an iterator over line segments
418    pub fn flatten(
419        &self,
420        tr: Transform,
421        flatness: Scalar,
422        close: bool,
423    ) -> impl Iterator<Item = Line> + '_ {
424        PathFlattenIter::new(self, tr, flatness, close)
425    }
426
427    /// Bounding box of the path after provided transformation is applied.
428    pub fn bbox(&self, tr: Transform) -> Option<BBox> {
429        self.subpaths()
430            .fold(None, |bbox, subpath| Some(subpath.bbox(bbox, tr)))
431    }
432
433    /// Calculate size of the image required to render the path
434    ///
435    /// Returns:
436    ///   - Size of the image
437    ///   - Transformation required
438    ///   - Position of lowest x and y point of the image
439    pub fn size(&self, tr: Transform) -> Option<(Size, Transform, Point)> {
440        let bbox = self.bbox(tr)?;
441        let Point([min_x, min_y]) = bbox.min();
442        let Point([max_x, max_y]) = bbox.max();
443        let min = Point::new(min_x.floor() - 1.0, min_y.floor() - 1.0);
444        let max = Point::new(max_x.ceil() + 1.0, max_y.ceil() + 1.0);
445        let size = Size {
446            width: (max.x() - min.x()).round() as usize,
447            height: (max.y() - min.y()).round() as usize,
448        };
449        let shift = Transform::new_translate(1.0 - min_x, 1.0 - min_y);
450        Some((size, shift * tr, min))
451    }
452
453    /// Reverse order and direction of all segments
454    pub fn reverse(&mut self) {
455        if self.segments.is_empty() {
456            return;
457        }
458
459        // reverse segments
460        let mut left = 0;
461        let mut right = self.segments.len() - 1;
462        while left < right {
463            let left_segment = self.segments[left].reverse();
464            let right_segment = self.segments[right].reverse();
465            self.segments[left] = right_segment;
466            self.segments[right] = left_segment;
467            left += 1;
468            right -= 1;
469        }
470        if left == right {
471            let left_segment = self.segments[left].reverse();
472            self.segments[left] = left_segment;
473        }
474
475        // reverse sub-paths offsets
476        for index in 0..(self.subpaths.len() - 1) {
477            self.subpaths[index] = self.subpaths[index + 1] - self.subpaths[index];
478        }
479        self.subpaths.reverse();
480        self.subpaths[0] = 0;
481        let mut offset = 0;
482        for index in 1..self.subpaths.len() {
483            offset += self.subpaths[index];
484            self.subpaths[index] = offset;
485        }
486
487        // reverse closed
488        self.closed.reverse();
489    }
490
491    /// Fill path with the provided paint
492    pub fn fill<R, P, I>(
493        &self,
494        rasterizer: R,
495        tr: Transform,
496        fill_rule: FillRule,
497        paint: P,
498        mut img: I,
499    ) -> I
500    where
501        R: Rasterizer,
502        P: Paint,
503        I: ImageMut<Pixel = LinColor>,
504    {
505        rasterizer.fill(self, tr, fill_rule, &paint, &mut img);
506        img
507    }
508
509    /// Rasterize mast for the path in into a provided image.
510    ///
511    /// Everything that is outside of the image will be cropped. Image is assumed
512    /// to contain zeros.
513    pub fn mask<R, I>(&self, rasterizer: R, tr: Transform, fill_rule: FillRule, mut img: I) -> I
514    where
515        R: Rasterizer,
516        I: ImageMut<Pixel = Scalar>,
517    {
518        rasterizer.mask(self, tr, &mut img, fill_rule);
519        img
520    }
521
522    /// Save path in SVG path format.
523    pub fn write_svg_path(&self, mut out: impl Write) -> std::io::Result<()> {
524        write!(out, "{}", self)
525    }
526
527    /// Load path from SVG path representation
528    pub fn read_svg_path(input: impl Read) -> std::io::Result<Self> {
529        let mut builder = PathBuilder::new();
530        for cmd in SvgPathParser::new(input) {
531            cmd?.apply(&mut builder)
532        }
533        Ok(builder.build())
534    }
535
536    pub fn display(&self, relative: bool, tr: Transform) -> PathSvgDisplay<'_> {
537        PathSvgDisplay {
538            path: self,
539            relative,
540            tr,
541        }
542    }
543
544    /// Returns struct that prints command per line on debug formatting.
545    pub fn verbose_debug(&self) -> PathVerboseDebug<'_> {
546        PathVerboseDebug { path: self }
547    }
548}
549
550impl fmt::Debug for Path {
551    fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
552        use std::fmt::Display;
553        self.display(false, Transform::identity()).fmt(out)
554    }
555}
556
557impl fmt::Display for Path {
558    fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
559        self.display(false, Transform::identity()).fmt(out)
560    }
561}
562
563pub struct PathVerboseDebug<'a> {
564    path: &'a Path,
565}
566
567impl fmt::Debug for PathVerboseDebug<'_> {
568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569        if self.path.subpaths.is_empty() {
570            write!(f, "Empty")?;
571        } else {
572            for subpath in self.path.subpaths.iter() {
573                subpath.fmt(f)?
574            }
575        }
576        Ok(())
577    }
578}
579
580pub struct PathSvgDisplay<'a> {
581    path: &'a Path,
582    relative: bool,
583    tr: Transform,
584}
585
586impl PathSvgDisplay<'_> {
587    fn fmt_point(
588        &self,
589        out: &mut fmt::Formatter<'_>,
590        formatter: &mut ScalarFormatter,
591        point: Point,
592        previous: Option<Point>,
593        sep: bool,
594    ) -> Result<Point, fmt::Error> {
595        let point_tr = self.tr.apply(point);
596        let point = previous.map_or_else(|| point_tr, |point_prev| point_tr - point_prev);
597        let x = point.x();
598        let y = point.y();
599        if sep && x >= 0.0 {
600            out.write_str(" ")?;
601        }
602        out.write_str(formatter.format_str(x))?;
603        if y >= 0.0 {
604            out.write_str(",")?;
605        }
606        out.write_str(formatter.format_str(y))?;
607        Ok(point_tr)
608    }
609}
610
611impl fmt::Display for PathSvgDisplay<'_> {
612    fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
613        let mut formatter = ScalarFormatter::new_fmt(out);
614        let mut previous: Option<Point> = None;
615        for subpath in self.path.subpaths() {
616            out.write_str(if self.relative && previous.is_some() {
617                "m"
618            } else {
619                "M"
620            })?;
621            let point_last =
622                self.fmt_point(out, &mut formatter, subpath.start(), previous, false)?;
623            if self.relative {
624                previous = Some(point_last);
625            }
626
627            for segment in subpath.segments().iter() {
628                let point_last = match segment {
629                    Segment::Line(line) => {
630                        out.write_str(if self.relative { "l" } else { "L" })?;
631                        self.fmt_point(out, &mut formatter, line.end(), previous, false)?
632                    }
633                    Segment::Quad(quad) => {
634                        out.write_str(if self.relative { "q" } else { "Q" })?;
635                        let [_, p1, p2] = quad.points();
636                        self.fmt_point(out, &mut formatter, p1, previous, false)?;
637                        self.fmt_point(out, &mut formatter, p2, previous, true)?
638                    }
639                    Segment::Cubic(cubic) => {
640                        out.write_str(if self.relative { "c" } else { "C" })?;
641                        let [_, p1, p2, p3] = cubic.points();
642                        self.fmt_point(out, &mut formatter, p1, previous, false)?;
643                        self.fmt_point(out, &mut formatter, p2, previous, true)?;
644                        self.fmt_point(out, &mut formatter, p3, previous, true)?
645                    }
646                };
647                if self.relative {
648                    previous = Some(point_last);
649                }
650            }
651            if subpath.is_closed() {
652                write!(out, "Z")?;
653            }
654        }
655        Ok(())
656    }
657}
658
659pub struct PathIter<'a> {
660    path: &'a Path,
661    index: usize,
662}
663
664impl<'a> Iterator for PathIter<'a> {
665    type Item = SubPath<'a>;
666
667    fn next(&mut self) -> Option<Self::Item> {
668        let result = self.path.get(self.index)?;
669        self.index += 1;
670        Some(result)
671    }
672}
673
674impl<'a> IntoIterator for &'a Path {
675    type Item = SubPath<'a>;
676    type IntoIter = PathIter<'a>;
677
678    fn into_iter(self) -> Self::IntoIter {
679        self.subpaths()
680    }
681}
682
683impl<'a> Extend<SubPath<'a>> for Path {
684    fn extend<T: IntoIterator<Item = SubPath<'a>>>(&mut self, iter: T) {
685        for subpath in iter {
686            self.push(subpath.segments, subpath.closed);
687        }
688    }
689}
690
691/// Extend segments with the offset segment and join between those segments.
692fn stroke_segment<F, S>(segments: &mut Vec<Segment>, segment: Segment, style: StrokeStyle, join: F)
693where
694    F: Fn(Segment, Segment, StrokeStyle) -> S,
695    S: IntoIterator<Item = Segment>,
696{
697    let offset = segments.len();
698    segment.offset(style.width / 2.0, segments);
699    if offset != 0 {
700        let src = segments.get(offset - 1).copied();
701        let dst = segments.get(offset).copied();
702        if let (Some(src), Some(dst)) = (src, dst) {
703            segments.splice(offset..offset, join(src, dst, style));
704        }
705    }
706}
707
708fn stroke_close(
709    subpath: SubPath<'_>,
710    segments: &mut Vec<Segment>,
711    style: StrokeStyle,
712    forward: bool,
713) {
714    let (first, last) = match (segments.first(), segments.last()) {
715        (Some(first), Some(last)) => (*first, *last),
716        _ => return,
717    };
718    let close = if forward {
719        Line::new(subpath.end(), subpath.start())
720    } else {
721        Line::new(subpath.start(), subpath.end())
722    };
723    match line_offset(close, style.width / 2.0) {
724        Some(close) if close.length() * 100.0 > style.width => {
725            let close = Segment::from(close);
726            segments.extend(last.line_join(close, style));
727            segments.push(close);
728            segments.extend(close.line_join(first, style));
729        }
730        _ => segments.extend(last.line_join(first, style)),
731    }
732}
733
734pub struct PathFlattenIter<'a> {
735    path: &'a Path,
736    transform: Transform,
737    flatness: Scalar,
738    close: bool,
739    subpath_index: usize,
740    segment_index: usize,
741    stack: Vec<Segment>,
742}
743
744impl<'a> PathFlattenIter<'a> {
745    fn new(path: &'a Path, transform: Transform, flatness: Scalar, close: bool) -> Self {
746        Self {
747            path,
748            transform,
749            flatness: 16.0 * flatness * flatness,
750            close,
751            subpath_index: 0,
752            segment_index: 0,
753            stack: Default::default(),
754        }
755    }
756}
757
758impl Iterator for PathFlattenIter<'_> {
759    type Item = Line;
760
761    fn next(&mut self) -> Option<Self::Item> {
762        loop {
763            match self.stack.pop() {
764                Some(segment) => {
765                    if segment.has_nans() {
766                        panic!("cannot flatten segment with NaN");
767                    }
768                    if segment.flatness() < self.flatness {
769                        return Some(Line::new(segment.start(), segment.end()));
770                    }
771                    let (s0, s1) = segment.split();
772                    self.stack.push(s1);
773                    self.stack.push(s0);
774                }
775                None => {
776                    let subpath = self.path.get(self.subpath_index)?;
777                    match subpath.segments().get(self.segment_index) {
778                        None => {
779                            self.subpath_index += 1;
780                            self.segment_index = 0;
781                            if subpath.closed || self.close {
782                                let line = Line::new(subpath.end(), subpath.start())
783                                    .transform(self.transform);
784                                return Some(line);
785                            }
786                        }
787                        Some(segment) => {
788                            self.segment_index += 1;
789                            self.stack.push(segment.transform(self.transform));
790                        }
791                    }
792                }
793            }
794        }
795    }
796}
797
798/// Path builder similar to Canvas/Cairo interface.
799#[derive(Clone)]
800pub struct PathBuilder {
801    position: Point,
802    segments: Vec<Segment>,
803    subpaths: Vec<usize>,
804    closed: Vec<bool>,
805}
806
807impl Default for PathBuilder {
808    fn default() -> Self {
809        Self::new()
810    }
811}
812
813impl PathBuilder {
814    pub fn new() -> Self {
815        Self {
816            position: Point::new(0.0, 0.0),
817            segments: Vec::new(),
818            subpaths: Vec::new(),
819            closed: Vec::new(),
820        }
821    }
822
823    pub fn from_path(path: Path) -> Self {
824        let mut builder = Self::new();
825        builder.segments = path.segments;
826        builder.subpaths = path.subpaths;
827        builder.closed = path.closed;
828        builder
829    }
830
831    /// Build path
832    pub fn build(&mut self) -> Path {
833        self.subpath_finish(false);
834        let PathBuilder {
835            segments,
836            subpaths,
837            closed,
838            ..
839        } = std::mem::take(self);
840        Path::new(segments, subpaths, closed)
841    }
842
843    pub fn push(&mut self, segments: &[Segment], closed: bool) {
844        self.segments.extend(segments.iter().copied());
845        self.subpath_finish(closed);
846    }
847
848    /// Finish current subpath
849    fn subpath_finish(&mut self, close: bool) {
850        if self.segments.is_empty() || self.subpaths.last().copied() == Some(self.segments.len()) {
851            return;
852        }
853        if self.subpaths.is_empty() {
854            self.subpaths.push(0);
855        }
856        if close {
857            // if we close subpath, current position is set to start of the current subpath
858            if let Some(subpath_start) = self
859                .subpaths
860                .last()
861                .and_then(|first_index| Some(self.segments.get(*first_index)?.start()))
862            {
863                self.position = subpath_start;
864            }
865        }
866        self.subpaths.push(self.segments.len());
867        self.closed.push(close);
868    }
869
870    /// Extend path from string, which is specified in the same format as SVGs path element.
871    pub fn append_svg_path(
872        &mut self,
873        string: impl AsRef<[u8]>,
874    ) -> Result<&mut Self, SvgParserError> {
875        for cmd in SvgPathParser::new(Cursor::new(string)) {
876            cmd?.apply(self);
877        }
878        Ok(self)
879    }
880
881    /// Move current position, ending current subpath
882    pub fn move_to(&mut self, p: impl Into<Point>) -> &mut Self {
883        self.subpath_finish(false);
884        self.position = p.into();
885        self
886    }
887
888    /// Close current subpath
889    pub fn close(&mut self) -> &mut Self {
890        self.subpath_finish(true);
891        self
892    }
893
894    /// Add line from the current position to the specified point
895    pub fn line_to(&mut self, p: impl Into<Point>) -> &mut Self {
896        let p = p.into();
897        if !self.position.is_close_to(p) {
898            let line = Line::new(self.position, p);
899            self.position = line.end();
900            self.segments.push(line.into());
901        }
902        self
903    }
904
905    /// Add quadratic bezier curve
906    pub fn quad_to(&mut self, p1: impl Into<Point>, p2: impl Into<Point>) -> &mut Self {
907        let quad = Quad::new(self.position, p1, p2);
908        self.position = quad.end();
909        self.segments.push(quad.into());
910        self
911    }
912
913    /// Add smooth quadratic bezier curve
914    pub fn quad_smooth_to(&mut self, p2: impl Into<Point>) -> &mut Self {
915        let p1 = match self.segments.last() {
916            Some(Segment::Quad(quad)) => quad.smooth(),
917            _ => self.position,
918        };
919        self.quad_to(p1, p2)
920    }
921
922    /// Add cubic bezier curve
923    pub fn cubic_to(
924        &mut self,
925        p1: impl Into<Point>,
926        p2: impl Into<Point>,
927        p3: impl Into<Point>,
928    ) -> &mut Self {
929        let cubic = Cubic::new(self.position, p1, p2, p3);
930        self.position = cubic.end();
931        self.segments.push(cubic.into());
932        self
933    }
934
935    /// Add smooth cubic bezier curve
936    pub fn cubic_smooth_to(&mut self, p2: impl Into<Point>, p3: impl Into<Point>) -> &mut Self {
937        let p1 = match self.segments.last() {
938            Some(Segment::Cubic(cubic)) => cubic.smooth(),
939            _ => self.position,
940        };
941        self.cubic_to(p1, p2, p3)
942    }
943
944    /// Add elliptic arc segment
945    pub fn arc_to(
946        &mut self,
947        radii: impl Into<Point>,
948        x_axis_rot: Scalar,
949        large: bool,
950        sweep: bool,
951        p: impl Into<Point>,
952    ) -> &mut Self {
953        let radii: Point = radii.into();
954        let p = p.into();
955        let arc = EllipArc::new_param(
956            self.position,
957            p,
958            radii.x(),
959            radii.y(),
960            x_axis_rot,
961            large,
962            sweep,
963        );
964        match arc {
965            None => self.line_to(p),
966            Some(arc) => {
967                self.segments.extend(arc.to_cubics().map(Segment::from));
968                self.position = p;
969                self
970            }
971        }
972    }
973
974    /// Add circle with the center at current position and provided radius.
975    ///
976    /// Current position is not changed after invocation.
977    pub fn circle(&mut self, radius: Scalar) -> &mut Self {
978        // https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves
979        // (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.5522847498307935
980        let offset = 0.5522847498307935 * radius;
981        let x_offset = Point::new(offset, 0.0);
982        let y_offset = Point::new(0.0, offset);
983        let center = self.position();
984        let p0 = center - Point::new(radius, 0.0);
985        let p1 = center - Point::new(0.0, radius);
986        let p2 = center + Point::new(radius, 0.0);
987        let p3 = center + Point::new(0.0, radius);
988
989        self.move_to(p0)
990            .cubic_to(p0 - y_offset, p1 - x_offset, p1)
991            .cubic_to(p1 + x_offset, p2 - y_offset, p2)
992            .cubic_to(p2 + y_offset, p3 + x_offset, p3)
993            .cubic_to(p3 - x_offset, p0 + y_offset, p0)
994            .close()
995            .move_to(center)
996    }
997
998    /// Add box with rounded corners, with current position being low-x and low-y coordinate
999    pub fn rbox(&mut self, size: impl Into<Point>, radii: impl Into<Point>) -> &mut Self {
1000        let init = self.position;
1001        let bbox = BBox::new(self.position, self.position + size.into());
1002        let Point([lx, ly]) = bbox.min();
1003        let Point([hx, hy]) = bbox.max();
1004
1005        let Point([rx, ry]) = radii.into();
1006        let rx = clamp(rx.abs(), 0.0, hx - lx);
1007        let ry = clamp(ry.abs(), 0.0, hy - ly);
1008        let radii = Point::new(rx, ry);
1009        let rounded = rx > EPSILON && ry > EPSILON;
1010
1011        self.move_to((lx + rx, ly)).line_to((hx - rx, ly));
1012        if rounded {
1013            self.arc_to(radii, 0.0, false, true, (hx, ly + ry));
1014        }
1015        self.line_to((hx, hy - ry));
1016        if rounded {
1017            self.arc_to(radii, 0.0, false, true, (hx - rx, hy));
1018        }
1019        self.line_to((lx + rx, hy));
1020        if rounded {
1021            self.arc_to(radii, 0.0, false, true, (lx, hy - ry));
1022        }
1023        self.line_to((lx, ly + ry));
1024        if rounded {
1025            self.arc_to(radii, 0.0, false, true, (lx + rx, ly));
1026        }
1027        self.close().move_to(init)
1028    }
1029
1030    /// Create checker board path inside bounding, useful to draw transparent area
1031    pub fn checkerboard(&mut self, bbox: BBox, cell_size: Scalar) -> &mut Self {
1032        let mut x = bbox.x();
1033        let mut y = bbox.y();
1034        while y < bbox.max().y() {
1035            while x < bbox.max().x() {
1036                let offset = Point::new(x, y);
1037                self.move_to(offset)
1038                    .line_to(offset + Point::new(cell_size, 0.0))
1039                    .line_to(offset + Point::new(cell_size, 2.0 * cell_size))
1040                    .line_to(offset + Point::new(2.0 * cell_size, 2.0 * cell_size))
1041                    .line_to(offset + Point::new(2.0 * cell_size, cell_size))
1042                    .line_to(offset + Point::new(0.0, cell_size))
1043                    .close();
1044                x += 2.0 * cell_size;
1045            }
1046            x = bbox.x();
1047            y += 2.0 * cell_size;
1048        }
1049        self.move_to(bbox.min())
1050    }
1051
1052    /// Current position of the builder
1053    pub fn position(&self) -> Point {
1054        self.position
1055    }
1056}
1057
1058impl FromStr for Path {
1059    type Err = SvgParserError;
1060
1061    fn from_str(text: &str) -> Result<Path, Self::Err> {
1062        Ok(Path::read_svg_path(Cursor::new(text))?)
1063    }
1064}
1065
1066#[cfg(feature = "serde")]
1067impl Serialize for Path {
1068    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1069    where
1070        S: serde::Serializer,
1071    {
1072        serializer.collect_str(self)
1073    }
1074}
1075
1076#[cfg(feature = "serde")]
1077impl<'de> Deserialize<'de> for Path {
1078    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1079    where
1080        D: serde::Deserializer<'de>,
1081    {
1082        std::borrow::Cow::<'de, str>::deserialize(deserializer)?
1083            .parse()
1084            .map_err(serde::de::Error::custom)
1085    }
1086}
1087
1088#[cfg(test)]
1089mod tests {
1090    use super::*;
1091    use crate::{PI, assert_approx_eq};
1092
1093    fn assert_path_eq(p0: &Path, p1: &Path) {
1094        assert_eq!(format!("{:#}", p0), format!("{:#}", p1));
1095    }
1096
1097    #[test]
1098    fn test_bbox() {
1099        let path: Path = SQUIRREL.parse().unwrap();
1100        let bbox = path.bbox(Transform::identity()).unwrap();
1101        assert_approx_eq!(bbox.x(), 0.25);
1102        assert_approx_eq!(bbox.y(), 1.0);
1103        assert_approx_eq!(bbox.width(), 15.75);
1104        assert_approx_eq!(bbox.height(), 14.0);
1105    }
1106
1107    const SQUIRREL: &str = r#"
1108    M12 1C9.79 1 8 2.31 8 3.92c0 1.94.5 3.03 0 6.08 0-4.5-2.77-6.34-4-6.34.05-.5-.48
1109    -.66-.48-.66s-.22.11-.3.34c-.27-.31-.56-.27-.56-.27l-.13.58S.7 4.29 .68 6.87c.2.33
1110    1.53.6 2.47.43.89.05.67.79.47.99C2.78 9.13 2 8 1 8S0 9 1 9s1 1 3 1c-3.09 1.2 0 4 0 4
1111    H3c-1 0-1 1-1 1h6c3 0 5-1 5-3.47 0-.85-.43-1.79 -1-2.53-1.11-1.46.23-2.68 1-2
1112    .77.68 3 1 3-2 0-2.21-1.79-4-4-4zM2.5 6 c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5
1113    -.22.5-.5.5z
1114    "#;
1115
1116    #[test]
1117    fn test_path_parse() -> Result<(), SvgParserError> {
1118        // complicated path
1119        let path: Path = SQUIRREL.parse()?;
1120        let reference = Path::builder()
1121            .move_to((12.0, 1.0))
1122            .cubic_to((9.79, 1.0), (8.0, 2.31), (8.0, 3.92))
1123            .cubic_to((8.0, 5.86), (8.5, 6.95), (8.0, 10.0))
1124            .cubic_to((8.0, 5.5), (5.23, 3.66), (4.0, 3.66))
1125            .cubic_to((4.05, 3.16), (3.52, 3.0), (3.52, 3.0))
1126            .cubic_to((3.52, 3.0), (3.3, 3.11), (3.22, 3.34))
1127            .cubic_to((2.95, 3.03), (2.66, 3.07), (2.66, 3.07))
1128            .line_to((2.53, 3.65))
1129            .cubic_to((2.53, 3.65), (0.7, 4.29), (0.68, 6.87))
1130            .cubic_to((0.88, 7.2), (2.21, 7.47), (3.15, 7.3))
1131            .cubic_to((4.04, 7.35), (3.82, 8.09), (3.62, 8.29))
1132            .cubic_to((2.78, 9.13), (2.0, 8.0), (1.0, 8.0))
1133            .cubic_to((0.0, 8.0), (0.0, 9.0), (1.0, 9.0))
1134            .cubic_to((2.0, 9.0), (2.0, 10.0), (4.0, 10.0))
1135            .cubic_to((0.91, 11.2), (4.0, 14.0), (4.0, 14.0))
1136            .line_to((3.0, 14.0))
1137            .cubic_to((2.0, 14.0), (2.0, 15.0), (2.0, 15.0))
1138            .line_to((8.0, 15.0))
1139            .cubic_to((11.0, 15.0), (13.0, 14.0), (13.0, 11.53))
1140            .cubic_to((13.0, 10.68), (12.57, 9.74), (12.0, 9.0))
1141            .cubic_to((10.89, 7.54), (12.23, 6.32), (13.0, 7.0))
1142            .cubic_to((13.77, 7.68), (16.0, 8.0), (16.0, 5.0))
1143            .cubic_to((16.0, 2.79), (14.21, 1.0), (12.0, 1.0))
1144            .close()
1145            .move_to((2.5, 6.0))
1146            .cubic_to((2.22, 6.0), (2.0, 5.78), (2.0, 5.5))
1147            .cubic_to((2.0, 5.22), (2.22, 5.0), (2.5, 5.0))
1148            .cubic_to((2.78, 5.0), (3.0, 5.22), (3.0, 5.5))
1149            .cubic_to((3.0, 5.78), (2.78, 6.0), (2.5, 6.0))
1150            .close()
1151            .build();
1152        assert_path_eq(&path, &reference);
1153
1154        let path: Path = " M0,0L1-1L1,0ZL0,1 L1,1Z ".parse()?;
1155        let reference = Path::builder()
1156            .move_to((0.0, 0.0))
1157            .line_to((1.0, -1.0))
1158            .line_to((1.0, 0.0))
1159            .close()
1160            .move_to((0.0, 0.0))
1161            .line_to((0.0, 1.0))
1162            .line_to((1.0, 1.0))
1163            .close()
1164            .build();
1165        assert_path_eq(&path, &reference);
1166
1167        let reference = Path::builder()
1168            .move_to((0.5, -3.0))
1169            .line_to((-11.0, -0.11))
1170            .build();
1171        // not separated scalars, implicit line segment
1172        let p1: Path = "M.5-3-11-.11".parse()?;
1173        // other spaces, implicit relative line segment
1174        let p2: Path = " m.5,-3 -11.5\n2.89 ".parse()?;
1175        assert_path_eq(&reference, &p1);
1176        assert_path_eq(&reference, &p2);
1177
1178        Ok(())
1179    }
1180
1181    #[test]
1182    fn test_save_load() -> std::io::Result<()> {
1183        let path: Path = SQUIRREL.parse()?;
1184        let mut path_save = Vec::new();
1185        path.write_svg_path(&mut path_save)?;
1186        let path_load = Path::read_svg_path(std::io::Cursor::new(path_save))?;
1187        assert_path_eq(&path, &path_load);
1188        Ok(())
1189    }
1190
1191    #[test]
1192    fn test_flatten() -> Result<(), SvgParserError> {
1193        let path: Path = SQUIRREL.parse()?;
1194        let tr = Transform::new_rotate(PI / 3.0).pre_translate(-10.0, -20.0);
1195        let lines: Vec<_> = path.flatten(tr, DEFAULT_FLATNESS, true).collect();
1196        let mut reference = Vec::new();
1197        for subpath in path.subpaths() {
1198            let subpath_lines: Vec<_> = subpath.flatten(tr, DEFAULT_FLATNESS, false).collect();
1199            // line are connected
1200            for ls in subpath_lines.windows(2) {
1201                assert!(ls[0].end().is_close_to(ls[1].start()));
1202            }
1203            reference.extend(subpath_lines);
1204        }
1205        assert_eq!(reference.len(), lines.len());
1206        for (l0, l1) in lines.iter().zip(reference.iter()) {
1207            assert!(l0.start().is_close_to(l1.start()));
1208            assert!(l0.end().is_close_to(l1.end()));
1209        }
1210        Ok(())
1211    }
1212
1213    #[test]
1214    fn test_winding() -> Result<(), SvgParserError> {
1215        let path: Path = SQUIRREL.parse()?;
1216        assert_eq!(path.winding_at((2.4, 5.4)), 0);
1217        assert_eq!(path.winding_at((3.5, 3.155)), 1);
1218        assert_eq!(path.winding_at((3.92, 3.155)), 0);
1219        assert_eq!(path.winding_at((12.46, 6.87)), 0);
1220        assert_eq!(path.winding_at((14.24, 7.455)), 1);
1221        Ok(())
1222    }
1223
1224    #[test]
1225    fn test_stroke() -> Result<(), SvgParserError> {
1226        // open path
1227        let path: Path = "M2,2L8,2C11,2 11,8 8,8L5,4".parse()?;
1228
1229        let path_stroke = path.stroke(StrokeStyle {
1230            width: 1.0,
1231            ..Default::default()
1232        });
1233        let path_reference: Path = r#"
1234        M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 L7.75,8.5 7.6,8.3 4.6,4.3
1235        5.4,3.7 8.4,7.7 8,7.5 C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5 L2,2.5 2,1.5 Z
1236        "#.parse()?;
1237        assert_path_eq(&path_reference, &path_stroke);
1238
1239        let path_stroke = path.stroke(StrokeStyle {
1240            width: 1.0,
1241            line_cap: LineCap::Round,
1242            line_join: LineJoin::Round,
1243        });
1244        let path_reference: Path = r#"
1245        M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 7.84274,8.5 7.69436,8.42581
1246        7.6,8.3 L4.6,4.3 C4.43542,4.08057 4.48057,3.76458 4.7,3.6 4.91943,3.43542 5.23542,3.48057 5.4,3.7 L8.4,7.7 8,7.5
1247        C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5 L2,2.5 C1.72571,2.5 1.5,2.27429 1.5,2
1248        1.5,1.72571 1.72571,1.5 2,1.5 Z
1249        "#.parse()?;
1250        assert_path_eq(&path_reference, &path_stroke);
1251
1252        // closed path
1253        let path: Path = "M2,2L8,2C11,2 11,8 8,8L5,4Z".parse()?;
1254
1255        let path_stroke = path.stroke(StrokeStyle {
1256            width: 1.0,
1257            line_cap: LineCap::Round,
1258            line_join: LineJoin::Round,
1259        });
1260        let path_reference: Path = r#"
1261        M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 7.84274,8.5 7.69436,8.42581
1262        7.6,8.3 L4.6,4.3 4.72265,4.41603 1.72265,2.41603 C1.53984,2.29415 1.45778,2.06539 1.52145,1.85511 1.58512,1.64482
1263        1.78029,1.5 2,1.5 ZM5.4,3.7 L8.4,7.7 8,7.5 C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5
1264        L2,2.5 2.27735,1.58397 5.27735,3.58397 C5.32451,3.61542 5.36599,3.65465 5.4,3.7 Z
1265        "#.parse()?;
1266        assert_path_eq(&path_reference, &path_stroke);
1267
1268        Ok(())
1269    }
1270
1271    #[test]
1272    fn test_reverse() -> Result<(), SvgParserError> {
1273        let mut path: Path =
1274            "M2,2L8,2C11,2 11,8 8,8L5,4ZM7,3L8,6C9,6 10,5 9,4ZM2,4Q2,8 3,8L5,8C6,8 2,3 2,4Z"
1275                .parse()?;
1276        path.reverse();
1277        let path_reference =
1278            "M2,4C2,3 6,8 5,8L3,8Q2,8 2,4ZM9,4C10,5 9,6 8,6L7,3ZM5,4L8,8C11,8 11,2 8,2L2,2Z"
1279                .parse()?;
1280        assert_path_eq(&path_reference, &path);
1281        Ok(())
1282    }
1283
1284    #[test]
1285    fn test_display() -> Result<(), SvgParserError> {
1286        let path: Path = SQUIRREL.parse()?;
1287
1288        let path_display = format!("{:#}", path);
1289        assert_path_eq(&path, &path_display.parse()?);
1290        let path_reference = "M12,1C9.79,1 8,2.31 8,3.92C8,5.86 8.5,6.95 8,10C8,5.5 5.23,3.66 4,3.66C4.05,3.16 3.52,3 3.52,3C3.52,3 3.3,3.11 3.22,3.34C2.95,3.03 2.66,3.07 2.66,3.07L2.53,3.65C2.53,3.65 0.7,4.29 0.68,6.87C0.88,7.2 2.21,7.47 3.15,7.3C4.04,7.35 3.82,8.09 3.62,8.29C2.78,9.13 2,8 1,8C0,8 0,9 1,9C2,9 2,10 4,10C0.91,11.2 4,14 4,14L3,14C2,14 2,15 2,15L8,15C11,15 13,14 13,11.53C13,10.68 12.57,9.74 12,9C10.89,7.54 12.23,6.32 13,7C13.77,7.68 16,8 16,5C16,2.79 14.21,1 12,1ZM2.5,6C2.22,6 2,5.78 2,5.5C2,5.22 2.22,5 2.5,5C2.78,5 3,5.22 3,5.5C3,5.78 2.78,6 2.5,6Z";
1291        assert_eq!(path_reference, path_display.as_str());
1292
1293        let path_relative = format!("{:#}", path.display(true, Transform::identity()));
1294        assert_path_eq(&path, &path_relative.parse()?);
1295        let path_reference = "M12,1c-2.21,0-4,1.31-4,2.92c0,1.94 0.5,3.03 0,6.08c0-4.5-2.77-6.34-4-6.34c0.05-0.5-0.48-0.66-0.48-0.66c0,0-0.22,0.11-0.3,0.34c-0.27-0.31-0.56-0.27-0.56-0.27l-0.13,0.58c0,0-1.83,0.64-1.85,3.22c0.2,0.33 1.53,0.6 2.47,0.43c0.89,0.05 0.67,0.79 0.47,0.99c-0.84,0.84-1.62-0.29-2.62-0.29c-1,0-1,1 0,1c1,0 1,1 3,1c-3.09,1.2 0,4 0,4l-1,0c-1,0-1,1-1,1l6,0c3,0 5-1 5-3.47c0-0.85-0.43-1.79-1-2.53c-1.11-1.46 0.23-2.68 1-2c0.77,0.68 3,1 3-2c0-2.21-1.79-4-4-4Zm-9.5,5c-0.28,0-0.5-0.22-0.5-0.5c0-0.28 0.22-0.5 0.5-0.5c0.28,0 0.5,0.22 0.5,0.5c0,0.28-0.22,0.5-0.5,0.5Z";
1296        assert_eq!(path_reference, path_relative.as_str());
1297
1298        Ok(())
1299    }
1300}