lyon_path/
builder.rs

1//! Path building utilities.
2//!
3//! ## `PathBuilder` or `SvgPathBuilder`
4//!
5//! Path can be built via either of two abstractions:
6//!
7//! - [PathBuilder](trait.PathBuilder.html) is a simple and efficient interface which
8//!   does not deal with any ambiguous cases.
9//! - [SvgPathBuilder](trait.SvgPathBuilder.html) is a higher-level interface that
10//!   follows SVG's specification, removing the the burden of dealing with special cases
11//!   from the user at a run-time cost.
12//!
13//! `SvgPathBuilder` may be a better choice when interactive with SVG, or dealing with arbitrary
14//! input. `PathBuilder`. `PathBuilder` is probably a more useful trait to implement when creating
15//! a new path data structure since all `PathBuilder` implementations automatically get an
16//! `SvgPathBuilder` adapter (see the `with_svg` method). It may also make sense to use the
17//! `PathBuilder` API when following a specification that behaves like SVG paths or when no
18//! performance can be traded for convenience.
19//!
20//! ## Examples
21//!
22//! The following example shows how to create a simple path using the
23//! [PathBuilder](trait.PathBuilder.html) interface.
24//!
25//! ```
26//! use lyon_path::{Path, geom::point};
27//!
28//! let mut builder = Path::builder();
29//!
30//! // All sub-paths *must* have be contained in a begin/end pair.
31//! builder.begin(point(0.0, 0.0));
32//! builder.line_to(point(1.0, 0.0));
33//! builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0));
34//! builder.end(false);
35//!
36//! builder.begin(point(10.0, 0.0));
37//! builder.cubic_bezier_to(point(12.0, 2.0), point(11.0, 2.0), point(5.0, 0.0));
38//! builder.close(); // close() is equivalent to end(true).
39//!
40//! let path = builder.build();
41//! ```
42//!
43//! The same path can be built using the `SvgPathBuilder` API:
44//!
45//! ```
46//! use lyon_path::{Path, geom::{point, vector}, builder::SvgPathBuilder};
47//!
48//! // Use the SVG adapter.
49//! let mut builder = Path::builder().with_svg();
50//!
51//! // All sub-paths *must* have be contained in a begin/end pair.
52//! builder.move_to(point(0.0, 0.0));
53//! builder.line_to(point(1.0, 0.0));
54//! builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0));
55//! // No need to explicitly end a sub-path.
56//!
57//! builder.move_to(point(10.0, 0.0));
58//! builder.relative_cubic_bezier_to(vector(2.0, 2.0), vector(1.0, 2.0), vector(-5.0, 0.0));
59//! builder.close();
60//!
61//! let path = builder.build();
62//! ```
63//!
64//! Implementors of the `PathBuilder` trait automatically gain access to a few other adapters.
65//! For example a builder that approximates curves with a sequence of line segments:
66//!
67//! ```
68//! use lyon_path::{Path, geom::point};
69//!
70//! let tolerance = 0.05;// maximum distance between a curve and its approximation.
71//! let mut builder = Path::builder().flattened(tolerance);
72//!
73//! builder.begin(point(0.0, 0.0));
74//! builder.quadratic_bezier_to(point(1.0, 0.0), point(1.0, 1.0));
75//! builder.end(true);
76//!
77//! // The resulting path contains only Begin, Line and End events.
78//! let path = builder.build();
79//! ```
80//!
81
82use crate::events::{Event, PathEvent};
83use crate::geom::{traits::Transformation, Arc, ArcFlags, LineSegment, SvgArc};
84use crate::math::*;
85use crate::path::Verb;
86use crate::polygon::Polygon;
87use crate::{Attributes, EndpointId, Winding, NO_ATTRIBUTES};
88
89use core::f32::consts::PI;
90use core::marker::Sized;
91
92use alloc::vec;
93use alloc::vec::Vec;
94
95#[cfg(not(feature = "std"))]
96use num_traits::Float;
97
98/// The radius of each corner of a rounded rectangle.
99#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
100pub struct BorderRadii {
101    pub top_left: f32,
102    pub top_right: f32,
103    pub bottom_left: f32,
104    pub bottom_right: f32,
105}
106
107impl BorderRadii {
108    pub fn new(radius: f32) -> Self {
109        let r = radius.abs();
110        BorderRadii {
111            top_left: r,
112            top_right: r,
113            bottom_left: r,
114            bottom_right: r,
115        }
116    }
117}
118
119impl core::fmt::Display for BorderRadii {
120    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121        // In the order of a well known convention (CSS) clockwise from top left
122        write!(
123            f,
124            "BorderRadii({}, {}, {}, {})",
125            self.top_left, self.top_right, self.bottom_left, self.bottom_right
126        )
127    }
128}
129
130/// A convenience wrapper for `PathBuilder` without custom attributes.
131///
132/// See the [PathBuilder] trait.
133///
134/// This simply forwards to an underlying `PathBuilder` implementation,
135/// using no attributes.
136#[derive(Clone, Debug, PartialEq, Hash)]
137#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
138pub struct NoAttributes<B: PathBuilder> {
139    pub(crate) inner: B,
140}
141
142impl<B: PathBuilder> NoAttributes<B> {
143    #[inline]
144    pub fn wrap(inner: B) -> Self {
145        assert_eq!(inner.num_attributes(), 0);
146        NoAttributes { inner }
147    }
148
149    pub fn new() -> Self
150    where
151        B: Default,
152    {
153        NoAttributes::wrap(B::default())
154    }
155
156    pub fn with_capacity(endpoints: usize, ctrl_points: usize) -> Self
157    where
158        B: Default,
159    {
160        let mut builder = B::default();
161        builder.reserve(endpoints, ctrl_points);
162        NoAttributes::wrap(builder)
163    }
164
165    /// Starts a new sub-path at a given position.
166    ///
167    /// There must be no sub-path in progress when this method is called.
168    /// `at` becomes the current position of the sub-path.
169    #[inline]
170    pub fn begin(&mut self, at: Point) -> EndpointId {
171        self.inner.begin(at, NO_ATTRIBUTES)
172    }
173
174    /// Ends the current sub path.
175    ///
176    /// A sub-path must be in progress when this method is called.
177    /// After this method is called, there is no sub-path in progress until
178    /// `begin` is called again.
179    #[inline]
180    pub fn end(&mut self, close: bool) {
181        self.inner.end(close);
182    }
183
184    /// Closes the current sub path.
185    ///
186    /// Shorthand for `builder.end(true)`.
187    #[inline]
188    pub fn close(&mut self) {
189        self.inner.close();
190    }
191
192    /// Adds a line segment to the current sub-path.
193    ///
194    /// A sub-path must be in progress when this method is called.
195    #[inline]
196    pub fn line_to(&mut self, to: Point) -> EndpointId {
197        self.inner.line_to(to, NO_ATTRIBUTES)
198    }
199
200    /// Adds a quadratic bézier curve to the current sub-path.
201    ///
202    /// A sub-path must be in progress when this method is called.
203    #[inline]
204    pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId {
205        self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES)
206    }
207
208    /// Adds a cubic bézier curve to the current sub-path.
209    ///
210    /// A sub-path must be in progress when this method is called.
211    #[inline]
212    pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId {
213        self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES)
214    }
215
216    /// Hints at the builder that a certain number of endpoints and control
217    /// points will be added.
218    ///
219    /// The Builder implementation may use this information to pre-allocate
220    /// memory as an optimization.
221    #[inline]
222    pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
223        self.inner.reserve(endpoints, ctrl_points);
224    }
225
226    /// Applies the provided path event.
227    ///
228    /// By default this calls one of `begin`, `end`, `line`, `quadratic_bezier_segment`,
229    /// or `cubic_bezier_segment` according to the path event.
230    ///
231    /// The requirements for each method apply to the corresponding event.
232    #[inline]
233    pub fn path_event(&mut self, event: PathEvent) {
234        self.inner.path_event(event, NO_ATTRIBUTES);
235    }
236
237    /// Adds a sub-path from a polygon.
238    ///
239    /// There must be no sub-path in progress when this method is called.
240    /// No sub-path is in progress after the method is called.
241    #[inline]
242    pub fn add_polygon(&mut self, polygon: Polygon<Point>) {
243        self.inner.add_polygon(polygon, NO_ATTRIBUTES);
244    }
245
246    /// Adds a sub-path containing a single point.
247    ///
248    /// There must be no sub-path in progress when this method is called.
249    /// No sub-path is in progress after the method is called.
250    #[inline]
251    pub fn add_point(&mut self, at: Point) -> EndpointId {
252        self.inner.add_point(at, NO_ATTRIBUTES)
253    }
254
255    /// Adds a sub-path containing a single line segment.
256    ///
257    /// There must be no sub-path in progress when this method is called.
258    /// No sub-path is in progress after the method is called.
259    #[inline]
260    pub fn add_line_segment(&mut self, line: &LineSegment<f32>) -> (EndpointId, EndpointId) {
261        self.inner.add_line_segment(line, NO_ATTRIBUTES)
262    }
263
264    /// Adds a sub-path containing an ellipse.
265    ///
266    /// There must be no sub-path in progress when this method is called.
267    /// No sub-path is in progress after the method is called.
268    #[inline]
269    pub fn add_ellipse(
270        &mut self,
271        center: Point,
272        radii: Vector,
273        x_rotation: Angle,
274        winding: Winding,
275    ) {
276        self.inner
277            .add_ellipse(center, radii, x_rotation, winding, NO_ATTRIBUTES);
278    }
279
280    /// Adds a sub-path containing a circle.
281    ///
282    /// There must be no sub-path in progress when this method is called.
283    /// No sub-path is in progress after the method is called.
284    #[inline]
285    pub fn add_circle(&mut self, center: Point, radius: f32, winding: Winding)
286    where
287        B: Sized,
288    {
289        self.inner
290            .add_circle(center, radius, winding, NO_ATTRIBUTES);
291    }
292
293    /// Adds a sub-path containing a rectangle.
294    ///
295    /// There must be no sub-path in progress when this method is called.
296    /// No sub-path is in progress after the method is called.
297    #[inline]
298    pub fn add_rectangle(&mut self, rect: &Box2D, winding: Winding) {
299        self.inner.add_rectangle(rect, winding, NO_ATTRIBUTES);
300    }
301
302    /// Adds a sub-path containing a rectangle.
303    ///
304    /// There must be no sub-path in progress when this method is called.
305    /// No sub-path is in progress after the method is called.
306    #[inline]
307    pub fn add_rounded_rectangle(&mut self, rect: &Box2D, radii: &BorderRadii, winding: Winding)
308    where
309        B: Sized,
310    {
311        self.inner
312            .add_rounded_rectangle(rect, radii, winding, NO_ATTRIBUTES);
313    }
314
315    /// Returns a builder that approximates all curves with sequences of line segments.
316    #[inline]
317    pub fn flattened(self, tolerance: f32) -> NoAttributes<Flattened<B>>
318    where
319        B: Sized,
320    {
321        NoAttributes {
322            inner: Flattened::new(self.inner, tolerance),
323        }
324    }
325
326    /// Returns a builder that applies the given transformation to all positions.
327    #[inline]
328    pub fn transformed<Transform>(
329        self,
330        transform: Transform,
331    ) -> NoAttributes<Transformed<B, Transform>>
332    where
333        B: Sized,
334        Transform: Transformation<f32>,
335    {
336        NoAttributes {
337            inner: Transformed::new(self.inner, transform),
338        }
339    }
340
341    /// Returns a builder that support SVG commands.
342    ///
343    /// This must be called before starting to add any sub-path.
344    #[inline]
345    pub fn with_svg(self) -> WithSvg<B>
346    where
347        B: Sized,
348    {
349        WithSvg::new(self.inner)
350    }
351
352    /// Builds a path object, consuming the builder.
353    #[inline]
354    pub fn build<P>(self) -> P
355    where
356        B: Build<PathType = P>,
357    {
358        self.inner.build()
359    }
360
361    #[inline]
362    pub fn inner(&self) -> &B {
363        &self.inner
364    }
365
366    #[inline]
367    pub fn inner_mut(&mut self) -> &mut B {
368        &mut self.inner
369    }
370
371    #[inline]
372    pub fn into_inner(self) -> B {
373        self.inner
374    }
375}
376
377impl<B: PathBuilder> PathBuilder for NoAttributes<B> {
378    #[inline]
379    fn num_attributes(&self) -> usize {
380        0
381    }
382
383    #[inline]
384    fn begin(&mut self, at: Point, _attributes: Attributes) -> EndpointId {
385        self.inner.begin(at, NO_ATTRIBUTES)
386    }
387
388    #[inline]
389    fn end(&mut self, close: bool) {
390        self.inner.end(close);
391    }
392
393    #[inline]
394    fn line_to(&mut self, to: Point, _attributes: Attributes) -> EndpointId {
395        self.inner.line_to(to, NO_ATTRIBUTES)
396    }
397
398    #[inline]
399    fn quadratic_bezier_to(
400        &mut self,
401        ctrl: Point,
402        to: Point,
403        _attributes: Attributes,
404    ) -> EndpointId {
405        self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES)
406    }
407
408    #[inline]
409    fn cubic_bezier_to(
410        &mut self,
411        ctrl1: Point,
412        ctrl2: Point,
413        to: Point,
414        _attributes: Attributes,
415    ) -> EndpointId {
416        self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES)
417    }
418
419    #[inline]
420    fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
421        self.inner.reserve(endpoints, ctrl_points)
422    }
423}
424
425impl<B: PathBuilder + Build> Build for NoAttributes<B> {
426    type PathType = B::PathType;
427
428    fn build(self) -> B::PathType {
429        self.inner.build()
430    }
431}
432
433impl<B: PathBuilder + Default> Default for NoAttributes<B> {
434    fn default() -> Self {
435        Self::new()
436    }
437}
438
439/// The base path building interface.
440///
441/// Unlike `SvgPathBuilder`, this interface strictly requires sub-paths to be manually
442/// started and ended (See the `begin` and `end` methods).
443/// All positions are provided in absolute coordinates.
444///
445/// The goal of this interface is to abstract over simple and fast implementations that
446/// do not deal with corner cases such as adding segments without starting a sub-path.
447///
448/// More elaborate interfaces are built on top of the provided primitives. In particular,
449/// the `SvgPathBuilder` trait providing more permissive and richer interface is
450/// automatically implemented via the `WithSvg` adapter (See the `with_svg` method).
451pub trait PathBuilder {
452    fn num_attributes(&self) -> usize;
453    /// Starts a new sub-path at a given position.
454    ///
455    /// There must be no sub-path in progress when this method is called.
456    /// `at` becomes the current position of the sub-path.
457    fn begin(&mut self, at: Point, custom_attributes: Attributes) -> EndpointId;
458
459    /// Ends the current sub path.
460    ///
461    /// A sub-path must be in progress when this method is called.
462    /// After this method is called, there is no sub-path in progress until
463    /// `begin` is called again.
464    fn end(&mut self, close: bool);
465
466    /// Closes the current sub path.
467    ///
468    /// Shorthand for `builder.end(true)`.
469    fn close(&mut self) {
470        self.end(true)
471    }
472
473    /// Adds a line segment to the current sub-path.
474    ///
475    /// A sub-path must be in progress when this method is called.
476    fn line_to(&mut self, to: Point, custom_attributes: Attributes) -> EndpointId;
477
478    /// Adds a quadratic bézier curve to the current sub-path.
479    ///
480    /// A sub-path must be in progress when this method is called.
481    fn quadratic_bezier_to(
482        &mut self,
483        ctrl: Point,
484        to: Point,
485        custom_attributes: Attributes,
486    ) -> EndpointId;
487
488    /// Adds a cubic bézier curve to the current sub-path.
489    ///
490    /// A sub-path must be in progress when this method is called.
491    fn cubic_bezier_to(
492        &mut self,
493        ctrl1: Point,
494        ctrl2: Point,
495        to: Point,
496        custom_attributes: Attributes,
497    ) -> EndpointId;
498
499    /// Hints at the builder that a certain number of endpoints and control
500    /// points will be added.
501    ///
502    /// The Builder implementation may use this information to pre-allocate
503    /// memory as an optimization.
504    fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {}
505
506    /// Applies the provided path event.
507    ///
508    /// By default this calls one of `begin`, `end`, `line`, `quadratic_bezier_segment`,
509    /// or `cubic_bezier_segment` according to the path event.
510    ///
511    /// The requirements for each method apply to the corresponding event.
512    fn path_event(&mut self, event: PathEvent, attributes: Attributes) {
513        match event {
514            PathEvent::Begin { at } => {
515                self.begin(at, attributes);
516            }
517            PathEvent::Line { to, .. } => {
518                self.line_to(to, attributes);
519            }
520            PathEvent::Quadratic { ctrl, to, .. } => {
521                self.quadratic_bezier_to(ctrl, to, attributes);
522            }
523            PathEvent::Cubic {
524                ctrl1, ctrl2, to, ..
525            } => {
526                self.cubic_bezier_to(ctrl1, ctrl2, to, attributes);
527            }
528            PathEvent::End { close, .. } => {
529                self.end(close);
530            }
531        }
532    }
533
534    fn event(&mut self, event: Event<(Point, Attributes), Point>) {
535        match event {
536            Event::Begin { at } => {
537                self.begin(at.0, at.1);
538            }
539            Event::Line { to, .. } => {
540                self.line_to(to.0, to.1);
541            }
542            Event::Quadratic { ctrl, to, .. } => {
543                self.quadratic_bezier_to(ctrl, to.0, to.1);
544            }
545            Event::Cubic {
546                ctrl1, ctrl2, to, ..
547            } => {
548                self.cubic_bezier_to(ctrl1, ctrl2, to.0, to.1);
549            }
550            Event::End { close, .. } => {
551                self.end(close);
552            }
553        }
554    }
555
556    /// Adds a sub-path from a polygon.
557    ///
558    /// There must be no sub-path in progress when this method is called.
559    /// No sub-path is in progress after the method is called.
560    fn add_polygon(&mut self, polygon: Polygon<Point>, attributes: Attributes) {
561        if polygon.points.is_empty() {
562            return;
563        }
564
565        self.reserve(polygon.points.len(), 0);
566
567        self.begin(polygon.points[0], attributes);
568        for p in &polygon.points[1..] {
569            self.line_to(*p, attributes);
570        }
571
572        self.end(polygon.closed);
573    }
574
575    /// Adds a sub-path containing a single point.
576    ///
577    /// There must be no sub-path in progress when this method is called.
578    /// No sub-path is in progress after the method is called.
579    fn add_point(&mut self, at: Point, attributes: Attributes) -> EndpointId {
580        let id = self.begin(at, attributes);
581        self.end(false);
582
583        id
584    }
585
586    /// Adds a sub-path containing a single line segment.
587    ///
588    /// There must be no sub-path in progress when this method is called.
589    /// No sub-path is in progress after the method is called.
590    fn add_line_segment(
591        &mut self,
592        line: &LineSegment<f32>,
593        attributes: Attributes,
594    ) -> (EndpointId, EndpointId) {
595        let a = self.begin(line.from, attributes);
596        let b = self.line_to(line.to, attributes);
597        self.end(false);
598
599        (a, b)
600    }
601
602    /// Adds a sub-path containing an ellipse.
603    ///
604    /// There must be no sub-path in progress when this method is called.
605    /// No sub-path is in progress after the method is called.
606    fn add_ellipse(
607        &mut self,
608        center: Point,
609        radii: Vector,
610        x_rotation: Angle,
611        winding: Winding,
612        attributes: Attributes,
613    ) {
614        let dir = match winding {
615            Winding::Positive => 1.0,
616            Winding::Negative => -1.0,
617        };
618
619        let arc = Arc {
620            center,
621            radii,
622            x_rotation,
623            start_angle: Angle::radians(0.0),
624            sweep_angle: Angle::radians(2.0 * PI) * dir,
625        };
626
627        self.begin(arc.sample(0.0), attributes);
628        arc.for_each_quadratic_bezier(&mut |curve| {
629            self.quadratic_bezier_to(curve.ctrl, curve.to, attributes);
630        });
631        self.end(true);
632    }
633
634    /// Adds a sub-path containing a circle.
635    ///
636    /// There must be no sub-path in progress when this method is called.
637    /// No sub-path is in progress after the method is called.
638    fn add_circle(&mut self, center: Point, radius: f32, winding: Winding, attributes: Attributes)
639    where
640        Self: Sized,
641    {
642        add_circle(self, center, radius, winding, attributes);
643    }
644
645    /// Adds a sub-path containing a rectangle.
646    ///
647    /// There must be no sub-path in progress when this method is called.
648    /// No sub-path is in progress after the method is called.
649    fn add_rectangle(&mut self, rect: &Box2D, winding: Winding, attributes: Attributes) {
650        match winding {
651            Winding::Positive => self.add_polygon(
652                Polygon {
653                    points: &[
654                        rect.min,
655                        point(rect.max.x, rect.min.y),
656                        rect.max,
657                        point(rect.min.x, rect.max.y),
658                    ],
659                    closed: true,
660                },
661                attributes,
662            ),
663            Winding::Negative => self.add_polygon(
664                Polygon {
665                    points: &[
666                        rect.min,
667                        point(rect.min.x, rect.max.y),
668                        rect.max,
669                        point(rect.max.x, rect.min.y),
670                    ],
671                    closed: true,
672                },
673                attributes,
674            ),
675        };
676    }
677
678    /// Adds a sub-path containing a rectangle.
679    ///
680    /// There must be no sub-path in progress when this method is called.
681    /// No sub-path is in progress after the method is called.
682    fn add_rounded_rectangle(
683        &mut self,
684        rect: &Box2D,
685        radii: &BorderRadii,
686        winding: Winding,
687        custom_attributes: Attributes,
688    ) where
689        Self: Sized,
690    {
691        add_rounded_rectangle(self, rect, radii, winding, custom_attributes);
692    }
693
694    /// Returns a builder that approximates all curves with sequences of line segments.
695    fn flattened(self, tolerance: f32) -> Flattened<Self>
696    where
697        Self: Sized,
698    {
699        Flattened::new(self, tolerance)
700    }
701
702    /// Returns a builder that applies the given transformation to all positions.
703    fn transformed<Transform>(self, transform: Transform) -> Transformed<Self, Transform>
704    where
705        Self: Sized,
706        Transform: Transformation<f32>,
707    {
708        Transformed::new(self, transform)
709    }
710
711    /// Returns a builder that support SVG commands.
712    ///
713    /// This must be called before starting to add any sub-path.
714    fn with_svg(self) -> WithSvg<Self>
715    where
716        Self: Sized,
717    {
718        WithSvg::new(self)
719    }
720}
721
722/// A path building interface that tries to stay close to SVG's path specification.
723/// <https://svgwg.org/specs/paths/>
724///
725/// Some of the wording in the documentation of this trait is borrowed from the SVG
726/// specification.
727///
728/// Unlike `PathBuilder`, implementations of this trait are expected to deal with
729/// various corners cases such as adding segments without starting a sub-path.
730pub trait SvgPathBuilder {
731    /// Start a new sub-path at the given position.
732    ///
733    /// Corresponding SVG command: `M`.
734    ///
735    /// This command establishes a new initial point and a new current point. The effect
736    /// is as if the "pen" were lifted and moved to a new location.
737    /// If a sub-path is in progress, it is ended without being closed.
738    fn move_to(&mut self, to: Point);
739
740    /// Ends the current sub-path by connecting it back to its initial point.
741    ///
742    /// Corresponding SVG command: `Z`.
743    ///
744    /// A straight line is drawn from the current point to the initial point of the
745    /// current sub-path.
746    /// The current position is set to the initial position of the sub-path that was
747    /// closed.
748    fn close(&mut self);
749
750    /// Adds a line segment to the current sub-path.
751    ///
752    /// Corresponding SVG command: `L`.
753    ///
754    /// The segment starts at the builder's current position.
755    /// If this is the very first command of the path (the builder therefore does not
756    /// have a current position), the `line_to` command is replaced with a `move_to(to)`.
757    fn line_to(&mut self, to: Point);
758
759    /// Adds a quadratic bézier segment to the current sub-path.
760    ///
761    /// Corresponding SVG command: `Q`.
762    ///
763    /// The segment starts at the builder's current position.
764    /// If this is the very first command of the path (the builder therefore does not
765    /// have a current position), the `quadratic_bezier_to` command is replaced with
766    /// a `move_to(to)`.
767    fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point);
768
769    /// Adds a cubic bézier segment to the current sub-path.
770    ///
771    /// Corresponding SVG command: `C`.
772    ///
773    /// The segment starts at the builder's current position.
774    /// If this is the very first command of the path (the builder therefore does not
775    /// have a current position), the `cubic_bezier_to` command is replaced with
776    /// a `move_to(to)`.
777    fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point);
778
779    /// Equivalent to `move_to` in relative coordinates.
780    ///
781    /// Corresponding SVG command: `m`.
782    ///
783    /// The provided coordinates are offsets relative to the current position of
784    /// the builder.
785    fn relative_move_to(&mut self, to: Vector);
786
787    /// Equivalent to `line_to` in relative coordinates.
788    ///
789    /// Corresponding SVG command: `l`.
790    ///
791    /// The provided coordinates are offsets relative to the current position of
792    /// the builder.
793    fn relative_line_to(&mut self, to: Vector);
794
795    /// Equivalent to `quadratic_bezier_to` in relative coordinates.
796    ///
797    /// Corresponding SVG command: `q`.
798    ///
799    /// the provided coordinates are offsets relative to the current position of
800    /// the builder.
801    fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector);
802
803    /// Equivalent to `cubic_bezier_to` in relative coordinates.
804    ///
805    /// Corresponding SVG command: `c`.
806    ///
807    /// The provided coordinates are offsets relative to the current position of
808    /// the builder.
809    fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector);
810
811    /// Equivalent to `cubic_bezier_to` with implicit first control point.
812    ///
813    /// Corresponding SVG command: `S`.
814    ///
815    /// The first control point is assumed to be the reflection of the second
816    /// control point on the previous command relative to the current point.
817    /// If there is no previous command or if the previous command was not a
818    /// cubic bézier segment, the first control point is coincident with
819    /// the current position.
820    fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point);
821
822    /// Equivalent to `smooth_cubic_bezier_to` in relative coordinates.
823    ///
824    /// Corresponding SVG command: `s`.
825    ///
826    /// The provided coordinates are offsets relative to the current position of
827    /// the builder.
828    fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector);
829
830    /// Equivalent to `quadratic_bezier_to` with implicit control point.
831    ///
832    /// Corresponding SVG command: `T`.
833    ///
834    /// The control point is assumed to be the reflection of the control
835    /// point on the previous command relative to the current point.
836    /// If there is no previous command or if the previous command was not a
837    /// quadratic bézier segment, a line segment is added instead.
838    fn smooth_quadratic_bezier_to(&mut self, to: Point);
839
840    /// Equivalent to `smooth_quadratic_bezier_to` in relative coordinates.
841    ///
842    /// Corresponding SVG command: `t`.
843    ///
844    /// The provided coordinates are offsets relative to the current position of
845    /// the builder.
846    fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector);
847
848    /// Adds an horizontal line segment.
849    ///
850    /// Corresponding SVG command: `H`.
851    ///
852    /// Equivalent to `line_to`, using the y coordinate of the current position.
853    fn horizontal_line_to(&mut self, x: f32);
854
855    /// Adds an horizontal line segment in relative coordinates.
856    ///
857    /// Corresponding SVG command: `l`.
858    ///
859    /// Equivalent to `line_to`, using the y coordinate of the current position.
860    /// `dx` is the horizontal offset relative to the current position.
861    fn relative_horizontal_line_to(&mut self, dx: f32);
862
863    /// Adds a vertical line segment.
864    ///
865    /// Corresponding SVG command: `V`.
866    ///
867    /// Equivalent to `line_to`, using the x coordinate of the current position.
868    fn vertical_line_to(&mut self, y: f32);
869
870    /// Adds a vertical line segment in relative coordinates.
871    ///
872    /// Corresponding SVG command: `v`.
873    ///
874    /// Equivalent to `line_to`, using the y coordinate of the current position.
875    /// `dy` is the horizontal offset relative to the current position.
876    fn relative_vertical_line_to(&mut self, dy: f32);
877
878    /// Adds an elliptical arc.
879    ///
880    /// Corresponding SVG command: `A`.
881    ///
882    /// The arc starts at the current point and ends at `to`.
883    /// The size and orientation of the ellipse are defined by `radii` and an `x_rotation`,
884    /// which indicates how the ellipse as a whole is rotated relative to the current coordinate
885    /// system. The center of the ellipse is calculated automatically to satisfy the constraints
886    /// imposed by the other parameters. the arc `flags` contribute to the automatic calculations
887    /// and help determine how the arc is built.
888    fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point);
889
890    /// Equivalent to `arc_to` in relative coordinates.
891    ///
892    /// Corresponding SVG command: `a`.
893    ///
894    /// The provided `to` coordinates are offsets relative to the current position of
895    /// the builder.
896    fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector);
897
898    /// Hints at the builder that a certain number of endpoints and control
899    /// points will be added.
900    ///
901    /// The Builder implementation may use this information to pre-allocate
902    /// memory as an optimization.
903    fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {}
904
905    /// Adds a sub-path from a polygon.
906    ///
907    /// There must be no sub-path in progress when this method is called.
908    /// No sub-path is in progress after the method is called.
909    fn add_polygon(&mut self, polygon: Polygon<Point>) {
910        if polygon.points.is_empty() {
911            return;
912        }
913
914        self.reserve(polygon.points.len(), 0);
915
916        self.move_to(polygon.points[0]);
917        for p in &polygon.points[1..] {
918            self.line_to(*p);
919        }
920
921        if polygon.closed {
922            self.close();
923        }
924    }
925}
926
927/// Builds a path.
928///
929/// This trait is separate from `PathBuilder` and `SvgPathBuilder` to allow them to
930/// be used as trait object (which isn't possible when a method returns an associated
931/// type).
932pub trait Build {
933    /// The type of object that is created by this builder.
934    type PathType;
935
936    /// Builds a path object, consuming the builder.
937    fn build(self) -> Self::PathType;
938}
939
940/// A Builder that approximates curves with successions of line segments.
941pub struct Flattened<Builder> {
942    builder: Builder,
943    current_position: Point,
944    tolerance: f32,
945    prev_attributes: Vec<f32>,
946    attribute_buffer: Vec<f32>,
947}
948
949impl<Builder: Build> Build for Flattened<Builder> {
950    type PathType = Builder::PathType;
951
952    fn build(self) -> Builder::PathType {
953        self.builder.build()
954    }
955}
956
957impl<Builder: PathBuilder> PathBuilder for Flattened<Builder> {
958    fn num_attributes(&self) -> usize {
959        self.builder.num_attributes()
960    }
961
962    fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId {
963        self.current_position = at;
964        self.builder.begin(at, attributes)
965    }
966
967    fn end(&mut self, close: bool) {
968        self.builder.end(close)
969    }
970
971    fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
972        let id = self.builder.line_to(to, attributes);
973        self.current_position = to;
974        self.prev_attributes.copy_from_slice(attributes);
975        id
976    }
977
978    fn quadratic_bezier_to(
979        &mut self,
980        ctrl: Point,
981        to: Point,
982        attributes: Attributes,
983    ) -> EndpointId {
984        let id = crate::private::flatten_quadratic_bezier(
985            self.tolerance,
986            self.current_position,
987            ctrl,
988            to,
989            attributes,
990            &self.prev_attributes,
991            &mut self.builder,
992            &mut self.attribute_buffer,
993        );
994        self.current_position = to;
995        self.prev_attributes.copy_from_slice(attributes);
996
997        id
998    }
999
1000    fn cubic_bezier_to(
1001        &mut self,
1002        ctrl1: Point,
1003        ctrl2: Point,
1004        to: Point,
1005        attributes: Attributes,
1006    ) -> EndpointId {
1007        let id = crate::private::flatten_cubic_bezier(
1008            self.tolerance,
1009            self.current_position,
1010            ctrl1,
1011            ctrl2,
1012            to,
1013            attributes,
1014            &self.prev_attributes,
1015            &mut self.builder,
1016            &mut self.attribute_buffer,
1017        );
1018        self.current_position = to;
1019        self.prev_attributes.copy_from_slice(attributes);
1020
1021        id
1022    }
1023
1024    fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1025        self.builder.reserve(endpoints + ctrl_points * 4, 0);
1026    }
1027}
1028
1029impl<Builder: PathBuilder> Flattened<Builder> {
1030    pub fn new(builder: Builder, tolerance: f32) -> Flattened<Builder> {
1031        let n = builder.num_attributes();
1032        Flattened {
1033            builder,
1034            current_position: point(0.0, 0.0),
1035            tolerance,
1036            prev_attributes: vec![0.0; n],
1037            attribute_buffer: vec![0.0; n],
1038        }
1039    }
1040
1041    pub fn build(self) -> Builder::PathType
1042    where
1043        Builder: Build,
1044    {
1045        self.builder.build()
1046    }
1047
1048    pub fn set_tolerance(&mut self, tolerance: f32) {
1049        self.tolerance = tolerance
1050    }
1051}
1052
1053/// Builds a path with a transformation applied.
1054pub struct Transformed<Builder, Transform> {
1055    builder: Builder,
1056    transform: Transform,
1057}
1058
1059impl<Builder, Transform> Transformed<Builder, Transform> {
1060    #[inline]
1061    pub fn new(builder: Builder, transform: Transform) -> Self {
1062        Transformed { builder, transform }
1063    }
1064
1065    #[inline]
1066    pub fn set_transform(&mut self, transform: Transform) {
1067        self.transform = transform;
1068    }
1069}
1070
1071impl<Builder: Build, Transform> Build for Transformed<Builder, Transform> {
1072    type PathType = Builder::PathType;
1073
1074    #[inline]
1075    fn build(self) -> Builder::PathType {
1076        self.builder.build()
1077    }
1078}
1079
1080impl<Builder, Transform> PathBuilder for Transformed<Builder, Transform>
1081where
1082    Builder: PathBuilder,
1083    Transform: Transformation<f32>,
1084{
1085    fn num_attributes(&self) -> usize {
1086        self.builder.num_attributes()
1087    }
1088
1089    #[inline]
1090    fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId {
1091        self.builder
1092            .begin(self.transform.transform_point(at), attributes)
1093    }
1094
1095    #[inline]
1096    fn end(&mut self, close: bool) {
1097        self.builder.end(close)
1098    }
1099
1100    #[inline]
1101    fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
1102        self.builder
1103            .line_to(self.transform.transform_point(to), attributes)
1104    }
1105
1106    #[inline]
1107    fn quadratic_bezier_to(
1108        &mut self,
1109        ctrl: Point,
1110        to: Point,
1111        attributes: Attributes,
1112    ) -> EndpointId {
1113        self.builder.quadratic_bezier_to(
1114            self.transform.transform_point(ctrl),
1115            self.transform.transform_point(to),
1116            attributes,
1117        )
1118    }
1119
1120    #[inline]
1121    fn cubic_bezier_to(
1122        &mut self,
1123        ctrl1: Point,
1124        ctrl2: Point,
1125        to: Point,
1126        attributes: Attributes,
1127    ) -> EndpointId {
1128        self.builder.cubic_bezier_to(
1129            self.transform.transform_point(ctrl1),
1130            self.transform.transform_point(ctrl2),
1131            self.transform.transform_point(to),
1132            attributes,
1133        )
1134    }
1135
1136    #[inline]
1137    fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1138        self.builder.reserve(endpoints, ctrl_points);
1139    }
1140}
1141
1142/// Implements an SVG-like building interface on top of a PathBuilder.
1143pub struct WithSvg<Builder: PathBuilder> {
1144    builder: Builder,
1145
1146    first_position: Point,
1147    current_position: Point,
1148    last_ctrl: Point,
1149    last_cmd: Verb,
1150    need_moveto: bool,
1151    is_empty: bool,
1152    attribute_buffer: Vec<f32>,
1153}
1154
1155impl<Builder: PathBuilder> WithSvg<Builder> {
1156    pub fn new(builder: Builder) -> Self {
1157        let attribute_buffer = vec![0.0; builder.num_attributes()];
1158        WithSvg {
1159            builder,
1160            first_position: point(0.0, 0.0),
1161            current_position: point(0.0, 0.0),
1162            last_ctrl: point(0.0, 0.0),
1163            need_moveto: true,
1164            is_empty: true,
1165            last_cmd: Verb::End,
1166            attribute_buffer,
1167        }
1168    }
1169
1170    pub fn build(mut self) -> Builder::PathType
1171    where
1172        Builder: Build,
1173    {
1174        self.end_if_needed();
1175        self.builder.build()
1176    }
1177
1178    pub fn flattened(self, tolerance: f32) -> WithSvg<Flattened<Builder>> {
1179        WithSvg::new(Flattened::new(self.builder, tolerance))
1180    }
1181
1182    pub fn transformed<Transform>(
1183        self,
1184        transform: Transform,
1185    ) -> WithSvg<Transformed<Builder, Transform>>
1186    where
1187        Transform: Transformation<f32>,
1188    {
1189        WithSvg::new(Transformed::new(self.builder, transform))
1190    }
1191
1192    pub fn move_to(&mut self, to: Point) -> EndpointId {
1193        self.end_if_needed();
1194
1195        let id = self.builder.begin(to, &self.attribute_buffer);
1196
1197        self.is_empty = false;
1198        self.need_moveto = false;
1199        self.first_position = to;
1200        self.current_position = to;
1201        self.last_cmd = Verb::Begin;
1202
1203        id
1204    }
1205
1206    pub fn line_to(&mut self, to: Point) -> EndpointId {
1207        if let Some(id) = self.begin_if_needed(&to) {
1208            return id;
1209        }
1210
1211        self.current_position = to;
1212        self.last_cmd = Verb::LineTo;
1213
1214        self.builder.line_to(to, &self.attribute_buffer)
1215    }
1216
1217    pub fn close(&mut self) {
1218        if self.need_moveto {
1219            return;
1220        }
1221
1222        // Relative path ops tend to accumulate small floating point error,
1223        // which results in the last segment ending almost but not quite at the
1224        // start of the sub-path, causing a new edge to be inserted which often
1225        // intersects with the first or last edge. This can affect algorithms that
1226        // Don't handle self-intersecting paths.
1227        // Deal with this by snapping the last point if it is very close to the
1228        // start of the sub path.
1229        //
1230        // TODO
1231        // if let Some(p) = self.builder.points.last_mut() {
1232        //     let d = (*p - self.first_position).abs();
1233        //     if d.x + d.y < 0.0001 {
1234        //         *p = self.first_position;
1235        //     }
1236        // }
1237
1238        self.current_position = self.first_position;
1239        self.need_moveto = true;
1240        self.last_cmd = Verb::Close;
1241
1242        self.builder.close();
1243    }
1244
1245    pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId {
1246        if let Some(id) = self.begin_if_needed(&to) {
1247            return id;
1248        }
1249
1250        self.current_position = to;
1251        self.last_cmd = Verb::QuadraticTo;
1252        self.last_ctrl = ctrl;
1253
1254        self.builder
1255            .quadratic_bezier_to(ctrl, to, &self.attribute_buffer)
1256    }
1257
1258    pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId {
1259        if let Some(id) = self.begin_if_needed(&to) {
1260            return id;
1261        }
1262
1263        self.current_position = to;
1264        self.last_cmd = Verb::CubicTo;
1265        self.last_ctrl = ctrl2;
1266
1267        self.builder
1268            .cubic_bezier_to(ctrl1, ctrl2, to, &self.attribute_buffer)
1269    }
1270
1271    pub fn arc(&mut self, center: Point, radii: Vector, sweep_angle: Angle, x_rotation: Angle) {
1272        nan_check(center);
1273        nan_check(radii.to_point());
1274        debug_assert!(!sweep_angle.get().is_nan());
1275        debug_assert!(!x_rotation.get().is_nan());
1276
1277        self.last_ctrl = self.current_position;
1278
1279        // If the center is equal to the current position, the start and end angles aren't
1280        // defined, so we just skip the arc to avoid generating NaNs that will cause issues
1281        // later.
1282        use lyon_geom::euclid::approxeq::ApproxEq;
1283        if self.current_position.approx_eq(&center) {
1284            return;
1285        }
1286
1287        let start_angle = (self.current_position - center).angle_from_x_axis() - x_rotation;
1288
1289        let arc = Arc {
1290            center,
1291            radii,
1292            start_angle,
1293            sweep_angle,
1294            x_rotation,
1295        };
1296
1297        // If the current position is not on the arc, move or line to the beginning of the
1298        // arc.
1299        let arc_start = arc.from();
1300        if self.need_moveto {
1301            self.move_to(arc_start);
1302        } else if (arc_start - self.current_position).square_length() < 0.01 {
1303            self.builder.line_to(arc_start, &self.attribute_buffer);
1304        }
1305
1306        arc.cast::<f64>().for_each_quadratic_bezier(&mut |curve| {
1307            let curve = curve.cast::<f32>();
1308            self.builder
1309                .quadratic_bezier_to(curve.ctrl, curve.to, &self.attribute_buffer);
1310            self.current_position = curve.to;
1311        });
1312    }
1313
1314    /// Ensures the current sub-path has a moveto command.
1315    ///
1316    /// Returns an ID if the command should be skipped and the ID returned instead.
1317    #[inline(always)]
1318    fn begin_if_needed(&mut self, default: &Point) -> Option<EndpointId> {
1319        if self.need_moveto {
1320            return self.insert_move_to(default);
1321        }
1322
1323        None
1324    }
1325
1326    #[inline(never)]
1327    fn insert_move_to(&mut self, default: &Point) -> Option<EndpointId> {
1328        if self.is_empty {
1329            return Some(self.move_to(*default));
1330        }
1331
1332        self.move_to(self.first_position);
1333
1334        None
1335    }
1336
1337    fn end_if_needed(&mut self) {
1338        if (self.last_cmd as u8) <= (Verb::Begin as u8) {
1339            self.builder.end(false);
1340        }
1341    }
1342
1343    pub fn current_position(&self) -> Point {
1344        self.current_position
1345    }
1346
1347    pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1348        self.builder.reserve(endpoints, ctrl_points);
1349    }
1350
1351    fn get_smooth_cubic_ctrl(&self) -> Point {
1352        match self.last_cmd {
1353            Verb::CubicTo => self.current_position + (self.current_position - self.last_ctrl),
1354            _ => self.current_position,
1355        }
1356    }
1357
1358    fn get_smooth_quadratic_ctrl(&self) -> Point {
1359        match self.last_cmd {
1360            Verb::QuadraticTo => self.current_position + (self.current_position - self.last_ctrl),
1361            _ => self.current_position,
1362        }
1363    }
1364
1365    fn relative_to_absolute(&self, v: Vector) -> Point {
1366        self.current_position + v
1367    }
1368}
1369
1370impl<Builder, Transform> WithSvg<Transformed<Builder, Transform>>
1371where
1372    Builder: PathBuilder,
1373    Transform: Transformation<f32>,
1374{
1375    #[inline]
1376    pub fn set_transform(&mut self, transform: Transform) {
1377        self.builder.set_transform(transform);
1378    }
1379}
1380
1381impl<Builder: PathBuilder + Build> Build for WithSvg<Builder> {
1382    type PathType = Builder::PathType;
1383
1384    fn build(mut self) -> Builder::PathType {
1385        self.end_if_needed();
1386        self.builder.build()
1387    }
1388}
1389
1390impl<Builder: PathBuilder> SvgPathBuilder for WithSvg<Builder> {
1391    fn move_to(&mut self, to: Point) {
1392        self.move_to(to);
1393    }
1394
1395    fn close(&mut self) {
1396        self.close();
1397    }
1398
1399    fn line_to(&mut self, to: Point) {
1400        self.line_to(to);
1401    }
1402
1403    fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
1404        self.quadratic_bezier_to(ctrl, to);
1405    }
1406
1407    fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
1408        self.cubic_bezier_to(ctrl1, ctrl2, to);
1409    }
1410
1411    fn relative_move_to(&mut self, to: Vector) {
1412        let to = self.relative_to_absolute(to);
1413        self.move_to(to);
1414    }
1415
1416    fn relative_line_to(&mut self, to: Vector) {
1417        let to = self.relative_to_absolute(to);
1418        self.line_to(to);
1419    }
1420
1421    fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector) {
1422        let ctrl = self.relative_to_absolute(ctrl);
1423        let to = self.relative_to_absolute(to);
1424        self.quadratic_bezier_to(ctrl, to);
1425    }
1426
1427    fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector) {
1428        let to = self.relative_to_absolute(to);
1429        let ctrl1 = self.relative_to_absolute(ctrl1);
1430        let ctrl2 = self.relative_to_absolute(ctrl2);
1431        self.cubic_bezier_to(ctrl1, ctrl2, to);
1432    }
1433
1434    fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point) {
1435        let ctrl1 = self.get_smooth_cubic_ctrl();
1436        self.cubic_bezier_to(ctrl1, ctrl2, to);
1437    }
1438
1439    fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector) {
1440        let ctrl1 = self.get_smooth_cubic_ctrl();
1441        let ctrl2 = self.relative_to_absolute(ctrl2);
1442        let to = self.relative_to_absolute(to);
1443        self.cubic_bezier_to(ctrl1, ctrl2, to);
1444    }
1445
1446    fn smooth_quadratic_bezier_to(&mut self, to: Point) {
1447        let ctrl = self.get_smooth_quadratic_ctrl();
1448        self.quadratic_bezier_to(ctrl, to);
1449    }
1450
1451    fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector) {
1452        let ctrl = self.get_smooth_quadratic_ctrl();
1453        let to = self.relative_to_absolute(to);
1454        self.quadratic_bezier_to(ctrl, to);
1455    }
1456
1457    fn horizontal_line_to(&mut self, x: f32) {
1458        let y = self.current_position.y;
1459        self.line_to(point(x, y));
1460    }
1461
1462    fn relative_horizontal_line_to(&mut self, dx: f32) {
1463        let p = self.current_position;
1464        self.line_to(point(p.x + dx, p.y));
1465    }
1466
1467    fn vertical_line_to(&mut self, y: f32) {
1468        let x = self.current_position.x;
1469        self.line_to(point(x, y));
1470    }
1471
1472    fn relative_vertical_line_to(&mut self, dy: f32) {
1473        let p = self.current_position;
1474        self.line_to(point(p.x, p.y + dy));
1475    }
1476
1477    fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point) {
1478        let svg_arc = SvgArc {
1479            from: self.current_position,
1480            to,
1481            radii,
1482            x_rotation,
1483            flags: ArcFlags {
1484                large_arc: flags.large_arc,
1485                sweep: flags.sweep,
1486            },
1487        };
1488
1489        if svg_arc.is_straight_line() {
1490            self.line_to(to);
1491        } else {
1492            let arc = svg_arc.to_arc();
1493            self.arc(arc.center, arc.radii, arc.sweep_angle, arc.x_rotation);
1494        }
1495    }
1496
1497    fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector) {
1498        let to = self.relative_to_absolute(to);
1499        self.arc_to(radii, x_rotation, flags, to);
1500    }
1501
1502    fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1503        self.builder.reserve(endpoints, ctrl_points);
1504    }
1505}
1506
1507/// Tessellate the stroke for an axis-aligned rounded rectangle.
1508fn add_circle<Builder: PathBuilder>(
1509    builder: &mut Builder,
1510    center: Point,
1511    radius: f32,
1512    winding: Winding,
1513    attributes: Attributes,
1514) {
1515    let radius = radius.abs();
1516    let dir = match winding {
1517        Winding::Positive => 1.0,
1518        Winding::Negative => -1.0,
1519    };
1520
1521    // https://spencermortensen.com/articles/bezier-circle/
1522    const CONSTANT_FACTOR: f32 = 0.55191505;
1523    let d = radius * CONSTANT_FACTOR;
1524
1525    builder.begin(center + vector(-radius, 0.0), attributes);
1526
1527    let ctrl_0 = center + vector(-radius, -d * dir);
1528    let ctrl_1 = center + vector(-d, -radius * dir);
1529    let mid = center + vector(0.0, -radius * dir);
1530    builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1531
1532    let ctrl_0 = center + vector(d, -radius * dir);
1533    let ctrl_1 = center + vector(radius, -d * dir);
1534    let mid = center + vector(radius, 0.0);
1535    builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1536
1537    let ctrl_0 = center + vector(radius, d * dir);
1538    let ctrl_1 = center + vector(d, radius * dir);
1539    let mid = center + vector(0.0, radius * dir);
1540    builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1541
1542    let ctrl_0 = center + vector(-d, radius * dir);
1543    let ctrl_1 = center + vector(-radius, d * dir);
1544    let mid = center + vector(-radius, 0.0);
1545    builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1546
1547    builder.close();
1548}
1549
1550/// Tessellate the stroke for an axis-aligned rounded rectangle.
1551fn add_rounded_rectangle<Builder: PathBuilder>(
1552    builder: &mut Builder,
1553    rect: &Box2D,
1554    radii: &BorderRadii,
1555    winding: Winding,
1556    attributes: Attributes,
1557) {
1558    let w = rect.width();
1559    let h = rect.height();
1560    let x_min = rect.min.x;
1561    let y_min = rect.min.y;
1562    let x_max = rect.max.x;
1563    let y_max = rect.max.y;
1564    let min_wh = w.min(h);
1565    let mut tl = radii.top_left.abs().min(min_wh);
1566    let mut tr = radii.top_right.abs().min(min_wh);
1567    let mut bl = radii.bottom_left.abs().min(min_wh);
1568    let mut br = radii.bottom_right.abs().min(min_wh);
1569
1570    // clamp border radii if they don't fit in the rectangle.
1571    if tl + tr > w {
1572        let x = (tl + tr - w) * 0.5;
1573        tl -= x;
1574        tr -= x;
1575    }
1576    if bl + br > w {
1577        let x = (bl + br - w) * 0.5;
1578        bl -= x;
1579        br -= x;
1580    }
1581    if tr + br > h {
1582        let x = (tr + br - h) * 0.5;
1583        tr -= x;
1584        br -= x;
1585    }
1586    if tl + bl > h {
1587        let x = (tl + bl - h) * 0.5;
1588        tl -= x;
1589        bl -= x;
1590    }
1591
1592    // https://spencermortensen.com/articles/bezier-circle/
1593    const CONSTANT_FACTOR: f32 = 0.55191505;
1594
1595    let tl_d = tl * CONSTANT_FACTOR;
1596    let tl_corner = point(x_min, y_min);
1597
1598    let tr_d = tr * CONSTANT_FACTOR;
1599    let tr_corner = point(x_max, y_min);
1600
1601    let br_d = br * CONSTANT_FACTOR;
1602    let br_corner = point(x_max, y_max);
1603
1604    let bl_d = bl * CONSTANT_FACTOR;
1605    let bl_corner = point(x_min, y_max);
1606
1607    let points = [
1608        point(x_min, y_min + tl),           // begin
1609        tl_corner + vector(0.0, tl - tl_d), // control
1610        tl_corner + vector(tl - tl_d, 0.0), // control
1611        tl_corner + vector(tl, 0.0),        // end
1612        point(x_max - tr, y_min),
1613        tr_corner + vector(-tr + tr_d, 0.0),
1614        tr_corner + vector(0.0, tr - tr_d),
1615        tr_corner + vector(0.0, tr),
1616        point(x_max, y_max - br),
1617        br_corner + vector(0.0, -br + br_d),
1618        br_corner + vector(-br + br_d, 0.0),
1619        br_corner + vector(-br, 0.0),
1620        point(x_min + bl, y_max),
1621        bl_corner + vector(bl - bl_d, 0.0),
1622        bl_corner + vector(0.0, -bl + bl_d),
1623        bl_corner + vector(0.0, -bl),
1624    ];
1625
1626    if winding == Winding::Positive {
1627        builder.begin(points[0], attributes);
1628        if tl > 0.0 {
1629            builder.cubic_bezier_to(points[1], points[2], points[3], attributes);
1630        }
1631        builder.line_to(points[4], attributes);
1632        if tr > 0.0 {
1633            builder.cubic_bezier_to(points[5], points[6], points[7], attributes);
1634        }
1635        builder.line_to(points[8], attributes);
1636        if br > 0.0 {
1637            builder.cubic_bezier_to(points[9], points[10], points[11], attributes);
1638        }
1639        builder.line_to(points[12], attributes);
1640        if bl > 0.0 {
1641            builder.cubic_bezier_to(points[13], points[14], points[15], attributes);
1642        }
1643    } else {
1644        builder.begin(points[15], attributes);
1645        if bl > 0.0 {
1646            builder.cubic_bezier_to(points[14], points[13], points[12], attributes);
1647        }
1648        builder.line_to(points[11], attributes);
1649        if br > 0.0 {
1650            builder.cubic_bezier_to(points[10], points[9], points[8], attributes);
1651        }
1652        builder.line_to(points[7], attributes);
1653        if tr > 0.0 {
1654            builder.cubic_bezier_to(points[6], points[5], points[4], attributes);
1655        }
1656        builder.line_to(points[3], attributes);
1657        if tl > 0.0 {
1658            builder.cubic_bezier_to(points[2], points[1], points[0], attributes);
1659        }
1660    }
1661    builder.end(true);
1662}
1663
1664#[inline]
1665fn nan_check(p: Point) {
1666    debug_assert!(p.x.is_finite());
1667    debug_assert!(p.y.is_finite());
1668}
1669
1670#[test]
1671fn svg_builder_line_to_after_close() {
1672    use crate::Path;
1673    use crate::PathEvent;
1674
1675    let mut p = Path::svg_builder();
1676    p.line_to(point(1.0, 0.0));
1677    p.close();
1678    p.line_to(point(2.0, 0.0));
1679
1680    let path = p.build();
1681    let mut it = path.iter();
1682    assert_eq!(
1683        it.next(),
1684        Some(PathEvent::Begin {
1685            at: point(1.0, 0.0)
1686        })
1687    );
1688    assert_eq!(
1689        it.next(),
1690        Some(PathEvent::End {
1691            last: point(1.0, 0.0),
1692            first: point(1.0, 0.0),
1693            close: true
1694        })
1695    );
1696    assert_eq!(
1697        it.next(),
1698        Some(PathEvent::Begin {
1699            at: point(1.0, 0.0)
1700        })
1701    );
1702    assert_eq!(
1703        it.next(),
1704        Some(PathEvent::Line {
1705            from: point(1.0, 0.0),
1706            to: point(2.0, 0.0)
1707        })
1708    );
1709    assert_eq!(
1710        it.next(),
1711        Some(PathEvent::End {
1712            last: point(2.0, 0.0),
1713            first: point(1.0, 0.0),
1714            close: false
1715        })
1716    );
1717    assert_eq!(it.next(), None);
1718}
1719
1720#[test]
1721fn svg_builder_relative_curves() {
1722    use crate::Path;
1723    use crate::PathEvent;
1724
1725    let mut p = Path::svg_builder();
1726    p.move_to(point(0.0, 0.0));
1727    p.relative_quadratic_bezier_to(vector(0., 100.), vector(-100., 100.));
1728    p.relative_line_to(vector(-50., 0.));
1729
1730    let path = p.build();
1731    let mut it = path.iter();
1732    assert_eq!(
1733        it.next(),
1734        Some(PathEvent::Begin {
1735            at: point(0.0, 0.0)
1736        })
1737    );
1738    assert_eq!(
1739        it.next(),
1740        Some(PathEvent::Quadratic {
1741            from: point(0.0, 0.0),
1742            ctrl: point(0.0, 100.0),
1743            to: point(-100., 100.),
1744        })
1745    );
1746    assert_eq!(
1747        it.next(),
1748        Some(PathEvent::Line {
1749            from: point(-100.0, 100.0),
1750            to: point(-150., 100.)
1751        })
1752    );
1753    assert_eq!(
1754        it.next(),
1755        Some(PathEvent::End {
1756            first: point(0.0, 0.0),
1757            last: point(-150., 100.),
1758            close: false,
1759        })
1760    );
1761    assert_eq!(it.next(), None);
1762}
1763
1764#[test]
1765fn svg_builder_arc_to_update_position() {
1766    use crate::Path;
1767
1768    let mut p = Path::svg_builder();
1769    p.move_to(point(0.0, 0.0));
1770    assert_eq!(p.current_position(), point(0.0, 0.0));
1771    p.arc_to(
1772        vector(100., 100.),
1773        Angle::degrees(0.),
1774        ArcFlags::default(),
1775        point(0.0, 100.0),
1776    );
1777    assert_ne!(p.current_position(), point(0.0, 0.0));
1778}
1779
1780#[test]
1781fn issue_650() {
1782    let mut builder = crate::path::Path::builder().with_svg();
1783    builder.arc(
1784        point(0.0, 0.0),
1785        vector(50.0, 50.0),
1786        Angle::radians(PI),
1787        Angle::radians(0.0),
1788    );
1789    builder.build();
1790}
1791
1792#[test]
1793fn straight_line_arc() {
1794    use crate::Path;
1795
1796    let mut p = Path::svg_builder();
1797    p.move_to(point(100.0, 0.0));
1798    // Don't assert on a "false" arc that's a straight line
1799    p.arc_to(
1800        vector(100., 100.),
1801        Angle::degrees(0.),
1802        ArcFlags::default(),
1803        point(100.0, 0.0),
1804    );
1805}
1806
1807#[test]
1808fn top_right_rounded_rect() {
1809    use crate::{math::*, Path};
1810    let mut builder = Path::builder();
1811    builder.add_rounded_rectangle(
1812        &Box2D::new(point(0., 0.), point(100., 100.)),
1813        &BorderRadii {
1814            top_right: 2.,
1815            ..Default::default()
1816        },
1817        Winding::Positive,
1818    );
1819    let path = builder.build();
1820    let tr = path.iter().skip(2).next().unwrap();
1821    assert_eq!(tr.from(), point(98., 0.));
1822    assert_eq!(tr.to(), point(100., 2.));
1823}