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