lyon_path/
lib.rs

1#![doc(html_logo_url = "https://nical.github.io/lyon-doc/lyon-logo.svg")]
2#![deny(bare_trait_objects)]
3#![deny(unconditional_recursion)]
4#![allow(clippy::match_like_matches_macro)]
5#![no_std]
6
7//! Data structures and traits to work with paths (vector graphics).
8//!
9//! To build and consume paths, see the [builder](builder/index.html) and
10//! [iterator](iterator/index.html) modules.
11//!
12//! This crate is reexported in [lyon](https://docs.rs/lyon/).
13//!
14//! # Examples
15//!
16//! ```
17//! # extern crate lyon_path;
18//! # fn main() {
19//! use lyon_path::Path;
20//! use lyon_path::math::{point};
21//! use lyon_path::builder::*;
22//!
23//! // Create a builder object to build the path.
24//! let mut builder = Path::builder();
25//!
26//! // Build a simple path.
27//! let mut builder = Path::builder();
28//! builder.begin(point(0.0, 0.0));
29//! builder.line_to(point(1.0, 2.0));
30//! builder.line_to(point(2.0, 0.0));
31//! builder.line_to(point(1.0, 1.0));
32//! builder.close();
33//!
34//! // Generate the actual path object.
35//! let path = builder.build();
36//!
37//! for event in &path {
38//!     println!("{:?}", event);
39//! }
40//! # }
41//! ```
42//!
43
44extern crate alloc;
45
46#[cfg(any(test, feature = "std"))]
47extern crate std;
48
49pub use lyon_geom as geom;
50
51#[cfg(feature = "serialization")]
52#[macro_use]
53pub extern crate serde;
54
55pub mod builder;
56pub mod commands;
57mod events;
58pub mod iterator;
59// TODO: remove "pub" on mod path to avoid redundant  "use lyon::path::path::Path" in user code
60//       breaking change would require 1.1 bump?
61pub mod path;
62pub use path::*;
63pub mod path_buffer;
64pub mod polygon;
65
66#[doc(hidden)]
67pub mod private;
68
69#[doc(inline)]
70pub use crate::commands::{PathCommands, PathCommandsSlice};
71pub use crate::events::*;
72pub use crate::geom::ArcFlags;
73#[doc(inline)]
74pub use crate::path::{Path, PathSlice};
75#[doc(inline)]
76pub use crate::path_buffer::{PathBuffer, PathBufferSlice};
77#[doc(inline)]
78pub use crate::polygon::{IdPolygon, Polygon};
79
80use core::fmt;
81use math::Point;
82
83pub mod traits {
84    //! `lyon_path` traits reexported here for convenience.
85
86    pub use crate::builder::Build;
87    pub use crate::builder::PathBuilder;
88    pub use crate::builder::SvgPathBuilder;
89    pub use crate::iterator::PathIterator;
90}
91
92pub mod math {
93    //! f32 version of the lyon_geom types used everywhere. Most other lyon crates
94    //! reexport them.
95
96    use crate::geom::euclid;
97
98    /// Alias for ```euclid::default::Point2D<f32>```.
99    pub type Point = euclid::default::Point2D<f32>;
100
101    /// Alias for ```euclid::default::Point2D<f32>```.
102    pub type Vector = euclid::default::Vector2D<f32>;
103
104    /// Alias for ```euclid::default::Size2D<f32>```.
105    pub type Size = euclid::default::Size2D<f32>;
106
107    /// Alias for ```euclid::default::Box2D<f32>```
108    pub type Box2D = euclid::default::Box2D<f32>;
109
110    /// Alias for ```euclid::default::Transform2D<f32>```
111    pub type Transform = euclid::default::Transform2D<f32>;
112
113    /// Alias for ```euclid::default::Rotation2D<f32>```
114    pub type Rotation = euclid::default::Rotation2D<f32>;
115
116    /// Alias for ```euclid::default::Translation2D<f32>```
117    pub type Translation = euclid::Translation2D<f32, euclid::UnknownUnit, euclid::UnknownUnit>;
118
119    /// Alias for ```euclid::default::Scale<f32>```
120    pub type Scale = euclid::default::Scale<f32>;
121
122    /// An angle in radians (f32).
123    pub type Angle = euclid::Angle<f32>;
124
125    /// Shorthand for `Vector::new(x, y)`.
126    #[inline]
127    pub fn vector(x: f32, y: f32) -> Vector {
128        Vector::new(x, y)
129    }
130
131    /// Shorthand for `Point::new(x, y)`.
132    #[inline]
133    pub fn point(x: f32, y: f32) -> Point {
134        Point::new(x, y)
135    }
136
137    /// Shorthand for `Size::new(x, y)`.
138    #[inline]
139    pub fn size(w: f32, h: f32) -> Size {
140        Size::new(w, h)
141    }
142}
143
144/// Line cap as defined by the SVG specification.
145///
146/// See: <https://svgwg.org/specs/strokes/#StrokeLinecapProperty>
147///
148/// <svg viewBox="0 0 400 399.99998" height="400" width="400">
149///   <g transform="translate(0,-652.36229)">
150///     <path style="opacity:1;fill:#80b3ff;stroke:#000000;stroke-width:1;stroke-linejoin:round;" d="m 240,983 a 30,30 0 0 1 -25,-15 30,30 0 0 1 0,-30.00001 30,30 0 0 1 25.98076,-15 l 0,30 z"/>
151///     <path style="fill:#80b3ff;stroke:#000000;stroke-width:1px;stroke-linecap:butt;" d="m 390,782.6 -150,0 0,-60 150,0.5"/>
152///     <circle style="opacity:1;fill:#ff7f2a;stroke:#000000;stroke-width:1;stroke-linejoin:round;" r="10" cy="752.89227" cx="240.86813"/>
153///     <path style="fill:none;stroke:#000000;stroke-width:1px;stroke-linejoin:round;" d="m 240,722.6 150,60"/>
154///     <path style="fill:#80b3ff;stroke:#000000;stroke-width:1px;stroke-linecap:butt;" d="m 390,882 -180,0 0,-60 180,0.4"/>
155///     <circle style="opacity:1;fill:#ff7f2a;stroke:#000000;stroke-width:1;stroke-linejoin:round;" cx="239.86813" cy="852.20868" r="10" />
156///     <path style="fill:none;stroke:#000000;stroke-width:1px;stroke-linejoin:round;" d="m 210.1,822.3 180,60"/>
157///     <path style="fill:#80b3ff;stroke:#000000;stroke-width:1px;stroke-linecap:butt;" d="m 390,983 -150,0 0,-60 150,0.4"/>
158///     <circle style="opacity:1;fill:#ff7f2a;stroke:#000000;stroke-width:1;stroke-linejoin:round;" cx="239.86813" cy="953.39734" r="10" />
159///     <path style="fill:none;stroke:#000000;stroke-width:1px;stroke-linejoin:round;" d="m 390,983 -150,-60 L 210,953 l 30,30 -21.5,-9.5 L 210,953 218.3,932.5 240,923.4"/>
160///     <text y="757.61273" x="183.65314" style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:Sans;text-align:end;text-anchor:end;fill:#000000;stroke:none;">
161///        <tspan y="757.61273" x="183.65314">LineCap::Butt</tspan>
162///        <tspan y="857.61273" x="183.65314">LineCap::Square</tspan>
163///        <tspan y="957.61273" x="183.65314">LineCap::Round</tspan>
164///      </text>
165///   </g>
166/// </svg>
167#[derive(Copy, Clone, Debug, PartialEq)]
168#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
169pub enum LineCap {
170    /// The stroke for each sub-path does not extend beyond its two endpoints.
171    /// A zero length sub-path will therefore not have any stroke.
172    Butt,
173    /// At the end of each sub-path, the shape representing the stroke will be
174    /// extended by a rectangle with the same width as the stroke width and
175    /// whose length is half of the stroke width. If a sub-path has zero length,
176    /// then the resulting effect is that the stroke for that sub-path consists
177    /// solely of a square with side length equal to the stroke width, centered
178    /// at the sub-path's point.
179    Square,
180    /// At each end of each sub-path, the shape representing the stroke will be extended
181    /// by a half circle with a radius equal to the stroke width.
182    /// If a sub-path has zero length, then the resulting effect is that the stroke for
183    /// that sub-path consists solely of a full circle centered at the sub-path's point.
184    Round,
185}
186
187/// Line join as defined by the SVG specification.
188///
189/// See: <https://svgwg.org/specs/strokes/#StrokeLinejoinProperty>
190#[derive(Copy, Clone, Debug, PartialEq)]
191#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
192pub enum LineJoin {
193    /// A sharp corner is to be used to join path segments.
194    Miter,
195    /// Same as a miter join, but if the miter limit is exceeded,
196    /// the miter is clipped at a miter length equal to the miter limit value
197    /// multiplied by the stroke width.
198    MiterClip,
199    /// A round corner is to be used to join path segments.
200    Round,
201    /// A beveled corner is to be used to join path segments.
202    /// The bevel shape is a triangle that fills the area between the two stroked
203    /// segments.
204    Bevel,
205}
206
207/// The positive or negative side of a vector or segment.
208///
209/// Given a reference vector `v0`, a vector `v1` is on the positive side
210/// if the sign of the cross product `v0 x v1` is positive.
211///
212/// This type does not use the left/right terminology to avoid confusion with
213/// left-handed / right-handed coordinate systems. Right-handed coordinate systems
214/// seem to be what a lot of people are most familiar with (especially in 2D), however
215/// most vector graphics specifications use y-down left-handed coordinate systems.
216/// Unfortunately mirroring the y axis inverts the meaning of "left" and "right", which
217/// causes confusion. In practice:
218///
219/// - In a y-down left-handed coordinate system such as `SVG`'s, `Side::Positive` is the right side.
220/// - In a y-up right-handed coordinate system, `Side::Positive` is the left side.
221#[derive(Copy, Clone, Debug, PartialEq)]
222#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
223pub enum Side {
224    Positive,
225    Negative,
226}
227
228impl Side {
229    #[inline]
230    pub fn opposite(self) -> Self {
231        match self {
232            Side::Positive => Side::Negative,
233            Side::Negative => Side::Positive,
234        }
235    }
236
237    #[inline]
238    pub fn is_positive(self) -> bool {
239        self == Side::Positive
240    }
241
242    #[inline]
243    pub fn is_negative(self) -> bool {
244        self == Side::Negative
245    }
246
247    #[inline]
248    pub fn to_f32(self) -> f32 {
249        match self {
250            Side::Positive => 1.0,
251            Side::Negative => -1.0,
252        }
253    }
254}
255
256/// The fill rule defines how to determine what is inside and what is outside of the shape.
257///
258/// See the SVG specification.
259#[derive(Copy, Clone, Debug, PartialEq)]
260#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
261pub enum FillRule {
262    EvenOdd,
263    NonZero,
264}
265
266impl FillRule {
267    #[inline]
268    pub fn is_in(&self, winding_number: i16) -> bool {
269        match *self {
270            FillRule::EvenOdd => winding_number % 2 != 0,
271            FillRule::NonZero => winding_number != 0,
272        }
273    }
274
275    #[inline]
276    pub fn is_out(&self, winding_number: i16) -> bool {
277        !self.is_in(winding_number)
278    }
279}
280
281/// The two possible orientations for the edges of a shape to be built in.
282///
283/// Positive winding corresponds to the positive orientation in trigonometry.
284#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
285#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
286pub enum Winding {
287    Positive,
288    Negative,
289}
290
291/// ID of a control point in a path.
292#[derive(Copy, Clone, PartialEq, Eq, Hash)]
293#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
294pub struct ControlPointId(pub u32);
295
296impl ControlPointId {
297    pub const INVALID: Self = ControlPointId(u32::MAX);
298    pub fn offset(self) -> usize {
299        self.0 as usize
300    }
301    pub fn to_usize(self) -> usize {
302        self.0 as usize
303    }
304    pub fn from_usize(val: usize) -> Self {
305        ControlPointId(val as u32)
306    }
307}
308
309impl fmt::Debug for ControlPointId {
310    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311        write!(f, "#{}", self.0)
312    }
313}
314
315/// ID of an endpoint point in a path.
316#[derive(Copy, Clone, PartialEq, Eq, Hash)]
317#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
318pub struct EndpointId(pub u32);
319impl EndpointId {
320    pub const INVALID: Self = EndpointId(u32::MAX);
321    pub fn offset(self) -> usize {
322        self.0 as usize
323    }
324    pub fn to_usize(self) -> usize {
325        self.0 as usize
326    }
327    pub fn from_usize(val: usize) -> Self {
328        EndpointId(val as u32)
329    }
330}
331
332impl fmt::Debug for EndpointId {
333    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334        write!(f, "#{}", self.0)
335    }
336}
337
338/// Refers to an event in a path.
339#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
340#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
341pub struct EventId(#[doc(hidden)] pub u32);
342
343impl EventId {
344    pub const INVALID: Self = EventId(u32::MAX);
345    pub fn to_usize(self) -> usize {
346        self.0 as usize
347    }
348}
349
350/// Interface for types types (typically endpoints and control points) that have
351/// a 2D position.
352pub trait Position {
353    fn position(&self) -> Point;
354}
355
356impl<U> Position for crate::geom::euclid::Point2D<f32, U> {
357    fn position(&self) -> Point {
358        self.to_untyped()
359    }
360}
361
362impl<'l, T: Position> Position for &'l T {
363    fn position(&self) -> Point {
364        (*self).position()
365    }
366}
367
368impl Position for (f32, f32) {
369    fn position(&self) -> Point {
370        Point::new(self.0, self.1)
371    }
372}
373
374impl Position for [f32; 2] {
375    fn position(&self) -> Point {
376        Point::new(self[0], self[1])
377    }
378}
379
380impl<T> Position for (Point, T) {
381    fn position(&self) -> Point {
382        self.0
383    }
384}
385
386/// Interface for objects storing endpoints and control points positions.
387///
388/// This interface can be implemented by path objects themselves or via external
389/// data structures.
390pub trait PositionStore {
391    fn get_endpoint(&self, id: EndpointId) -> Point;
392    fn get_control_point(&self, id: ControlPointId) -> Point;
393}
394
395impl<'l> PositionStore for (&'l [Point], &'l [Point]) {
396    fn get_endpoint(&self, id: EndpointId) -> Point {
397        self.0[id.to_usize()]
398    }
399    fn get_control_point(&self, id: ControlPointId) -> Point {
400        self.1[id.to_usize()]
401    }
402}
403
404/// Interface for objects storing custom attributes associated with endpoints.
405///
406/// This interface can be implemented by path objects themselves or via external
407/// data structures.
408pub trait AttributeStore {
409    /// Returns the endpoint's custom attributes as a slice of 32 bits floats.
410    ///
411    /// The size of the slice must be equal to the result of `num_attributes()`.
412    fn get(&self, id: EndpointId) -> Attributes;
413
414    /// Returns the number of float attributes per endpoint.
415    ///
416    /// All endpoints must have the same number of attributes.
417    fn num_attributes(&self) -> usize;
418}
419
420impl AttributeStore for () {
421    fn get(&self, _: EndpointId) -> Attributes {
422        NO_ATTRIBUTES
423    }
424
425    fn num_attributes(&self) -> usize {
426        0
427    }
428}
429
430/// A view over a contiguous storage of custom attributes.
431pub struct AttributeSlice<'l> {
432    data: &'l [f32],
433    stride: usize,
434}
435
436impl<'l> AttributeSlice<'l> {
437    pub fn new(data: &'l [f32], num_attributes: usize) -> Self {
438        AttributeSlice {
439            data,
440            stride: num_attributes,
441        }
442    }
443}
444
445impl<'l> AttributeStore for AttributeSlice<'l> {
446    fn get(&self, id: EndpointId) -> Attributes {
447        let start = id.to_usize() * self.stride;
448        let end = start + self.stride;
449        &self.data[start..end]
450    }
451
452    fn num_attributes(&self) -> usize {
453        self.stride
454    }
455}
456
457/// An alias for `usize`.
458pub type AttributeIndex = usize;
459/// An alias for a slice of `f32` values.
460pub type Attributes<'l> = &'l [f32];
461/// An empty attribute slice.
462pub const NO_ATTRIBUTES: Attributes<'static> = &[];