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