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