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: f32 = 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: usize = builder.num_attributes();
1032 Flattened {
1033 builder,
1034 current_position: point(x:0.0, y: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.for_each_quadratic_bezier(&mut |curve| {
1307 self.builder
1308 .quadratic_bezier_to(curve.ctrl, curve.to, &self.attribute_buffer);
1309 self.current_position = curve.to;
1310 });
1311 }
1312
1313 /// Ensures the current sub-path has a moveto command.
1314 ///
1315 /// Returns an ID if the command should be skipped and the ID returned instead.
1316 #[inline(always)]
1317 fn begin_if_needed(&mut self, default: &Point) -> Option<EndpointId> {
1318 if self.need_moveto {
1319 return self.insert_move_to(default);
1320 }
1321
1322 None
1323 }
1324
1325 #[inline(never)]
1326 fn insert_move_to(&mut self, default: &Point) -> Option<EndpointId> {
1327 if self.is_empty {
1328 return Some(self.move_to(*default));
1329 }
1330
1331 self.move_to(self.first_position);
1332
1333 None
1334 }
1335
1336 fn end_if_needed(&mut self) {
1337 if (self.last_cmd as u8) <= (Verb::Begin as u8) {
1338 self.builder.end(false);
1339 }
1340 }
1341
1342 pub fn current_position(&self) -> Point {
1343 self.current_position
1344 }
1345
1346 pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1347 self.builder.reserve(endpoints, ctrl_points);
1348 }
1349
1350 fn get_smooth_cubic_ctrl(&self) -> Point {
1351 match self.last_cmd {
1352 Verb::CubicTo => self.current_position + (self.current_position - self.last_ctrl),
1353 _ => self.current_position,
1354 }
1355 }
1356
1357 fn get_smooth_quadratic_ctrl(&self) -> Point {
1358 match self.last_cmd {
1359 Verb::QuadraticTo => self.current_position + (self.current_position - self.last_ctrl),
1360 _ => self.current_position,
1361 }
1362 }
1363
1364 fn relative_to_absolute(&self, v: Vector) -> Point {
1365 self.current_position + v
1366 }
1367}
1368
1369impl<Builder, Transform> WithSvg<Transformed<Builder, Transform>>
1370where
1371 Builder: PathBuilder,
1372 Transform: Transformation<f32>,
1373{
1374 #[inline]
1375 pub fn set_transform(&mut self, transform: Transform) {
1376 self.builder.set_transform(transform);
1377 }
1378}
1379
1380impl<Builder: PathBuilder + Build> Build for WithSvg<Builder> {
1381 type PathType = Builder::PathType;
1382
1383 fn build(mut self) -> Builder::PathType {
1384 self.end_if_needed();
1385 self.builder.build()
1386 }
1387}
1388
1389impl<Builder: PathBuilder> SvgPathBuilder for WithSvg<Builder> {
1390 fn move_to(&mut self, to: Point) {
1391 self.move_to(to);
1392 }
1393
1394 fn close(&mut self) {
1395 self.close();
1396 }
1397
1398 fn line_to(&mut self, to: Point) {
1399 self.line_to(to);
1400 }
1401
1402 fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
1403 self.quadratic_bezier_to(ctrl, to);
1404 }
1405
1406 fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
1407 self.cubic_bezier_to(ctrl1, ctrl2, to);
1408 }
1409
1410 fn relative_move_to(&mut self, to: Vector) {
1411 let to = self.relative_to_absolute(to);
1412 self.move_to(to);
1413 }
1414
1415 fn relative_line_to(&mut self, to: Vector) {
1416 let to = self.relative_to_absolute(to);
1417 self.line_to(to);
1418 }
1419
1420 fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector) {
1421 let ctrl = self.relative_to_absolute(ctrl);
1422 let to = self.relative_to_absolute(to);
1423 self.quadratic_bezier_to(ctrl, to);
1424 }
1425
1426 fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector) {
1427 let to = self.relative_to_absolute(to);
1428 let ctrl1 = self.relative_to_absolute(ctrl1);
1429 let ctrl2 = self.relative_to_absolute(ctrl2);
1430 self.cubic_bezier_to(ctrl1, ctrl2, to);
1431 }
1432
1433 fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point) {
1434 let ctrl1 = self.get_smooth_cubic_ctrl();
1435 self.cubic_bezier_to(ctrl1, ctrl2, to);
1436 }
1437
1438 fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector) {
1439 let ctrl1 = self.get_smooth_cubic_ctrl();
1440 let ctrl2 = self.relative_to_absolute(ctrl2);
1441 let to = self.relative_to_absolute(to);
1442 self.cubic_bezier_to(ctrl1, ctrl2, to);
1443 }
1444
1445 fn smooth_quadratic_bezier_to(&mut self, to: Point) {
1446 let ctrl = self.get_smooth_quadratic_ctrl();
1447 self.quadratic_bezier_to(ctrl, to);
1448 }
1449
1450 fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector) {
1451 let ctrl = self.get_smooth_quadratic_ctrl();
1452 let to = self.relative_to_absolute(to);
1453 self.quadratic_bezier_to(ctrl, to);
1454 }
1455
1456 fn horizontal_line_to(&mut self, x: f32) {
1457 let y = self.current_position.y;
1458 self.line_to(point(x, y));
1459 }
1460
1461 fn relative_horizontal_line_to(&mut self, dx: f32) {
1462 let p = self.current_position;
1463 self.line_to(point(p.x + dx, p.y));
1464 }
1465
1466 fn vertical_line_to(&mut self, y: f32) {
1467 let x = self.current_position.x;
1468 self.line_to(point(x, y));
1469 }
1470
1471 fn relative_vertical_line_to(&mut self, dy: f32) {
1472 let p = self.current_position;
1473 self.line_to(point(p.x, p.y + dy));
1474 }
1475
1476 fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point) {
1477 let svg_arc = SvgArc {
1478 from: self.current_position,
1479 to,
1480 radii,
1481 x_rotation,
1482 flags: ArcFlags {
1483 large_arc: flags.large_arc,
1484 sweep: flags.sweep,
1485 },
1486 };
1487
1488 if svg_arc.is_straight_line() {
1489 self.line_to(to);
1490 } else {
1491 let arc = svg_arc.to_arc();
1492 self.arc(arc.center, arc.radii, arc.sweep_angle, arc.x_rotation);
1493 }
1494 }
1495
1496 fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector) {
1497 let to = self.relative_to_absolute(to);
1498 self.arc_to(radii, x_rotation, flags, to);
1499 }
1500
1501 fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1502 self.builder.reserve(endpoints, ctrl_points);
1503 }
1504}
1505
1506/// Tessellate the stroke for an axis-aligned rounded rectangle.
1507fn add_circle<Builder: PathBuilder>(
1508 builder: &mut Builder,
1509 center: Point,
1510 radius: f32,
1511 winding: Winding,
1512 attributes: Attributes,
1513) {
1514 let radius = radius.abs();
1515 let dir = match winding {
1516 Winding::Positive => 1.0,
1517 Winding::Negative => -1.0,
1518 };
1519
1520 // https://spencermortensen.com/articles/bezier-circle/
1521 const CONSTANT_FACTOR: f32 = 0.55191505;
1522 let d = radius * CONSTANT_FACTOR;
1523
1524 builder.begin(center + vector(-radius, 0.0), attributes);
1525
1526 let ctrl_0 = center + vector(-radius, -d * dir);
1527 let ctrl_1 = center + vector(-d, -radius * dir);
1528 let mid = center + vector(0.0, -radius * dir);
1529 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1530
1531 let ctrl_0 = center + vector(d, -radius * dir);
1532 let ctrl_1 = center + vector(radius, -d * dir);
1533 let mid = center + vector(radius, 0.0);
1534 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1535
1536 let ctrl_0 = center + vector(radius, d * dir);
1537 let ctrl_1 = center + vector(d, radius * dir);
1538 let mid = center + vector(0.0, radius * dir);
1539 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1540
1541 let ctrl_0 = center + vector(-d, radius * dir);
1542 let ctrl_1 = center + vector(-radius, d * dir);
1543 let mid = center + vector(-radius, 0.0);
1544 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1545
1546 builder.close();
1547}
1548
1549/// Tessellate the stroke for an axis-aligned rounded rectangle.
1550fn add_rounded_rectangle<Builder: PathBuilder>(
1551 builder: &mut Builder,
1552 rect: &Box2D,
1553 radii: &BorderRadii,
1554 winding: Winding,
1555 attributes: Attributes,
1556) {
1557 let w = rect.width();
1558 let h = rect.height();
1559 let x_min = rect.min.x;
1560 let y_min = rect.min.y;
1561 let x_max = rect.max.x;
1562 let y_max = rect.max.y;
1563 let min_wh = w.min(h);
1564 let mut tl = radii.top_left.abs().min(min_wh);
1565 let mut tr = radii.top_right.abs().min(min_wh);
1566 let mut bl = radii.bottom_left.abs().min(min_wh);
1567 let mut br = radii.bottom_right.abs().min(min_wh);
1568
1569 // clamp border radii if they don't fit in the rectangle.
1570 if tl + tr > w {
1571 let x = (tl + tr - w) * 0.5;
1572 tl -= x;
1573 tr -= x;
1574 }
1575 if bl + br > w {
1576 let x = (bl + br - w) * 0.5;
1577 bl -= x;
1578 br -= x;
1579 }
1580 if tr + br > h {
1581 let x = (tr + br - h) * 0.5;
1582 tr -= x;
1583 br -= x;
1584 }
1585 if tl + bl > h {
1586 let x = (tl + bl - h) * 0.5;
1587 tl -= x;
1588 bl -= x;
1589 }
1590
1591 // https://spencermortensen.com/articles/bezier-circle/
1592 const CONSTANT_FACTOR: f32 = 0.55191505;
1593
1594 let tl_d = tl * CONSTANT_FACTOR;
1595 let tl_corner = point(x_min, y_min);
1596
1597 let tr_d = tr * CONSTANT_FACTOR;
1598 let tr_corner = point(x_max, y_min);
1599
1600 let br_d = br * CONSTANT_FACTOR;
1601 let br_corner = point(x_max, y_max);
1602
1603 let bl_d = bl * CONSTANT_FACTOR;
1604 let bl_corner = point(x_min, y_max);
1605
1606 let points = [
1607 point(x_min, y_min + tl), // begin
1608 tl_corner + vector(0.0, tl - tl_d), // control
1609 tl_corner + vector(tl - tl_d, 0.0), // control
1610 tl_corner + vector(tl, 0.0), // end
1611 point(x_max - tr, y_min),
1612 tr_corner + vector(-tr + tr_d, 0.0),
1613 tr_corner + vector(0.0, tr - tr_d),
1614 tr_corner + vector(0.0, tr),
1615 point(x_max, y_max - br),
1616 br_corner + vector(0.0, -br + br_d),
1617 br_corner + vector(-br + br_d, 0.0),
1618 br_corner + vector(-br, 0.0),
1619 point(x_min + bl, y_max),
1620 bl_corner + vector(bl - bl_d, 0.0),
1621 bl_corner + vector(0.0, -bl + bl_d),
1622 bl_corner + vector(0.0, -bl),
1623 ];
1624
1625 if winding == Winding::Positive {
1626 builder.begin(points[0], attributes);
1627 if tl > 0.0 {
1628 builder.cubic_bezier_to(points[1], points[2], points[3], attributes);
1629 }
1630 builder.line_to(points[4], attributes);
1631 if tl > 0.0 {
1632 builder.cubic_bezier_to(points[5], points[6], points[7], attributes);
1633 }
1634 builder.line_to(points[8], attributes);
1635 if br > 0.0 {
1636 builder.cubic_bezier_to(points[9], points[10], points[11], attributes);
1637 }
1638 builder.line_to(points[12], attributes);
1639 if bl > 0.0 {
1640 builder.cubic_bezier_to(points[13], points[14], points[15], attributes);
1641 }
1642 } else {
1643 builder.begin(points[15], attributes);
1644 if bl > 0.0 {
1645 builder.cubic_bezier_to(points[14], points[13], points[12], attributes);
1646 }
1647 builder.line_to(points[11], attributes);
1648 if br > 0.0 {
1649 builder.cubic_bezier_to(points[10], points[9], points[8], attributes);
1650 }
1651 builder.line_to(points[7], attributes);
1652 if tl > 0.0 {
1653 builder.cubic_bezier_to(points[6], points[5], points[4], attributes);
1654 }
1655 builder.line_to(points[3], attributes);
1656 if tl > 0.0 {
1657 builder.cubic_bezier_to(points[2], points[1], points[0], attributes);
1658 }
1659 }
1660 builder.end(true);
1661}
1662
1663#[inline]
1664fn nan_check(p: Point) {
1665 debug_assert!(p.x.is_finite());
1666 debug_assert!(p.y.is_finite());
1667}
1668
1669#[test]
1670fn svg_builder_line_to_after_close() {
1671 use crate::Path;
1672 use crate::PathEvent;
1673
1674 let mut p = Path::svg_builder();
1675 p.line_to(point(1.0, 0.0));
1676 p.close();
1677 p.line_to(point(2.0, 0.0));
1678
1679 let path = p.build();
1680 let mut it = path.iter();
1681 assert_eq!(
1682 it.next(),
1683 Some(PathEvent::Begin {
1684 at: point(1.0, 0.0)
1685 })
1686 );
1687 assert_eq!(
1688 it.next(),
1689 Some(PathEvent::End {
1690 last: point(1.0, 0.0),
1691 first: point(1.0, 0.0),
1692 close: true
1693 })
1694 );
1695 assert_eq!(
1696 it.next(),
1697 Some(PathEvent::Begin {
1698 at: point(1.0, 0.0)
1699 })
1700 );
1701 assert_eq!(
1702 it.next(),
1703 Some(PathEvent::Line {
1704 from: point(1.0, 0.0),
1705 to: point(2.0, 0.0)
1706 })
1707 );
1708 assert_eq!(
1709 it.next(),
1710 Some(PathEvent::End {
1711 last: point(2.0, 0.0),
1712 first: point(1.0, 0.0),
1713 close: false
1714 })
1715 );
1716 assert_eq!(it.next(), None);
1717}
1718
1719#[test]
1720fn svg_builder_relative_curves() {
1721 use crate::Path;
1722 use crate::PathEvent;
1723
1724 let mut p = Path::svg_builder();
1725 p.move_to(point(0.0, 0.0));
1726 p.relative_quadratic_bezier_to(vector(0., 100.), vector(-100., 100.));
1727 p.relative_line_to(vector(-50., 0.));
1728
1729 let path = p.build();
1730 let mut it = path.iter();
1731 assert_eq!(
1732 it.next(),
1733 Some(PathEvent::Begin {
1734 at: point(0.0, 0.0)
1735 })
1736 );
1737 assert_eq!(
1738 it.next(),
1739 Some(PathEvent::Quadratic {
1740 from: point(0.0, 0.0),
1741 ctrl: point(0.0, 100.0),
1742 to: point(-100., 100.),
1743 })
1744 );
1745 assert_eq!(
1746 it.next(),
1747 Some(PathEvent::Line {
1748 from: point(-100.0, 100.0),
1749 to: point(-150., 100.)
1750 })
1751 );
1752 assert_eq!(
1753 it.next(),
1754 Some(PathEvent::End {
1755 first: point(0.0, 0.0),
1756 last: point(-150., 100.),
1757 close: false,
1758 })
1759 );
1760 assert_eq!(it.next(), None);
1761}
1762
1763#[test]
1764fn svg_builder_arc_to_update_position() {
1765 use crate::Path;
1766
1767 let mut p: WithSvg = Path::svg_builder();
1768 p.move_to(point(x:0.0, y:0.0));
1769 assert_eq!(p.current_position(), point(0.0, 0.0));
1770 p.arc_to(
1771 radii:vector(100., 100.),
1772 x_rotation:Angle::degrees(0.),
1773 flags:ArcFlags::default(),
1774 to:point(x:0.0, y:100.0),
1775 );
1776 assert_ne!(p.current_position(), point(0.0, 0.0));
1777}
1778
1779#[test]
1780fn issue_650() {
1781 let mut builder: WithSvg = crate::path::Path::builder().with_svg();
1782 builder.arc(
1783 center:point(0.0, 0.0),
1784 radii:vector(50.0, 50.0),
1785 sweep_angle:Angle::radians(PI),
1786 x_rotation:Angle::radians(0.0),
1787 );
1788 builder.build();
1789}
1790
1791#[test]
1792fn straight_line_arc() {
1793 use crate::Path;
1794
1795 let mut p: WithSvg = Path::svg_builder();
1796 p.move_to(point(x:100.0, y:0.0));
1797 // Don't assert on a "false" arc that's a straight line
1798 p.arc_to(
1799 radii:vector(100., 100.),
1800 x_rotation:Angle::degrees(0.),
1801 flags:ArcFlags::default(),
1802 to:point(x:100.0, y:0.0),
1803 );
1804}
1805