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> = &[];