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.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}
1824