lyon_path-1.0.5/.cargo_vcs_info.json0000644000000001510000000000100130310ustar { "git": { "sha1": "3c709ff5f263897447da9ed8fd153614728cf356" }, "path_in_vcs": "crates/path" }lyon_path-1.0.5/Cargo.toml0000644000000023470000000000100110400ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "lyon_path" version = "1.0.5" authors = ["Nicolas Silva "] description = "Types and utilities to store, build and iterate over 2D paths." documentation = "https://docs.rs/lyon_path/" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/nical/lyon" resolver = "2" [lib] name = "lyon_path" [dependencies.lyon_geom] version = "1.0.4" default-features = false [dependencies.num-traits] version = "0.2.15" features = ["libm"] default-features = false [dependencies.serde] version = "1.0" features = ["serde_derive"] optional = true default-features = false [features] default = ["std"] serialization = [ "serde", "lyon_geom/serialization", ] std = [ "lyon_geom/std", "num-traits/std", ] lyon_path-1.0.5/Cargo.toml.orig000064400000000000000000000013701046102023000145140ustar 00000000000000[package] name = "lyon_path" version = "1.0.5" description = "Types and utilities to store, build and iterate over 2D paths." authors = ["Nicolas Silva "] repository = "https://github.com/nical/lyon" documentation = "https://docs.rs/lyon_path/" license = "MIT OR Apache-2.0" workspace = "../.." edition = "2018" [lib] name = "lyon_path" [features] default = ["std"] std = ["lyon_geom/std", "num-traits/std"] serialization = ["serde", "lyon_geom/serialization"] [dependencies] lyon_geom = { version = "1.0.4", path = "../geom", default-features = false } num-traits = { version = "0.2.15", default-features = false, features = ["libm"] } serde = { version = "1.0", optional = true, features = ["serde_derive"], default-features = false } lyon_path-1.0.5/README.md000064400000000000000000000007231046102023000131050ustar 00000000000000# lyon::path Path data structures and tools for vector graphics.

crates.io documentation

`lyon_path` can be used as a standalone crate or as part of [lyon](https://docs.rs/lyon/) via the `lyon::path` module. lyon_path-1.0.5/src/builder.rs000064400000000000000000001535131046102023000144170ustar 00000000000000//! Path building utilities. //! //! ## `PathBuilder` or `SvgPathBuilder` //! //! Path can be built via either of two abstractions: //! //! - [PathBuilder](trait.PathBuilder.html) is a simple and efficient interface which //! does not deal with any ambiguous cases. //! - [SvgPathBuilder](trait.SvgPathBuilder.html) is a higher-level interface that //! follows SVG's specification, removing the the burden of dealing with special cases //! from the user at a run-time cost. //! //! `SvgPathBuilder` may be a better choice when interactive with SVG, or dealing with arbitrary //! input. `PathBuilder`. `PathBuilder` is probably a more useful trait to implement when creating //! a new path data structure since all `PathBuilder` implementations automatically get an //! `SvgPathBuilder` adapter (see the `with_svg` method). It may also make sense to use the //! `PathBuilder` API when following a specification that behaves like SVG paths or when no //! performance can be traded for convenience. //! //! ## Examples //! //! The following example shows how to create a simple path using the //! [PathBuilder](trait.PathBuilder.html) interface. //! //! ``` //! use lyon_path::{Path, geom::point}; //! //! let mut builder = Path::builder(); //! //! // All sub-paths *must* have be contained in a begin/end pair. //! builder.begin(point(0.0, 0.0)); //! builder.line_to(point(1.0, 0.0)); //! builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0)); //! builder.end(false); //! //! builder.begin(point(10.0, 0.0)); //! builder.cubic_bezier_to(point(12.0, 2.0), point(11.0, 2.0), point(5.0, 0.0)); //! builder.close(); // close() is equivalent to end(true). //! //! let path = builder.build(); //! ``` //! //! The same path can be built using the `SvgPathBuilder` API: //! //! ``` //! use lyon_path::{Path, geom::{point, vector}, builder::SvgPathBuilder}; //! //! // Use the SVG adapter. //! let mut builder = Path::builder().with_svg(); //! //! // All sub-paths *must* have be contained in a begin/end pair. //! builder.move_to(point(0.0, 0.0)); //! builder.line_to(point(1.0, 0.0)); //! builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0)); //! // No need to explicitly end a sub-path. //! //! builder.move_to(point(10.0, 0.0)); //! builder.relative_cubic_bezier_to(vector(2.0, 2.0), vector(1.0, 2.0), vector(-5.0, 0.0)); //! builder.close(); //! //! let path = builder.build(); //! ``` //! //! Implementors of the `PathBuilder` trait automatically gain access to a few other adapters. //! For example a builder that approximates curves with a sequence of line segments: //! //! ``` //! use lyon_path::{Path, geom::point}; //! //! let tolerance = 0.05;// maximum distance between a curve and its approximation. //! let mut builder = Path::builder().flattened(tolerance); //! //! builder.begin(point(0.0, 0.0)); //! builder.quadratic_bezier_to(point(1.0, 0.0), point(1.0, 1.0)); //! builder.end(true); //! //! // The resulting path contains only Begin, Line and End events. //! let path = builder.build(); //! ``` //! use crate::events::{Event, PathEvent}; use crate::geom::{traits::Transformation, Arc, ArcFlags, LineSegment, SvgArc}; use crate::math::*; use crate::path::Verb; use crate::polygon::Polygon; use crate::{Attributes, EndpointId, Winding, NO_ATTRIBUTES}; use core::f32::consts::PI; use core::marker::Sized; use alloc::vec; use alloc::vec::Vec; #[cfg(not(feature = "std"))] use num_traits::Float; /// The radius of each corner of a rounded rectangle. #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)] pub struct BorderRadii { pub top_left: f32, pub top_right: f32, pub bottom_left: f32, pub bottom_right: f32, } impl BorderRadii { pub fn new(radius: f32) -> Self { let r = radius.abs(); BorderRadii { top_left: r, top_right: r, bottom_left: r, bottom_right: r, } } } impl core::fmt::Display for BorderRadii { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // In the order of a well known convention (CSS) clockwise from top left write!( f, "BorderRadii({}, {}, {}, {})", self.top_left, self.top_right, self.bottom_left, self.bottom_right ) } } /// A convenience wrapper for `PathBuilder` without custom attributes. /// /// See the [PathBuilder] trait. /// /// This simply forwards to an underlying `PathBuilder` implementation, /// using no attributes. #[derive(Clone, Debug, PartialEq, Hash)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct NoAttributes { pub(crate) inner: B, } impl NoAttributes { #[inline] pub fn wrap(inner: B) -> Self { assert_eq!(inner.num_attributes(), 0); NoAttributes { inner } } pub fn new() -> Self where B: Default, { NoAttributes::wrap(B::default()) } pub fn with_capacity(endpoints: usize, ctrl_points: usize) -> Self where B: Default, { let mut builder = B::default(); builder.reserve(endpoints, ctrl_points); NoAttributes::wrap(builder) } /// Starts a new sub-path at a given position. /// /// There must be no sub-path in progress when this method is called. /// `at` becomes the current position of the sub-path. #[inline] pub fn begin(&mut self, at: Point) -> EndpointId { self.inner.begin(at, NO_ATTRIBUTES) } /// Ends the current sub path. /// /// A sub-path must be in progress when this method is called. /// After this method is called, there is no sub-path in progress until /// `begin` is called again. #[inline] pub fn end(&mut self, close: bool) { self.inner.end(close); } /// Closes the current sub path. /// /// Shorthand for `builder.end(true)`. #[inline] pub fn close(&mut self) { self.inner.close(); } /// Adds a line segment to the current sub-path. /// /// A sub-path must be in progress when this method is called. #[inline] pub fn line_to(&mut self, to: Point) -> EndpointId { self.inner.line_to(to, NO_ATTRIBUTES) } /// Adds a quadratic bézier curve to the current sub-path. /// /// A sub-path must be in progress when this method is called. #[inline] pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId { self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES) } /// Adds a cubic bézier curve to the current sub-path. /// /// A sub-path must be in progress when this method is called. #[inline] pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId { self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES) } /// Hints at the builder that a certain number of endpoints and control /// points will be added. /// /// The Builder implementation may use this information to pre-allocate /// memory as an optimization. #[inline] pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.inner.reserve(endpoints, ctrl_points); } /// Applies the provided path event. /// /// By default this calls one of `begin`, `end`, `line`, `quadratic_bezier_segment`, /// or `cubic_bezier_segment` according to the path event. /// /// The requirements for each method apply to the corresponding event. #[inline] pub fn path_event(&mut self, event: PathEvent) { self.inner.path_event(event, NO_ATTRIBUTES); } /// Adds a sub-path from a polygon. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_polygon(&mut self, polygon: Polygon) { self.inner.add_polygon(polygon, NO_ATTRIBUTES); } /// Adds a sub-path containing a single point. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_point(&mut self, at: Point) -> EndpointId { self.inner.add_point(at, NO_ATTRIBUTES) } /// Adds a sub-path containing a single line segment. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_line_segment(&mut self, line: &LineSegment) -> (EndpointId, EndpointId) { self.inner.add_line_segment(line, NO_ATTRIBUTES) } /// Adds a sub-path containing an ellipse. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_ellipse( &mut self, center: Point, radii: Vector, x_rotation: Angle, winding: Winding, ) { self.inner .add_ellipse(center, radii, x_rotation, winding, NO_ATTRIBUTES); } /// Adds a sub-path containing a circle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_circle(&mut self, center: Point, radius: f32, winding: Winding) where B: Sized, { self.inner .add_circle(center, radius, winding, NO_ATTRIBUTES); } /// Adds a sub-path containing a rectangle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_rectangle(&mut self, rect: &Box2D, winding: Winding) { self.inner.add_rectangle(rect, winding, NO_ATTRIBUTES); } /// Adds a sub-path containing a rectangle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. #[inline] pub fn add_rounded_rectangle(&mut self, rect: &Box2D, radii: &BorderRadii, winding: Winding) where B: Sized, { self.inner .add_rounded_rectangle(rect, radii, winding, NO_ATTRIBUTES); } /// Returns a builder that approximates all curves with sequences of line segments. #[inline] pub fn flattened(self, tolerance: f32) -> NoAttributes> where B: Sized, { NoAttributes { inner: Flattened::new(self.inner, tolerance), } } /// Returns a builder that applies the given transformation to all positions. #[inline] pub fn transformed( self, transform: Transform, ) -> NoAttributes> where B: Sized, Transform: Transformation, { NoAttributes { inner: Transformed::new(self.inner, transform), } } /// Returns a builder that support SVG commands. /// /// This must be called before starting to add any sub-path. #[inline] pub fn with_svg(self) -> WithSvg where B: Sized, { WithSvg::new(self.inner) } /// Builds a path object, consuming the builder. #[inline] pub fn build

(self) -> P where B: Build, { self.inner.build() } #[inline] pub fn inner(&self) -> &B { &self.inner } #[inline] pub fn inner_mut(&mut self) -> &mut B { &mut self.inner } #[inline] pub fn into_inner(self) -> B { self.inner } } impl PathBuilder for NoAttributes { #[inline] fn num_attributes(&self) -> usize { 0 } #[inline] fn begin(&mut self, at: Point, _attributes: Attributes) -> EndpointId { self.inner.begin(at, NO_ATTRIBUTES) } #[inline] fn end(&mut self, close: bool) { self.inner.end(close); } #[inline] fn line_to(&mut self, to: Point, _attributes: Attributes) -> EndpointId { self.inner.line_to(to, NO_ATTRIBUTES) } #[inline] fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES) } #[inline] fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES) } #[inline] fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.inner.reserve(endpoints, ctrl_points) } } impl Build for NoAttributes { type PathType = B::PathType; fn build(self) -> B::PathType { self.inner.build() } } impl Default for NoAttributes { fn default() -> Self { Self::new() } } /// The base path building interface. /// /// Unlike `SvgPathBuilder`, this interface strictly requires sub-paths to be manually /// started and ended (See the `begin` and `end` methods). /// All positions are provided in absolute coordinates. /// /// The goal of this interface is to abstract over simple and fast implementations that /// do not deal with corner cases such as adding segments without starting a sub-path. /// /// More elaborate interfaces are built on top of the provided primitives. In particular, /// the `SvgPathBuilder` trait providing more permissive and richer interface is /// automatically implemented via the `WithSvg` adapter (See the `with_svg` method). pub trait PathBuilder { fn num_attributes(&self) -> usize; /// Starts a new sub-path at a given position. /// /// There must be no sub-path in progress when this method is called. /// `at` becomes the current position of the sub-path. fn begin(&mut self, at: Point, custom_attributes: Attributes) -> EndpointId; /// Ends the current sub path. /// /// A sub-path must be in progress when this method is called. /// After this method is called, there is no sub-path in progress until /// `begin` is called again. fn end(&mut self, close: bool); /// Closes the current sub path. /// /// Shorthand for `builder.end(true)`. fn close(&mut self) { self.end(true) } /// Adds a line segment to the current sub-path. /// /// A sub-path must be in progress when this method is called. fn line_to(&mut self, to: Point, custom_attributes: Attributes) -> EndpointId; /// Adds a quadratic bézier curve to the current sub-path. /// /// A sub-path must be in progress when this method is called. fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, custom_attributes: Attributes, ) -> EndpointId; /// Adds a cubic bézier curve to the current sub-path. /// /// A sub-path must be in progress when this method is called. fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, custom_attributes: Attributes, ) -> EndpointId; /// Hints at the builder that a certain number of endpoints and control /// points will be added. /// /// The Builder implementation may use this information to pre-allocate /// memory as an optimization. fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {} /// Applies the provided path event. /// /// By default this calls one of `begin`, `end`, `line`, `quadratic_bezier_segment`, /// or `cubic_bezier_segment` according to the path event. /// /// The requirements for each method apply to the corresponding event. fn path_event(&mut self, event: PathEvent, attributes: Attributes) { match event { PathEvent::Begin { at } => { self.begin(at, attributes); } PathEvent::Line { to, .. } => { self.line_to(to, attributes); } PathEvent::Quadratic { ctrl, to, .. } => { self.quadratic_bezier_to(ctrl, to, attributes); } PathEvent::Cubic { ctrl1, ctrl2, to, .. } => { self.cubic_bezier_to(ctrl1, ctrl2, to, attributes); } PathEvent::End { close, .. } => { self.end(close); } } } fn event(&mut self, event: Event<(Point, Attributes), Point>) { match event { Event::Begin { at } => { self.begin(at.0, at.1); } Event::Line { to, .. } => { self.line_to(to.0, to.1); } Event::Quadratic { ctrl, to, .. } => { self.quadratic_bezier_to(ctrl, to.0, to.1); } Event::Cubic { ctrl1, ctrl2, to, .. } => { self.cubic_bezier_to(ctrl1, ctrl2, to.0, to.1); } Event::End { close, .. } => { self.end(close); } } } /// Adds a sub-path from a polygon. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_polygon(&mut self, polygon: Polygon, attributes: Attributes) { if polygon.points.is_empty() { return; } self.reserve(polygon.points.len(), 0); self.begin(polygon.points[0], attributes); for p in &polygon.points[1..] { self.line_to(*p, attributes); } self.end(polygon.closed); } /// Adds a sub-path containing a single point. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_point(&mut self, at: Point, attributes: Attributes) -> EndpointId { let id = self.begin(at, attributes); self.end(false); id } /// Adds a sub-path containing a single line segment. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_line_segment( &mut self, line: &LineSegment, attributes: Attributes, ) -> (EndpointId, EndpointId) { let a = self.begin(line.from, attributes); let b = self.line_to(line.to, attributes); self.end(false); (a, b) } /// Adds a sub-path containing an ellipse. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_ellipse( &mut self, center: Point, radii: Vector, x_rotation: Angle, winding: Winding, attributes: Attributes, ) { let dir = match winding { Winding::Positive => 1.0, Winding::Negative => -1.0, }; let arc = Arc { center, radii, x_rotation, start_angle: Angle::radians(0.0), sweep_angle: Angle::radians(2.0 * PI) * dir, }; self.begin(arc.sample(0.0), attributes); arc.for_each_quadratic_bezier(&mut |curve| { self.quadratic_bezier_to(curve.ctrl, curve.to, attributes); }); self.end(true); } /// Adds a sub-path containing a circle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_circle(&mut self, center: Point, radius: f32, winding: Winding, attributes: Attributes) where Self: Sized, { add_circle(self, center, radius, winding, attributes); } /// Adds a sub-path containing a rectangle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_rectangle(&mut self, rect: &Box2D, winding: Winding, attributes: Attributes) { match winding { Winding::Positive => self.add_polygon( Polygon { points: &[ rect.min, point(rect.max.x, rect.min.y), rect.max, point(rect.min.x, rect.max.y), ], closed: true, }, attributes, ), Winding::Negative => self.add_polygon( Polygon { points: &[ rect.min, point(rect.min.x, rect.max.y), rect.max, point(rect.max.x, rect.min.y), ], closed: true, }, attributes, ), }; } /// Adds a sub-path containing a rectangle. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_rounded_rectangle( &mut self, rect: &Box2D, radii: &BorderRadii, winding: Winding, custom_attributes: Attributes, ) where Self: Sized, { add_rounded_rectangle(self, rect, radii, winding, custom_attributes); } /// Returns a builder that approximates all curves with sequences of line segments. fn flattened(self, tolerance: f32) -> Flattened where Self: Sized, { Flattened::new(self, tolerance) } /// Returns a builder that applies the given transformation to all positions. fn transformed(self, transform: Transform) -> Transformed where Self: Sized, Transform: Transformation, { Transformed::new(self, transform) } /// Returns a builder that support SVG commands. /// /// This must be called before starting to add any sub-path. fn with_svg(self) -> WithSvg where Self: Sized, { WithSvg::new(self) } } /// A path building interface that tries to stay close to SVG's path specification. /// /// /// Some of the wording in the documentation of this trait is borrowed from the SVG /// specification. /// /// Unlike `PathBuilder`, implementations of this trait are expected to deal with /// various corners cases such as adding segments without starting a sub-path. pub trait SvgPathBuilder { /// Start a new sub-path at the given position. /// /// Corresponding SVG command: `M`. /// /// This command establishes a new initial point and a new current point. The effect /// is as if the "pen" were lifted and moved to a new location. /// If a sub-path is in progress, it is ended without being closed. fn move_to(&mut self, to: Point); /// Ends the current sub-path by connecting it back to its initial point. /// /// Corresponding SVG command: `Z`. /// /// A straight line is drawn from the current point to the initial point of the /// current sub-path. /// The current position is set to the initial position of the sub-path that was /// closed. fn close(&mut self); /// Adds a line segment to the current sub-path. /// /// Corresponding SVG command: `L`. /// /// The segment starts at the builder's current position. /// If this is the very first command of the path (the builder therefore does not /// have a current position), the `line_to` command is replaced with a `move_to(to)`. fn line_to(&mut self, to: Point); /// Adds a quadratic bézier segment to the current sub-path. /// /// Corresponding SVG command: `Q`. /// /// The segment starts at the builder's current position. /// If this is the very first command of the path (the builder therefore does not /// have a current position), the `quadratic_bezier_to` command is replaced with /// a `move_to(to)`. fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point); /// Adds a cubic bézier segment to the current sub-path. /// /// Corresponding SVG command: `C`. /// /// The segment starts at the builder's current position. /// If this is the very first command of the path (the builder therefore does not /// have a current position), the `cubic_bezier_to` command is replaced with /// a `move_to(to)`. fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point); /// Equivalent to `move_to` in relative coordinates. /// /// Corresponding SVG command: `m`. /// /// The provided coordinates are offsets relative to the current position of /// the builder. fn relative_move_to(&mut self, to: Vector); /// Equivalent to `line_to` in relative coordinates. /// /// Corresponding SVG command: `l`. /// /// The provided coordinates are offsets relative to the current position of /// the builder. fn relative_line_to(&mut self, to: Vector); /// Equivalent to `quadratic_bezier_to` in relative coordinates. /// /// Corresponding SVG command: `q`. /// /// the provided coordinates are offsets relative to the current position of /// the builder. fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector); /// Equivalent to `cubic_bezier_to` in relative coordinates. /// /// Corresponding SVG command: `c`. /// /// The provided coordinates are offsets relative to the current position of /// the builder. fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector); /// Equivalent to `cubic_bezier_to` with implicit first control point. /// /// Corresponding SVG command: `S`. /// /// The first control point is assumed to be the reflection of the second /// control point on the previous command relative to the current point. /// If there is no previous command or if the previous command was not a /// cubic bézier segment, the first control point is coincident with /// the current position. fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point); /// Equivalent to `smooth_cubic_bezier_to` in relative coordinates. /// /// Corresponding SVG command: `s`. /// /// The provided coordinates are offsets relative to the current position of /// the builder. fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector); /// Equivalent to `quadratic_bezier_to` with implicit control point. /// /// Corresponding SVG command: `T`. /// /// The control point is assumed to be the reflection of the control /// point on the previous command relative to the current point. /// If there is no previous command or if the previous command was not a /// quadratic bézier segment, a line segment is added instead. fn smooth_quadratic_bezier_to(&mut self, to: Point); /// Equivalent to `smooth_quadratic_bezier_to` in relative coordinates. /// /// Corresponding SVG command: `t`. /// /// The provided coordinates are offsets relative to the current position of /// the builder. fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector); /// Adds an horizontal line segment. /// /// Corresponding SVG command: `H`. /// /// Equivalent to `line_to`, using the y coordinate of the current position. fn horizontal_line_to(&mut self, x: f32); /// Adds an horizontal line segment in relative coordinates. /// /// Corresponding SVG command: `l`. /// /// Equivalent to `line_to`, using the y coordinate of the current position. /// `dx` is the horizontal offset relative to the current position. fn relative_horizontal_line_to(&mut self, dx: f32); /// Adds a vertical line segment. /// /// Corresponding SVG command: `V`. /// /// Equivalent to `line_to`, using the x coordinate of the current position. fn vertical_line_to(&mut self, y: f32); /// Adds a vertical line segment in relative coordinates. /// /// Corresponding SVG command: `v`. /// /// Equivalent to `line_to`, using the y coordinate of the current position. /// `dy` is the horizontal offset relative to the current position. fn relative_vertical_line_to(&mut self, dy: f32); /// Adds an elliptical arc. /// /// Corresponding SVG command: `A`. /// /// The arc starts at the current point and ends at `to`. /// The size and orientation of the ellipse are defined by `radii` and an `x_rotation`, /// which indicates how the ellipse as a whole is rotated relative to the current coordinate /// system. The center of the ellipse is calculated automatically to satisfy the constraints /// imposed by the other parameters. the arc `flags` contribute to the automatic calculations /// and help determine how the arc is built. fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point); /// Equivalent to `arc_to` in relative coordinates. /// /// Corresponding SVG command: `a`. /// /// The provided `to` coordinates are offsets relative to the current position of /// the builder. fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector); /// Hints at the builder that a certain number of endpoints and control /// points will be added. /// /// The Builder implementation may use this information to pre-allocate /// memory as an optimization. fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {} /// Adds a sub-path from a polygon. /// /// There must be no sub-path in progress when this method is called. /// No sub-path is in progress after the method is called. fn add_polygon(&mut self, polygon: Polygon) { if polygon.points.is_empty() { return; } self.reserve(polygon.points.len(), 0); self.move_to(polygon.points[0]); for p in &polygon.points[1..] { self.line_to(*p); } if polygon.closed { self.close(); } } } /// Builds a path. /// /// This trait is separate from `PathBuilder` and `SvgPathBuilder` to allow them to /// be used as trait object (which isn't possible when a method returns an associated /// type). pub trait Build { /// The type of object that is created by this builder. type PathType; /// Builds a path object, consuming the builder. fn build(self) -> Self::PathType; } /// A Builder that approximates curves with successions of line segments. pub struct Flattened { builder: Builder, current_position: Point, tolerance: f32, prev_attributes: Vec, attribute_buffer: Vec, } impl Build for Flattened { type PathType = Builder::PathType; fn build(self) -> Builder::PathType { self.builder.build() } } impl PathBuilder for Flattened { fn num_attributes(&self) -> usize { self.builder.num_attributes() } fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { self.current_position = at; self.builder.begin(at, attributes) } fn end(&mut self, close: bool) { self.builder.end(close) } fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { let id = self.builder.line_to(to, attributes); self.current_position = to; self.prev_attributes.copy_from_slice(attributes); id } fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = crate::private::flatten_quadratic_bezier( self.tolerance, self.current_position, ctrl, to, attributes, &self.prev_attributes, &mut self.builder, &mut self.attribute_buffer, ); self.current_position = to; self.prev_attributes.copy_from_slice(attributes); id } fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = crate::private::flatten_cubic_bezier( self.tolerance, self.current_position, ctrl1, ctrl2, to, attributes, &self.prev_attributes, &mut self.builder, &mut self.attribute_buffer, ); self.current_position = to; self.prev_attributes.copy_from_slice(attributes); id } fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints + ctrl_points * 4, 0); } } impl Flattened { pub fn new(builder: Builder, tolerance: f32) -> Flattened { let n = builder.num_attributes(); Flattened { builder, current_position: point(0.0, 0.0), tolerance, prev_attributes: vec![0.0; n], attribute_buffer: vec![0.0; n], } } pub fn build(self) -> Builder::PathType where Builder: Build, { self.builder.build() } pub fn set_tolerance(&mut self, tolerance: f32) { self.tolerance = tolerance } } /// Builds a path with a transformation applied. pub struct Transformed { builder: Builder, transform: Transform, } impl Transformed { #[inline] pub fn new(builder: Builder, transform: Transform) -> Self { Transformed { builder, transform } } #[inline] pub fn set_transform(&mut self, transform: Transform) { self.transform = transform; } } impl Build for Transformed { type PathType = Builder::PathType; #[inline] fn build(self) -> Builder::PathType { self.builder.build() } } impl PathBuilder for Transformed where Builder: PathBuilder, Transform: Transformation, { fn num_attributes(&self) -> usize { self.builder.num_attributes() } #[inline] fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { self.builder .begin(self.transform.transform_point(at), attributes) } #[inline] fn end(&mut self, close: bool) { self.builder.end(close) } #[inline] fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { self.builder .line_to(self.transform.transform_point(to), attributes) } #[inline] fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.builder.quadratic_bezier_to( self.transform.transform_point(ctrl), self.transform.transform_point(to), attributes, ) } #[inline] fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.builder.cubic_bezier_to( self.transform.transform_point(ctrl1), self.transform.transform_point(ctrl2), self.transform.transform_point(to), attributes, ) } #[inline] fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints, ctrl_points); } } /// Implements an SVG-like building interface on top of a PathBuilder. pub struct WithSvg { builder: Builder, first_position: Point, current_position: Point, last_ctrl: Point, last_cmd: Verb, need_moveto: bool, is_empty: bool, attribute_buffer: Vec, } impl WithSvg { pub fn new(builder: Builder) -> Self { let attribute_buffer = vec![0.0; builder.num_attributes()]; WithSvg { builder, first_position: point(0.0, 0.0), current_position: point(0.0, 0.0), last_ctrl: point(0.0, 0.0), need_moveto: true, is_empty: true, last_cmd: Verb::End, attribute_buffer, } } pub fn build(mut self) -> Builder::PathType where Builder: Build, { self.end_if_needed(); self.builder.build() } pub fn flattened(self, tolerance: f32) -> WithSvg> { WithSvg::new(Flattened::new(self.builder, tolerance)) } pub fn transformed( self, transform: Transform, ) -> WithSvg> where Transform: Transformation, { WithSvg::new(Transformed::new(self.builder, transform)) } pub fn move_to(&mut self, to: Point) -> EndpointId { self.end_if_needed(); let id = self.builder.begin(to, &self.attribute_buffer); self.is_empty = false; self.need_moveto = false; self.first_position = to; self.current_position = to; self.last_cmd = Verb::Begin; id } pub fn line_to(&mut self, to: Point) -> EndpointId { if let Some(id) = self.begin_if_needed(&to) { return id; } self.current_position = to; self.last_cmd = Verb::LineTo; self.builder.line_to(to, &self.attribute_buffer) } pub fn close(&mut self) { if self.need_moveto { return; } // Relative path ops tend to accumulate small floating point error, // which results in the last segment ending almost but not quite at the // start of the sub-path, causing a new edge to be inserted which often // intersects with the first or last edge. This can affect algorithms that // Don't handle self-intersecting paths. // Deal with this by snapping the last point if it is very close to the // start of the sub path. // // TODO // if let Some(p) = self.builder.points.last_mut() { // let d = (*p - self.first_position).abs(); // if d.x + d.y < 0.0001 { // *p = self.first_position; // } // } self.current_position = self.first_position; self.need_moveto = true; self.last_cmd = Verb::Close; self.builder.close(); } pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId { if let Some(id) = self.begin_if_needed(&to) { return id; } self.current_position = to; self.last_cmd = Verb::QuadraticTo; self.last_ctrl = ctrl; self.builder .quadratic_bezier_to(ctrl, to, &self.attribute_buffer) } pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId { if let Some(id) = self.begin_if_needed(&to) { return id; } self.current_position = to; self.last_cmd = Verb::CubicTo; self.last_ctrl = ctrl2; self.builder .cubic_bezier_to(ctrl1, ctrl2, to, &self.attribute_buffer) } pub fn arc(&mut self, center: Point, radii: Vector, sweep_angle: Angle, x_rotation: Angle) { nan_check(center); nan_check(radii.to_point()); debug_assert!(!sweep_angle.get().is_nan()); debug_assert!(!x_rotation.get().is_nan()); self.last_ctrl = self.current_position; // If the center is equal to the current position, the start and end angles aren't // defined, so we just skip the arc to avoid generating NaNs that will cause issues // later. use lyon_geom::euclid::approxeq::ApproxEq; if self.current_position.approx_eq(¢er) { return; } let start_angle = (self.current_position - center).angle_from_x_axis() - x_rotation; let arc = Arc { center, radii, start_angle, sweep_angle, x_rotation, }; // If the current position is not on the arc, move or line to the beginning of the // arc. let arc_start = arc.from(); if self.need_moveto { self.move_to(arc_start); } else if (arc_start - self.current_position).square_length() < 0.01 { self.builder.line_to(arc_start, &self.attribute_buffer); } arc.for_each_quadratic_bezier(&mut |curve| { self.builder .quadratic_bezier_to(curve.ctrl, curve.to, &self.attribute_buffer); self.current_position = curve.to; }); } /// Ensures the current sub-path has a moveto command. /// /// Returns an ID if the command should be skipped and the ID returned instead. #[inline(always)] fn begin_if_needed(&mut self, default: &Point) -> Option { if self.need_moveto { return self.insert_move_to(default); } None } #[inline(never)] fn insert_move_to(&mut self, default: &Point) -> Option { if self.is_empty { return Some(self.move_to(*default)); } self.move_to(self.first_position); None } fn end_if_needed(&mut self) { if (self.last_cmd as u8) <= (Verb::Begin as u8) { self.builder.end(false); } } pub fn current_position(&self) -> Point { self.current_position } pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints, ctrl_points); } fn get_smooth_cubic_ctrl(&self) -> Point { match self.last_cmd { Verb::CubicTo => self.current_position + (self.current_position - self.last_ctrl), _ => self.current_position, } } fn get_smooth_quadratic_ctrl(&self) -> Point { match self.last_cmd { Verb::QuadraticTo => self.current_position + (self.current_position - self.last_ctrl), _ => self.current_position, } } fn relative_to_absolute(&self, v: Vector) -> Point { self.current_position + v } } impl WithSvg> where Builder: PathBuilder, Transform: Transformation, { #[inline] pub fn set_transform(&mut self, transform: Transform) { self.builder.set_transform(transform); } } impl Build for WithSvg { type PathType = Builder::PathType; fn build(mut self) -> Builder::PathType { self.end_if_needed(); self.builder.build() } } impl SvgPathBuilder for WithSvg { fn move_to(&mut self, to: Point) { self.move_to(to); } fn close(&mut self) { self.close(); } fn line_to(&mut self, to: Point) { self.line_to(to); } fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) { self.quadratic_bezier_to(ctrl, to); } fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) { self.cubic_bezier_to(ctrl1, ctrl2, to); } fn relative_move_to(&mut self, to: Vector) { let to = self.relative_to_absolute(to); self.move_to(to); } fn relative_line_to(&mut self, to: Vector) { let to = self.relative_to_absolute(to); self.line_to(to); } fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector) { let ctrl = self.relative_to_absolute(ctrl); let to = self.relative_to_absolute(to); self.quadratic_bezier_to(ctrl, to); } fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector) { let to = self.relative_to_absolute(to); let ctrl1 = self.relative_to_absolute(ctrl1); let ctrl2 = self.relative_to_absolute(ctrl2); self.cubic_bezier_to(ctrl1, ctrl2, to); } fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point) { let ctrl1 = self.get_smooth_cubic_ctrl(); self.cubic_bezier_to(ctrl1, ctrl2, to); } fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector) { let ctrl1 = self.get_smooth_cubic_ctrl(); let ctrl2 = self.relative_to_absolute(ctrl2); let to = self.relative_to_absolute(to); self.cubic_bezier_to(ctrl1, ctrl2, to); } fn smooth_quadratic_bezier_to(&mut self, to: Point) { let ctrl = self.get_smooth_quadratic_ctrl(); self.quadratic_bezier_to(ctrl, to); } fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector) { let ctrl = self.get_smooth_quadratic_ctrl(); let to = self.relative_to_absolute(to); self.quadratic_bezier_to(ctrl, to); } fn horizontal_line_to(&mut self, x: f32) { let y = self.current_position.y; self.line_to(point(x, y)); } fn relative_horizontal_line_to(&mut self, dx: f32) { let p = self.current_position; self.line_to(point(p.x + dx, p.y)); } fn vertical_line_to(&mut self, y: f32) { let x = self.current_position.x; self.line_to(point(x, y)); } fn relative_vertical_line_to(&mut self, dy: f32) { let p = self.current_position; self.line_to(point(p.x, p.y + dy)); } fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point) { let svg_arc = SvgArc { from: self.current_position, to, radii, x_rotation, flags: ArcFlags { large_arc: flags.large_arc, sweep: flags.sweep, }, }; if svg_arc.is_straight_line() { self.line_to(to); } else { let arc = svg_arc.to_arc(); self.arc(arc.center, arc.radii, arc.sweep_angle, arc.x_rotation); } } fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector) { let to = self.relative_to_absolute(to); self.arc_to(radii, x_rotation, flags, to); } fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints, ctrl_points); } } /// Tessellate the stroke for an axis-aligned rounded rectangle. fn add_circle( builder: &mut Builder, center: Point, radius: f32, winding: Winding, attributes: Attributes, ) { let radius = radius.abs(); let dir = match winding { Winding::Positive => 1.0, Winding::Negative => -1.0, }; // https://spencermortensen.com/articles/bezier-circle/ const CONSTANT_FACTOR: f32 = 0.55191505; let d = radius * CONSTANT_FACTOR; builder.begin(center + vector(-radius, 0.0), attributes); let ctrl_0 = center + vector(-radius, -d * dir); let ctrl_1 = center + vector(-d, -radius * dir); let mid = center + vector(0.0, -radius * dir); builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes); let ctrl_0 = center + vector(d, -radius * dir); let ctrl_1 = center + vector(radius, -d * dir); let mid = center + vector(radius, 0.0); builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes); let ctrl_0 = center + vector(radius, d * dir); let ctrl_1 = center + vector(d, radius * dir); let mid = center + vector(0.0, radius * dir); builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes); let ctrl_0 = center + vector(-d, radius * dir); let ctrl_1 = center + vector(-radius, d * dir); let mid = center + vector(-radius, 0.0); builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes); builder.close(); } /// Tessellate the stroke for an axis-aligned rounded rectangle. fn add_rounded_rectangle( builder: &mut Builder, rect: &Box2D, radii: &BorderRadii, winding: Winding, attributes: Attributes, ) { let w = rect.width(); let h = rect.height(); let x_min = rect.min.x; let y_min = rect.min.y; let x_max = rect.max.x; let y_max = rect.max.y; let min_wh = w.min(h); let mut tl = radii.top_left.abs().min(min_wh); let mut tr = radii.top_right.abs().min(min_wh); let mut bl = radii.bottom_left.abs().min(min_wh); let mut br = radii.bottom_right.abs().min(min_wh); // clamp border radii if they don't fit in the rectangle. if tl + tr > w { let x = (tl + tr - w) * 0.5; tl -= x; tr -= x; } if bl + br > w { let x = (bl + br - w) * 0.5; bl -= x; br -= x; } if tr + br > h { let x = (tr + br - h) * 0.5; tr -= x; br -= x; } if tl + bl > h { let x = (tl + bl - h) * 0.5; tl -= x; bl -= x; } // https://spencermortensen.com/articles/bezier-circle/ const CONSTANT_FACTOR: f32 = 0.55191505; let tl_d = tl * CONSTANT_FACTOR; let tl_corner = point(x_min, y_min); let tr_d = tr * CONSTANT_FACTOR; let tr_corner = point(x_max, y_min); let br_d = br * CONSTANT_FACTOR; let br_corner = point(x_max, y_max); let bl_d = bl * CONSTANT_FACTOR; let bl_corner = point(x_min, y_max); let points = [ point(x_min, y_min + tl), // begin tl_corner + vector(0.0, tl - tl_d), // control tl_corner + vector(tl - tl_d, 0.0), // control tl_corner + vector(tl, 0.0), // end point(x_max - tr, y_min), tr_corner + vector(-tr + tr_d, 0.0), tr_corner + vector(0.0, tr - tr_d), tr_corner + vector(0.0, tr), point(x_max, y_max - br), br_corner + vector(0.0, -br + br_d), br_corner + vector(-br + br_d, 0.0), br_corner + vector(-br, 0.0), point(x_min + bl, y_max), bl_corner + vector(bl - bl_d, 0.0), bl_corner + vector(0.0, -bl + bl_d), bl_corner + vector(0.0, -bl), ]; if winding == Winding::Positive { builder.begin(points[0], attributes); if tl > 0.0 { builder.cubic_bezier_to(points[1], points[2], points[3], attributes); } builder.line_to(points[4], attributes); if tl > 0.0 { builder.cubic_bezier_to(points[5], points[6], points[7], attributes); } builder.line_to(points[8], attributes); if br > 0.0 { builder.cubic_bezier_to(points[9], points[10], points[11], attributes); } builder.line_to(points[12], attributes); if bl > 0.0 { builder.cubic_bezier_to(points[13], points[14], points[15], attributes); } } else { builder.begin(points[15], attributes); if bl > 0.0 { builder.cubic_bezier_to(points[14], points[13], points[12], attributes); } builder.line_to(points[11], attributes); if br > 0.0 { builder.cubic_bezier_to(points[10], points[9], points[8], attributes); } builder.line_to(points[7], attributes); if tl > 0.0 { builder.cubic_bezier_to(points[6], points[5], points[4], attributes); } builder.line_to(points[3], attributes); if tl > 0.0 { builder.cubic_bezier_to(points[2], points[1], points[0], attributes); } } builder.end(true); } #[inline] fn nan_check(p: Point) { debug_assert!(p.x.is_finite()); debug_assert!(p.y.is_finite()); } #[test] fn svg_builder_line_to_after_close() { use crate::Path; use crate::PathEvent; let mut p = Path::svg_builder(); p.line_to(point(1.0, 0.0)); p.close(); p.line_to(point(2.0, 0.0)); let path = p.build(); let mut it = path.iter(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(1.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(1.0, 0.0), first: point(1.0, 0.0), close: true }) ); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(1.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(1.0, 0.0), to: point(2.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(2.0, 0.0), first: point(1.0, 0.0), close: false }) ); assert_eq!(it.next(), None); } #[test] fn svg_builder_relative_curves() { use crate::Path; use crate::PathEvent; let mut p = Path::svg_builder(); p.move_to(point(0.0, 0.0)); p.relative_quadratic_bezier_to(vector(0., 100.), vector(-100., 100.)); p.relative_line_to(vector(-50., 0.)); let path = p.build(); let mut it = path.iter(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(0.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Quadratic { from: point(0.0, 0.0), ctrl: point(0.0, 100.0), to: point(-100., 100.), }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(-100.0, 100.0), to: point(-150., 100.) }) ); assert_eq!( it.next(), Some(PathEvent::End { first: point(0.0, 0.0), last: point(-150., 100.), close: false, }) ); assert_eq!(it.next(), None); } #[test] fn svg_builder_arc_to_update_position() { use crate::Path; let mut p = Path::svg_builder(); p.move_to(point(0.0, 0.0)); assert_eq!(p.current_position(), point(0.0, 0.0)); p.arc_to( vector(100., 100.), Angle::degrees(0.), ArcFlags::default(), point(0.0, 100.0), ); assert_ne!(p.current_position(), point(0.0, 0.0)); } #[test] fn issue_650() { let mut builder = crate::path::Path::builder().with_svg(); builder.arc( point(0.0, 0.0), vector(50.0, 50.0), Angle::radians(PI), Angle::radians(0.0), ); builder.build(); } #[test] fn straight_line_arc() { use crate::Path; let mut p = Path::svg_builder(); p.move_to(point(100.0, 0.0)); // Don't assert on a "false" arc that's a straight line p.arc_to( vector(100., 100.), Angle::degrees(0.), ArcFlags::default(), point(100.0, 0.0), ); } lyon_path-1.0.5/src/commands.rs000064400000000000000000001024251046102023000145660ustar 00000000000000//! A generic representation for paths that allow more control over how //! endpoints and control points are stored. //! //! # Motivation //! //! The default `Path` data structure in this crate is works well for the //! most common use cases. Sometimes, however, it is useful to be able to //! specify exactly how endpoints and control points are stored instead of //! relying on implicitly following the order of the events. //! //! This module contains bricks to help with building custom path representations. //! The central piece is the [`PathCommands`](struct.PathCommands.html) buffer and //! its [`PathCommandsBuilder`](struct.PathCommandsBuilder.html), providing a compact //! representation for path events with IDs instead of positions. //! //! # Examples //! //! The following example shows how `PathCommands` can be used together with an //! external buffers for positions to implement features similar to the default //! Path type with a different data structure. //! //! ``` //! use lyon_path::{EndpointId, Event, IdEvent, commands::PathCommands}; //! let points = &[ //! [0.0, 0.0], //! [1.0, 1.0], //! [0.0, 2.0], //! ]; //! //! let mut cmds = PathCommands::builder(); //! cmds.begin(EndpointId(0)); //! cmds.line_to(EndpointId(1)); //! cmds.line_to(EndpointId(2)); //! cmds.end(true); //! //! let cmds = cmds.build(); //! //! for event in &cmds { //! match event { //! IdEvent::Begin { at } => { println!("move to {:?}", points[at.to_usize()]); } //! IdEvent::Line { to, .. } => { println!("line to {:?}", points[to.to_usize()]); } //! IdEvent::End { close: true, .. } => { println!("close"); } //! _ => { panic!("unexpected event!") } //! } //! } //! //! // Iterate over the points directly using CommandsPathSlice //! for event in cmds.path_slice(points, points).events() { //! match event { //! Event::Begin { at } => { println!("move to {:?}", at); } //! Event::Line { to, .. } => { println!("line to {:?}", to); } //! Event::End { close: true, .. } => { println!("close"); } //! _ => { panic!("unexpected event!") } //! } //! } //! //! ``` use crate::events::{Event, IdEvent, PathEvent}; use crate::math::Point; use crate::{ControlPointId, EndpointId, EventId, Position, PositionStore}; use core::fmt; use crate::private::DebugValidator; use alloc::boxed::Box; use alloc::vec::Vec; // Note: Tried making the path generic over the integer type used to store // the commands to allow u16 and u32, but the performance difference is very // small and not worth the added complexity. mod verb { pub const LINE: u32 = 0; pub const QUADRATIC: u32 = 1; pub const CUBIC: u32 = 2; pub const BEGIN: u32 = 3; pub const CLOSE: u32 = 4; pub const END: u32 = 5; } /// Sadly this is very close to core::slice::Iter but reimplementing /// it manually to iterate over u32 makes a difference. /// It would seem that having next return u32 with a special value /// for the end of the iteration instead of Option should /// improve performance (simpler code and a bunch of unwraps removed), /// however a naive initial attempt led to worse performance. #[derive(Copy, Clone)] struct CmdIter<'l> { ptr: *const u32, end: *const u32, _marker: core::marker::PhantomData<&'l u32>, } impl<'l> CmdIter<'l> { fn new(slice: &'l [u32]) -> Self { let ptr = slice.as_ptr(); let end = unsafe { ptr.add(slice.len()) }; CmdIter { ptr, end, _marker: core::marker::PhantomData, } } #[inline] fn next(&mut self) -> Option { unsafe { if self.ptr == self.end { return None; } let val = *self.ptr; self.ptr = self.ptr.offset(1); Some(val) } } } /// The commands of a path encoded in a single array using IDs to refer /// to endpoints and control points externally. /// /// `PathCommands` is a good fit when the a custom endpoint and control point /// types are needed or when their the user needs full control over their storage. /// /// # Representation /// /// Path commands contains a single array of 32 bits integer values encoding path /// commands, endpoint IDs or control point IDs. /// /// ```ascii /// __________________________________________________________________________ /// | | | | | | | | /// | Begin |EndpointID| Line |EndpointID|Quadratic|ControlPointId|EndpointID| ... /// |_______|__________|______|__________|_________|______________|__________|_ /// /// ``` /// /// # Example /// /// ``` /// use lyon_path::{EndpointId, PathCommands}; /// /// let mut cmds = PathCommands::builder(); /// /// cmds.begin(EndpointId(0)); /// cmds.line_to(EndpointId(1)); /// cmds.line_to(EndpointId(2)); /// cmds.end(true); /// /// let cmds = cmds.build(); /// #[derive(Clone)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct PathCommands { cmds: Box<[u32]>, } impl PathCommands { /// Creates a [PathCommandsBuilder](struct.PathCommandsBuilder.html) to create path commands. pub fn builder() -> PathCommandsBuilder { PathCommandsBuilder::new() } /// Returns an iterator over the path commands. pub fn iter(&self) -> Iter { Iter::new(&self.cmds) } /// Returns a view on the path commands. pub fn as_slice(&self) -> PathCommandsSlice { PathCommandsSlice { cmds: &self.cmds } } /// Returns a view on a path made of these commands with endpoint and /// control point slices. pub fn path_slice<'l, Endpoint, ControlPoint>( &'l self, endpoints: &'l [Endpoint], control_points: &'l [ControlPoint], ) -> CommandsPathSlice { CommandsPathSlice { endpoints, control_points, cmds: self.as_slice(), } } /// Returns an iterator over the path, with endpoints and control points. pub fn events<'l, Endpoint, ControlPoint>( &'l self, endpoints: &'l [Endpoint], control_points: &'l [ControlPoint], ) -> Events { Events { cmds: CmdIter::new(&self.cmds), first_endpoint: 0, prev_endpoint: 0, endpoints, control_points, } } /// Returns the event for a given event ID. pub fn event(&self, id: EventId) -> IdEvent { self.as_slice().event(id) } /// Returns the next event id within the path. pub fn next_event_id_in_path(&self, id: EventId) -> Option { self.as_slice().next_event_id_in_path(id) } /// Returns the next event id within the sub-path. /// /// Loops back to the first event after the end of the sub-path. pub fn next_event_id_in_sub_path(&self, id: EventId) -> EventId { self.as_slice().next_event_id_in_sub_path(id) } } impl fmt::Debug for PathCommands { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_slice().fmt(f) } } impl<'l> IntoIterator for &'l PathCommands { type Item = IdEvent; type IntoIter = Iter<'l>; fn into_iter(self) -> Iter<'l> { self.iter() } } impl<'l> From<&'l PathCommands> for PathCommandsSlice<'l> { fn from(commands: &'l PathCommands) -> Self { commands.as_slice() } } /// A view over [`PathCommands`](struct.PathCommands.html). #[derive(Copy, Clone)] pub struct PathCommandsSlice<'l> { cmds: &'l [u32], } impl<'l> PathCommandsSlice<'l> { /// Returns an iterator over the path commands. pub fn iter(&self) -> Iter { Iter::new(self.cmds) } /// Returns the event for a given event ID. pub fn event(&self, id: EventId) -> IdEvent { let idx = id.to_usize(); match self.cmds[idx] { verb::LINE => IdEvent::Line { from: EndpointId(self.cmds[idx - 1]), to: EndpointId(self.cmds[idx + 1]), }, verb::QUADRATIC => IdEvent::Quadratic { from: EndpointId(self.cmds[idx - 1]), ctrl: ControlPointId(self.cmds[idx + 1]), to: EndpointId(self.cmds[idx + 2]), }, verb::CUBIC => IdEvent::Cubic { from: EndpointId(self.cmds[idx - 1]), ctrl1: ControlPointId(self.cmds[idx + 1]), ctrl2: ControlPointId(self.cmds[idx + 2]), to: EndpointId(self.cmds[idx + 3]), }, verb::BEGIN => IdEvent::Begin { at: EndpointId(self.cmds[idx + 1]), }, verb::END => { let first_event = self.cmds[idx + 1] as usize; IdEvent::End { last: EndpointId(self.cmds[idx - 1]), first: EndpointId(self.cmds[first_event + 1]), close: false, } } _ => { // CLOSE let first_event = self.cmds[idx + 1] as usize; IdEvent::End { last: EndpointId(self.cmds[idx - 1]), first: EndpointId(self.cmds[first_event + 1]), close: true, } } } } /// Returns the next event id within the path. pub fn next_event_id_in_sub_path(&self, id: EventId) -> EventId { let idx = id.to_usize(); match self.cmds[idx] { verb::LINE | verb::BEGIN => EventId(id.0 + 2), verb::QUADRATIC => EventId(id.0 + 3), verb::CUBIC => EventId(id.0 + 4), //verb::END | verb::CLOSE _ => EventId(self.cmds[idx + 1]), } } /// Returns the next event id within the path. pub fn next_event_id_in_path(&self, id: EventId) -> Option { let idx = id.to_usize(); let next = match self.cmds[idx] { verb::QUADRATIC => EventId(id.0 + 3), verb::CUBIC => EventId(id.0 + 4), // verb::LINE | verb::BEGIN | verb::END | verb::CLOSE _ => EventId(id.0 + 2), }; if next.0 < self.cmds.len() as u32 { return Some(next); } None } } impl<'l> fmt::Debug for PathCommandsSlice<'l> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\"")?; for evt in self.iter() { match evt { IdEvent::Line { to, .. } => write!(f, "L {to:?}"), IdEvent::Quadratic { ctrl, to, .. } => write!(f, "Q {ctrl:?} {to:?} "), IdEvent::Cubic { ctrl1, ctrl2, to, .. } => write!(f, "C {ctrl1:?} {ctrl2:?} {to:?} "), IdEvent::Begin { at, .. } => write!(f, "M {at:?} "), IdEvent::End { close: true, .. } => write!(f, "Z "), IdEvent::End { close: false, .. } => Ok(()), }?; } write!(f, "\"") } } /// A view on a [`PathCommands`](struct.PathCommands.html) buffer and /// two slices for endpoints and control points, providing similar /// functionalities as `PathSlice`. #[derive(Copy, Clone)] pub struct CommandsPathSlice<'l, Endpoint, ControlPoint> { endpoints: &'l [Endpoint], control_points: &'l [ControlPoint], cmds: PathCommandsSlice<'l>, } impl<'l, Endpoint, ControlPoint> CommandsPathSlice<'l, Endpoint, ControlPoint> { /// Returns an iterator over the events of the path using IDs. pub fn iter(&self) -> Iter { self.cmds.iter() } /// Returns an iterator over the events of the path using endpoint /// and control point references. pub fn events(&self) -> Events { Events { cmds: CmdIter::new(self.cmds.cmds), first_endpoint: 0, prev_endpoint: 0, endpoints: self.endpoints, control_points: self.control_points, } } } impl<'l, Endpoint, ControlPoint> core::ops::Index for CommandsPathSlice<'l, Endpoint, ControlPoint> { type Output = Endpoint; fn index(&self, id: EndpointId) -> &Endpoint { &self.endpoints[id.to_usize()] } } impl<'l, Endpoint, ControlPoint> core::ops::Index for CommandsPathSlice<'l, Endpoint, ControlPoint> { type Output = ControlPoint; fn index(&self, id: ControlPointId) -> &ControlPoint { &self.control_points[id.to_usize()] } } impl<'l, Endpoint, ControlPoint> fmt::Debug for CommandsPathSlice<'l, Endpoint, ControlPoint> where Endpoint: fmt::Debug, ControlPoint: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{ ")?; for evt in self.events() { match evt { Event::Line { to, .. } => write!(f, "L {to:?}"), Event::Quadratic { ctrl, to, .. } => write!(f, "Q {ctrl:?} {to:?} "), Event::Cubic { ctrl1, ctrl2, to, .. } => write!(f, "C {ctrl1:?} {ctrl2:?} {to:?} "), Event::Begin { at, .. } => write!(f, "M {at:?} "), Event::End { close: true, .. } => write!(f, "Z "), Event::End { close: false, .. } => Ok(()), }?; } write!(f, "}}") } } /// Builds path commands. /// /// See [`PathCommands`](struct.PathCommands.html). #[derive(Debug, Default, Clone)] pub struct PathCommandsBuilder { cmds: Vec, first_event_index: u32, validator: DebugValidator, } impl PathCommandsBuilder { /// Creates a builder without allocating memory. pub fn new() -> Self { Self::default() } /// Creates a pre-allocated builder. pub fn with_capacity(cap: usize) -> Self { Self { cmds: Vec::with_capacity(cap), ..Self::default() } } pub fn begin(&mut self, to: EndpointId) -> EventId { self.validator.begin(); self.first_event_index = self.cmds.len() as u32; let id = EventId(self.cmds.len() as u32); self.cmds.push(verb::BEGIN); self.cmds.push(to.0); id } pub fn end(&mut self, close: bool) -> Option { self.validator.end(); let id = EventId(self.cmds.len() as u32); let cmd = if close { verb::CLOSE } else { verb::END }; self.cmds.push(cmd); self.cmds.push(self.first_event_index); Some(id) } pub fn line_to(&mut self, to: EndpointId) -> EventId { self.validator.edge(); let id = EventId(self.cmds.len() as u32); self.cmds.push(verb::LINE); self.cmds.push(to.0); id } pub fn quadratic_bezier_to(&mut self, ctrl: ControlPointId, to: EndpointId) -> EventId { self.validator.edge(); let id = EventId(self.cmds.len() as u32); self.cmds.push(verb::QUADRATIC); self.cmds.push(ctrl.0); self.cmds.push(to.0); id } pub fn cubic_bezier_to( &mut self, ctrl1: ControlPointId, ctrl2: ControlPointId, to: EndpointId, ) -> EventId { self.validator.edge(); let id = EventId(self.cmds.len() as u32); self.cmds.push(verb::CUBIC); self.cmds.push(ctrl1.0); self.cmds.push(ctrl2.0); self.cmds.push(to.0); id } /// Consumes the builder and returns path commands. pub fn build(self) -> PathCommands { self.validator.build(); PathCommands { cmds: self.cmds.into_boxed_slice(), } } } /// An iterator of `Event<&Endpoint, &ControlPoint>`. #[derive(Clone)] pub struct Events<'l, Endpoint, ControlPoint> { cmds: CmdIter<'l>, prev_endpoint: usize, first_endpoint: usize, endpoints: &'l [Endpoint], control_points: &'l [ControlPoint], } impl<'l, Endpoint, ControlPoint> Iterator for Events<'l, Endpoint, ControlPoint> { type Item = Event<&'l Endpoint, &'l ControlPoint>; #[inline] fn next(&mut self) -> Option> { match self.cmds.next() { Some(verb::BEGIN) => { let to = self.cmds.next().unwrap() as usize; self.prev_endpoint = to; self.first_endpoint = to; Some(Event::Begin { at: &self.endpoints[to], }) } Some(verb::LINE) => { let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Line { from: &self.endpoints[from], to: &self.endpoints[to], }) } Some(verb::QUADRATIC) => { let ctrl = self.cmds.next().unwrap() as usize; let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Quadratic { from: &self.endpoints[from], ctrl: &self.control_points[ctrl], to: &self.endpoints[to], }) } Some(verb::CUBIC) => { let ctrl1 = self.cmds.next().unwrap() as usize; let ctrl2 = self.cmds.next().unwrap() as usize; let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Cubic { from: &self.endpoints[from], ctrl1: &self.control_points[ctrl1], ctrl2: &self.control_points[ctrl2], to: &self.endpoints[to], }) } Some(verb::END) => { let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; Some(Event::End { last: &self.endpoints[last], first: &self.endpoints[first], close: false, }) } Some(_) => { // CLOSE let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; Some(Event::End { last: &self.endpoints[last], first: &self.endpoints[first], close: true, }) } None => None, } } } impl<'l, Ep, Cp> Events<'l, Ep, Cp> where Ep: Position, Cp: Position, { pub fn points(self) -> PointEvents<'l, Ep, Cp> { PointEvents { cmds: self.cmds, prev_endpoint: self.prev_endpoint, first_endpoint: self.first_endpoint, endpoints: self.endpoints, control_points: self.control_points, } } } /// An iterator of `Event<&Endpoint, &ControlPoint>`. #[derive(Clone)] pub struct Iter<'l> { cmds: CmdIter<'l>, idx: u32, prev_endpoint: EndpointId, first_endpoint: EndpointId, } impl<'l> Iter<'l> { fn new(cmds: &'l [u32]) -> Self { Iter { cmds: CmdIter::new(cmds), idx: 0, prev_endpoint: EndpointId(0), first_endpoint: EndpointId(0), } } } impl<'l> Iterator for Iter<'l> { type Item = IdEvent; #[inline] fn next(&mut self) -> Option { match self.cmds.next() { Some(verb::BEGIN) => { let to = EndpointId(self.cmds.next().unwrap()); self.prev_endpoint = to; self.first_endpoint = to; self.idx += 2; Some(IdEvent::Begin { at: to }) } Some(verb::LINE) => { let to = EndpointId(self.cmds.next().unwrap()); let from = self.prev_endpoint; self.prev_endpoint = to; self.idx += 2; Some(IdEvent::Line { from, to }) } Some(verb::QUADRATIC) => { let ctrl = ControlPointId(self.cmds.next().unwrap()); let to = EndpointId(self.cmds.next().unwrap()); let from = self.prev_endpoint; self.prev_endpoint = to; self.idx += 3; Some(IdEvent::Quadratic { from, ctrl, to }) } Some(verb::CUBIC) => { let ctrl1 = ControlPointId(self.cmds.next().unwrap()); let ctrl2 = ControlPointId(self.cmds.next().unwrap()); let to = EndpointId(self.cmds.next().unwrap()); let from = self.prev_endpoint; self.prev_endpoint = to; self.idx += 4; Some(IdEvent::Cubic { from, ctrl1, ctrl2, to, }) } Some(verb::END) => { let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; self.idx += 2; Some(IdEvent::End { last, first, close: false, }) } Some(_) => { let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; self.idx += 2; Some(IdEvent::End { last, first, close: true, }) } None => None, } } } /// An iterator of `PathEvent`. #[derive(Clone)] pub struct PointEvents<'l, Endpoint, ControlPoint> { cmds: CmdIter<'l>, prev_endpoint: usize, first_endpoint: usize, endpoints: &'l [Endpoint], control_points: &'l [ControlPoint], } impl<'l, Endpoint, ControlPoint> Iterator for PointEvents<'l, Endpoint, ControlPoint> where Endpoint: Position, ControlPoint: Position, { type Item = PathEvent; #[inline] fn next(&mut self) -> Option { match self.cmds.next() { Some(verb::BEGIN) => { let to = self.cmds.next().unwrap() as usize; self.prev_endpoint = to; self.first_endpoint = to; Some(Event::Begin { at: self.endpoints[to].position(), }) } Some(verb::LINE) => { let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Line { from: self.endpoints[from].position(), to: self.endpoints[to].position(), }) } Some(verb::QUADRATIC) => { let ctrl = self.cmds.next().unwrap() as usize; let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Quadratic { from: self.endpoints[from].position(), ctrl: self.control_points[ctrl].position(), to: self.endpoints[to].position(), }) } Some(verb::CUBIC) => { let ctrl1 = self.cmds.next().unwrap() as usize; let ctrl2 = self.cmds.next().unwrap() as usize; let to = self.cmds.next().unwrap() as usize; let from = self.prev_endpoint; self.prev_endpoint = to; Some(Event::Cubic { from: self.endpoints[from].position(), ctrl1: self.control_points[ctrl1].position(), ctrl2: self.control_points[ctrl2].position(), to: self.endpoints[to].position(), }) } Some(verb::END) => { let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; Some(Event::End { last: self.endpoints[last].position(), first: self.endpoints[first].position(), close: false, }) } Some(_) => { let _first_index = self.cmds.next(); let last = self.prev_endpoint; let first = self.first_endpoint; self.prev_endpoint = first; Some(Event::End { last: self.endpoints[last].position(), first: self.endpoints[first].position(), close: true, }) } None => None, } } } impl<'l, Endpoint, ControlPoint> PositionStore for CommandsPathSlice<'l, Endpoint, ControlPoint> where Endpoint: Position, ControlPoint: Position, { fn get_endpoint(&self, id: EndpointId) -> Point { self[id].position() } fn get_control_point(&self, id: ControlPointId) -> Point { self[id].position() } } #[cfg(debug_assertions)] #[test] #[should_panic] fn missing_begin_1() { let mut builder = PathCommands::builder(); builder.line_to(EndpointId(1)); builder.end(true); builder.build(); } #[cfg(debug_assertions)] #[test] #[should_panic] fn missing_begin_2() { let mut builder = PathCommands::builder(); builder.begin(EndpointId(0)); builder.line_to(EndpointId(1)); builder.end(true); builder.line_to(EndpointId(1)); builder.end(true); builder.build(); } #[cfg(debug_assertions)] #[test] #[should_panic] fn missing_end() { let mut builder = PathCommands::builder(); builder.begin(EndpointId(0)); builder.line_to(EndpointId(1)); builder.build(); } #[test] fn simple_path() { let mut builder = PathCommands::builder(); builder.begin(EndpointId(0)); builder.line_to(EndpointId(1)); builder.quadratic_bezier_to(ControlPointId(2), EndpointId(3)); builder.cubic_bezier_to(ControlPointId(4), ControlPointId(5), EndpointId(6)); builder.end(false); builder.begin(EndpointId(10)); builder.line_to(EndpointId(11)); builder.quadratic_bezier_to(ControlPointId(12), EndpointId(13)); builder.cubic_bezier_to(ControlPointId(14), ControlPointId(15), EndpointId(16)); builder.end(true); builder.begin(EndpointId(20)); builder.line_to(EndpointId(21)); builder.quadratic_bezier_to(ControlPointId(22), EndpointId(23)); builder.cubic_bezier_to(ControlPointId(24), ControlPointId(25), EndpointId(26)); builder.end(false); let path = builder.build(); let mut iter = path.iter(); assert_eq!(iter.next(), Some(IdEvent::Begin { at: EndpointId(0) })); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(0), to: EndpointId(1) }) ); assert_eq!( iter.next(), Some(IdEvent::Quadratic { from: EndpointId(1), ctrl: ControlPointId(2), to: EndpointId(3) }) ); assert_eq!( iter.next(), Some(IdEvent::Cubic { from: EndpointId(3), ctrl1: ControlPointId(4), ctrl2: ControlPointId(5), to: EndpointId(6) }) ); assert_eq!( iter.next(), Some(IdEvent::End { last: EndpointId(6), first: EndpointId(0), close: false }) ); assert_eq!(iter.next(), Some(IdEvent::Begin { at: EndpointId(10) })); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(10), to: EndpointId(11) }) ); assert_eq!( iter.next(), Some(IdEvent::Quadratic { from: EndpointId(11), ctrl: ControlPointId(12), to: EndpointId(13) }) ); assert_eq!( iter.next(), Some(IdEvent::Cubic { from: EndpointId(13), ctrl1: ControlPointId(14), ctrl2: ControlPointId(15), to: EndpointId(16) }) ); assert_eq!( iter.next(), Some(IdEvent::End { last: EndpointId(16), first: EndpointId(10), close: true }) ); assert_eq!(iter.next(), Some(IdEvent::Begin { at: EndpointId(20) })); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(20), to: EndpointId(21) }) ); assert_eq!( iter.next(), Some(IdEvent::Quadratic { from: EndpointId(21), ctrl: ControlPointId(22), to: EndpointId(23) }) ); assert_eq!( iter.next(), Some(IdEvent::Cubic { from: EndpointId(23), ctrl1: ControlPointId(24), ctrl2: ControlPointId(25), to: EndpointId(26) }) ); assert_eq!( iter.next(), Some(IdEvent::End { last: EndpointId(26), first: EndpointId(20), close: false }) ); assert_eq!(iter.next(), None); } #[test] fn next_event() { let mut builder = PathCommands::builder(); builder.begin(EndpointId(0)); builder.line_to(EndpointId(1)); builder.quadratic_bezier_to(ControlPointId(2), EndpointId(3)); builder.cubic_bezier_to(ControlPointId(4), ControlPointId(5), EndpointId(6)); builder.end(false); builder.begin(EndpointId(10)); builder.line_to(EndpointId(11)); builder.quadratic_bezier_to(ControlPointId(12), EndpointId(13)); builder.cubic_bezier_to(ControlPointId(14), ControlPointId(15), EndpointId(16)); builder.end(true); builder.begin(EndpointId(20)); builder.line_to(EndpointId(21)); builder.quadratic_bezier_to(ControlPointId(22), EndpointId(23)); builder.cubic_bezier_to(ControlPointId(24), ControlPointId(25), EndpointId(26)); builder.end(false); let path = builder.build(); let mut id = EventId(0); let first = id; assert_eq!(path.event(id), IdEvent::Begin { at: EndpointId(0) }); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Line { from: EndpointId(0), to: EndpointId(1) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Quadratic { from: EndpointId(1), ctrl: ControlPointId(2), to: EndpointId(3) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Cubic { from: EndpointId(3), ctrl1: ControlPointId(4), ctrl2: ControlPointId(5), to: EndpointId(6) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::End { last: EndpointId(6), first: EndpointId(0), close: false } ); assert_eq!(path.next_event_id_in_sub_path(id), first); id = path.next_event_id_in_path(id).unwrap(); let first = id; assert_eq!(path.event(id), IdEvent::Begin { at: EndpointId(10) }); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Line { from: EndpointId(10), to: EndpointId(11) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Quadratic { from: EndpointId(11), ctrl: ControlPointId(12), to: EndpointId(13) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Cubic { from: EndpointId(13), ctrl1: ControlPointId(14), ctrl2: ControlPointId(15), to: EndpointId(16) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::End { last: EndpointId(16), first: EndpointId(10), close: true } ); assert_eq!(path.next_event_id_in_sub_path(id), first); id = path.next_event_id_in_path(id).unwrap(); let first = id; assert_eq!(path.event(id), IdEvent::Begin { at: EndpointId(20) }); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Line { from: EndpointId(20), to: EndpointId(21) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Quadratic { from: EndpointId(21), ctrl: ControlPointId(22), to: EndpointId(23) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::Cubic { from: EndpointId(23), ctrl1: ControlPointId(24), ctrl2: ControlPointId(25), to: EndpointId(26) } ); id = path.next_event_id_in_path(id).unwrap(); assert_eq!( path.event(id), IdEvent::End { last: EndpointId(26), first: EndpointId(20), close: false } ); assert_eq!(path.next_event_id_in_path(id), None); assert_eq!(path.next_event_id_in_sub_path(id), first); } lyon_path-1.0.5/src/events.rs000064400000000000000000000101221046102023000142610ustar 00000000000000use crate::geom::traits::Transformation; use crate::math::Point; use crate::{ControlPointId, EndpointId, Position}; /// Represents an event or edge of path. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum Event { Begin { at: Endpoint, }, Line { from: Endpoint, to: Endpoint, }, Quadratic { from: Endpoint, ctrl: ControlPoint, to: Endpoint, }, Cubic { from: Endpoint, ctrl1: ControlPoint, ctrl2: ControlPoint, to: Endpoint, }, End { last: Endpoint, first: Endpoint, close: bool, }, } /// A path event representing endpoints and control points as positions. pub type PathEvent = Event; /// A path event representing endpoints and control points as IDs. pub type IdEvent = Event; impl Event { pub fn is_edge(&self) -> bool { match self { &Event::Line { .. } | &Event::Quadratic { .. } | &Event::Cubic { .. } | &Event::End { close: true, .. } => true, _ => false, } } pub fn from(&self) -> Ep where Ep: Clone, { match &self { &Event::Line { from, .. } | &Event::Quadratic { from, .. } | &Event::Cubic { from, .. } | &Event::Begin { at: from } | &Event::End { last: from, .. } => from.clone(), } } pub fn to(&self) -> Ep where Ep: Clone, { match &self { &Event::Line { to, .. } | &Event::Quadratic { to, .. } | &Event::Cubic { to, .. } | &Event::Begin { at: to } | &Event::End { first: to, .. } => to.clone(), } } pub fn with_points(&self) -> PathEvent where Ep: Position, Cp: Position, { match self { Event::Line { from, to } => Event::Line { from: from.position(), to: to.position(), }, Event::Quadratic { from, ctrl, to } => Event::Quadratic { from: from.position(), ctrl: ctrl.position(), to: to.position(), }, Event::Cubic { from, ctrl1, ctrl2, to, } => Event::Cubic { from: from.position(), ctrl1: ctrl1.position(), ctrl2: ctrl2.position(), to: to.position(), }, Event::Begin { at } => Event::Begin { at: at.position() }, Event::End { last, first, close } => Event::End { last: last.position(), first: first.position(), close: *close, }, } } } impl PathEvent { pub fn transformed>(&self, mat: &T) -> Self { match self { Event::Line { from, to } => Event::Line { from: mat.transform_point(*from), to: mat.transform_point(*to), }, Event::Quadratic { from, ctrl, to } => Event::Quadratic { from: mat.transform_point(*from), ctrl: mat.transform_point(*ctrl), to: mat.transform_point(*to), }, Event::Cubic { from, ctrl1, ctrl2, to, } => Event::Cubic { from: mat.transform_point(*from), ctrl1: mat.transform_point(*ctrl1), ctrl2: mat.transform_point(*ctrl2), to: mat.transform_point(*to), }, Event::Begin { at } => Event::Begin { at: mat.transform_point(*at), }, Event::End { first, last, close } => Event::End { last: mat.transform_point(*last), first: mat.transform_point(*first), close: *close, }, } } } lyon_path-1.0.5/src/iterator.rs000064400000000000000000000277231046102023000146250ustar 00000000000000//! Tools to iterate over paths. //! //! # Lyon path iterators //! //! ## Overview //! //! This module provides a collection of traits to extend the `Iterator` trait when //! iterating over paths. //! //! ## Examples //! //! ``` //! use lyon_path::iterator::*; //! use lyon_path::math::{point, vector}; //! use lyon_path::{Path, PathEvent}; //! //! // Start with a path. //! let mut builder = Path::builder(); //! builder.begin(point(0.0, 0.0)); //! builder.line_to(point(10.0, 0.0)); //! builder.cubic_bezier_to(point(10.0, 10.0), point(0.0, 10.0), point(0.0, 5.0)); //! builder.end(true); //! let path = builder.build(); //! //! // A simple std::iter::Iterator, //! let simple_iter = path.iter(); //! //! // Make it an iterator over simpler primitives flattened events, //! // which do not contain any curve. To do so we approximate each curve //! // linear segments according to a tolerance threshold which controls //! // the tradeoff between fidelity of the approximation and amount of //! // generated events. Let's use a tolerance threshold of 0.01. //! // The beauty of this approach is that the flattening happens lazily //! // while iterating without allocating memory for the path. //! let flattened_iter = path.iter().flattened(0.01); //! //! for evt in flattened_iter { //! match evt { //! PathEvent::Begin { at } => { println!(" - move to {:?}", at); } //! PathEvent::Line { from, to } => { println!(" - line {:?} -> {:?}", from, to); } //! PathEvent::End { last, first, close } => { //! if close { //! println!(" - close {:?} -> {:?}", last, first); //! } else { //! println!(" - end"); //! } //! } //! _ => { panic!() } //! } //! } //! ``` //! //! Chaining the provided iterators allow performing some path manipulations lazily //! without allocating actual path objects to hold the result of the transformations. //! //! ``` //! extern crate lyon_path; //! use lyon_path::iterator::*; //! use lyon_path::math::{point, Angle, Rotation}; //! use lyon_path::Path; //! //! fn main() { //! // In practice it is more common to iterate over Path objects than vectors //! // of SVG commands (the former can be constructed from the latter). //! let mut builder = Path::builder(); //! builder.begin(point(1.0, 1.0)); //! builder.line_to(point(2.0, 1.0)); //! builder.quadratic_bezier_to(point(2.0, 2.0), point(1.0, 2.0)); //! builder.cubic_bezier_to(point(0.0, 2.0), point(0.0, 0.0), point(1.0, 0.0)); //! builder.end(true); //! let path = builder.build(); //! //! let transform = Rotation::new(Angle::radians(1.0)); //! //! for evt in path.iter().transformed(&transform).flattened(0.1) { //! // ... //! } //! } //! ``` use crate::geom::traits::Transformation; use crate::geom::{cubic_bezier, quadratic_bezier, CubicBezierSegment, QuadraticBezierSegment}; use crate::math::*; use crate::{Attributes, Event, PathEvent}; // TODO: It would be great to add support for attributes in PathIterator. /// An extension trait for `PathEvent` iterators. pub trait PathIterator: Iterator + Sized { /// Returns an iterator that turns curves into line segments. fn flattened(self, tolerance: f32) -> Flattened { Flattened::new(tolerance, self) } /// Returns an iterator applying a 2D transform to all of its events. fn transformed>(self, mat: &T) -> Transformed { Transformed::new(mat, self) } } impl PathIterator for Iter where Iter: Iterator {} pub struct NoAttributes(pub(crate) Iter); impl<'l, Iter> NoAttributes where Iter: Iterator), Point>>, { pub fn with_attributes(self) -> Iter { self.0 } } impl<'l, Iter> Iterator for NoAttributes where Iter: Iterator), Point>>, { type Item = PathEvent; fn next(&mut self) -> Option { self.0.next().map(|event| event.with_points()) } } /// An iterator that consumes `Event` iterator and yields flattened path events (with no curves). pub struct Flattened { it: Iter, current_position: Point, current_curve: TmpFlatteningIter, tolerance: f32, } enum TmpFlatteningIter { Quadratic(quadratic_bezier::Flattened), Cubic(cubic_bezier::Flattened), None, } impl> Flattened { /// Create the iterator. pub fn new(tolerance: f32, it: Iter) -> Self { Flattened { it, current_position: point(0.0, 0.0), current_curve: TmpFlatteningIter::None, tolerance, } } } impl Iterator for Flattened where Iter: Iterator, { type Item = PathEvent; fn next(&mut self) -> Option { match self.current_curve { TmpFlatteningIter::Quadratic(ref mut it) => { if let Some(to) = it.next() { let from = self.current_position; self.current_position = to; return Some(PathEvent::Line { from, to }); } } TmpFlatteningIter::Cubic(ref mut it) => { if let Some(to) = it.next() { let from = self.current_position; self.current_position = to; return Some(PathEvent::Line { from, to }); } } _ => {} } self.current_curve = TmpFlatteningIter::None; match self.it.next() { Some(PathEvent::Begin { at }) => Some(PathEvent::Begin { at }), Some(PathEvent::Line { from, to }) => Some(PathEvent::Line { from, to }), Some(PathEvent::End { last, first, close }) => { Some(PathEvent::End { last, first, close }) } Some(PathEvent::Quadratic { from, ctrl, to }) => { self.current_position = from; self.current_curve = TmpFlatteningIter::Quadratic( QuadraticBezierSegment { from, ctrl, to }.flattened(self.tolerance), ); self.next() } Some(PathEvent::Cubic { from, ctrl1, ctrl2, to, }) => { self.current_position = from; self.current_curve = TmpFlatteningIter::Cubic( CubicBezierSegment { from, ctrl1, ctrl2, to, } .flattened(self.tolerance), ); self.next() } None => None, } } fn size_hint(&self) -> (usize, Option) { // At minimum, the inner iterator size hint plus the flattening iterator size hint can form the lower // bracket. // We can't determine a maximum limit. let mut lo = self.it.size_hint().0; match &self.current_curve { TmpFlatteningIter::Quadratic(t) => { lo += t.size_hint().0; } TmpFlatteningIter::Cubic(t) => { lo += t.size_hint().0; } _ => {} } (lo, None) } } /// Applies a 2D transform to a path iterator and yields the resulting path iterator. pub struct Transformed<'l, I, T> { it: I, transform: &'l T, } impl<'l, I, T: Transformation> Transformed<'l, I, T> where I: Iterator, { /// Creates a new transformed path iterator from a path iterator. #[inline] pub fn new(transform: &'l T, it: I) -> Transformed<'l, I, T> { Transformed { it, transform } } } impl<'l, I, T> Iterator for Transformed<'l, I, T> where I: Iterator, T: Transformation, { type Item = PathEvent; fn next(&mut self) -> Option { match self.it.next() { None => None, Some(ref evt) => Some(evt.transformed(self.transform)), } } } /// An iterator that consumes an iterator of `Point`s and produces `Event`s. /// /// # Example /// /// ``` /// # extern crate lyon_path; /// # use lyon_path::iterator::FromPolyline; /// # use lyon_path::math::point; /// # fn main() { /// let points = [ /// point(1.0, 1.0), /// point(2.0, 1.0), /// point(1.0, 2.0) /// ]; /// let iter = FromPolyline::closed(points.iter().cloned()); /// # } /// ``` pub struct FromPolyline { iter: Iter, current: Point, first: Point, is_first: bool, done: bool, close: bool, } impl> FromPolyline { pub fn new(close: bool, iter: Iter) -> Self { FromPolyline { iter, current: point(0.0, 0.0), first: point(0.0, 0.0), is_first: true, done: false, close, } } pub fn closed(iter: Iter) -> Self { FromPolyline::new(true, iter) } pub fn open(iter: Iter) -> Self { FromPolyline::new(false, iter) } } impl Iterator for FromPolyline where Iter: Iterator, { type Item = PathEvent; fn next(&mut self) -> Option { if self.done { return None; } if let Some(next) = self.iter.next() { debug_assert!(next.x.is_finite()); debug_assert!(next.y.is_finite()); let from = self.current; self.current = next; return if self.is_first { self.is_first = false; self.first = next; Some(PathEvent::Begin { at: next }) } else { Some(PathEvent::Line { from, to: next }) }; } self.done = true; Some(PathEvent::End { last: self.current, first: self.first, close: self.close, }) } } #[test] fn test_from_polyline_open() { let points = &[ point(1.0, 1.0), point(3.0, 1.0), point(4.0, 5.0), point(5.0, 2.0), ]; let mut evts = FromPolyline::open(points.iter().cloned()); assert_eq!( evts.next(), Some(PathEvent::Begin { at: point(1.0, 1.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(1.0, 1.0), to: point(3.0, 1.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(3.0, 1.0), to: point(4.0, 5.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(4.0, 5.0), to: point(5.0, 2.0) }) ); assert_eq!( evts.next(), Some(PathEvent::End { last: point(5.0, 2.0), first: point(1.0, 1.0), close: false }) ); assert_eq!(evts.next(), None); } #[test] fn test_from_polyline_closed() { let points = &[ point(1.0, 1.0), point(3.0, 1.0), point(4.0, 5.0), point(5.0, 2.0), ]; let mut evts = FromPolyline::closed(points.iter().cloned()); assert_eq!( evts.next(), Some(PathEvent::Begin { at: point(1.0, 1.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(1.0, 1.0), to: point(3.0, 1.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(3.0, 1.0), to: point(4.0, 5.0) }) ); assert_eq!( evts.next(), Some(PathEvent::Line { from: point(4.0, 5.0), to: point(5.0, 2.0) }) ); assert_eq!( evts.next(), Some(PathEvent::End { last: point(5.0, 2.0), first: point(1.0, 1.0), close: true }) ); assert_eq!(evts.next(), None); } lyon_path-1.0.5/src/lib.rs000064400000000000000000000344371046102023000135420ustar 00000000000000#![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```. pub type Point = euclid::default::Point2D; /// Alias for ```euclid::default::Point2D```. pub type Vector = euclid::default::Vector2D; /// Alias for ```euclid::default::Size2D```. pub type Size = euclid::default::Size2D; /// Alias for ```euclid::default::Box2D``` pub type Box2D = euclid::default::Box2D; /// Alias for ```euclid::default::Transform2D``` pub type Transform = euclid::default::Transform2D; /// Alias for ```euclid::default::Rotation2D``` pub type Rotation = euclid::default::Rotation2D; /// Alias for ```euclid::default::Translation2D``` pub type Translation = euclid::Translation2D; /// Alias for ```euclid::default::Scale``` pub type Scale = euclid::default::Scale; /// An angle in radians (f32). pub type Angle = euclid::Angle; /// 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: /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// LineCap::Butt /// LineCap::Square /// LineCap::Round /// /// /// #[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: #[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 Position for crate::geom::euclid::Point2D { 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 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> = &[]; lyon_path-1.0.5/src/path.rs000064400000000000000000001741261046102023000137300ustar 00000000000000//! The default path data structure. //! use crate::builder::*; use crate::geom::traits::Transformation; use crate::geom::{CubicBezierSegment, QuadraticBezierSegment}; use crate::iterator::NoAttributes as IterNoAttributes; use crate::math::*; use crate::private::DebugValidator; use crate::{ AttributeStore, Attributes, ControlPointId, EndpointId, Event, IdEvent, PathEvent, PositionStore, NO_ATTRIBUTES, }; use core::fmt; use core::iter::{FromIterator, IntoIterator}; use core::u32; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; /// Enumeration corresponding to the [Event](https://docs.rs/lyon_core/*/lyon_core/events/enum.Event.html) enum /// without the parameters. /// /// This is used by the [Path](struct.Path.html) data structure to store path events a tad /// more efficiently. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub(crate) enum Verb { LineTo, QuadraticTo, CubicTo, Begin, Close, End, } /// A simple path data structure. /// /// # Custom attributes /// /// Paths can store a fixed number of extra `f32` values per endpoint, called /// "custom attributes" or "interpolated attributes" through the documentation. /// These can be handy to represent arbitrary attributes such as variable colors, /// line width, etc. /// /// See also: /// - [`BuilderWithAttributes`](struct.BuilderWithAttributes.html). /// - [`Path::builder_with_attributes`](struct.Path.html#method.builder_with_attributes). /// - [`Path::attributes`](struct.Path.html#method.attributes). /// /// # Representation /// /// Paths contain two buffers: /// - a buffer of commands (Begin, Line, Quadratic, Cubic, Close or End), /// - and a buffer of pairs of floats that can be endpoints control points or custom attributes. /// /// The order of storage for points is determined by the sequence of commands. /// Custom attributes (if any) always directly follow endpoints. If there is an odd number /// of attributes, the last float of the each attribute sequence is set to zero and is not used. /// /// ```ascii /// __________________________ /// | | | | /// | Begin | Line |Quadratic| ... /// |_______|______|_________|_ /// __________________________________________________________________________ /// | | | | | | | | /// |start x,y|attributes| to x, y |attributes|ctrl x,y | to x, y |attributes| ... /// |_________|__________|_________|__________|_________|_________|__________|_ /// ``` /// #[derive(Clone, Default)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Path { points: Box<[Point]>, verbs: Box<[Verb]>, num_attributes: usize, } /// A view on a `Path`. #[derive(Copy, Clone)] pub struct PathSlice<'l> { pub(crate) points: &'l [Point], pub(crate) verbs: &'l [Verb], pub(crate) num_attributes: usize, } pub type Builder = NoAttributes; impl Path { /// Creates a [Builder](struct.Builder.html) to build a path. pub fn builder() -> Builder { NoAttributes::wrap(BuilderImpl::new()) } /// Creates a [BuilderWithAttributes](struct.BuilderWithAttributes.html) to build a path /// with custom attributes. pub fn builder_with_attributes(num_attributes: usize) -> BuilderWithAttributes { BuilderWithAttributes::new(num_attributes) } /// Creates an [WithSvg](../builder/struct.WithSvg.html) to build a path /// with a rich set of commands. pub fn svg_builder() -> WithSvg { WithSvg::new(BuilderImpl::new()) } /// Creates an Empty `Path`. #[inline] pub fn new() -> Path { Path { points: Box::new([]), verbs: Box::new([]), num_attributes: 0, } } #[inline] pub fn with_attributes(num_attributes: usize) -> Path { Path { points: Box::new([]), verbs: Box::new([]), num_attributes, } } /// Returns a view on this `Path`. #[inline] pub fn as_slice(&self) -> PathSlice { PathSlice { points: &self.points[..], verbs: &self.verbs[..], num_attributes: self.num_attributes, } } /// Returns a slice over an endpoint's custom attributes. #[inline] pub fn attributes(&self, endpoint: EndpointId) -> Attributes { interpolated_attributes(self.num_attributes, &self.points, endpoint) } /// Iterates over the entire `Path`, ignoring custom attributes. pub fn iter(&self) -> Iter { Iter::new(self.num_attributes, &self.points[..], &self.verbs[..]) } /// Iterates over the endpoint and control point ids of the `Path`. pub fn id_iter(&self) -> IdIter { IdIter::new(self.num_attributes, &self.verbs[..]) } /// Iterates over the entire `Path` with custom attributes. pub fn iter_with_attributes(&self) -> IterWithAttributes { IterWithAttributes::new(self.num_attributes(), &self.points[..], &self.verbs[..]) } /// Applies a transform to all endpoints and control points of this path and /// Returns the result. pub fn transformed>(mut self, transform: &T) -> Self { self.apply_transform(transform); self } /// Returns a reversed version of this path in the form of an iterator pub fn reversed(&self) -> IterNoAttributes { IterNoAttributes(Reversed::new(self.as_slice())) } /// Returns the first endpoint and its custom attributes if any. #[inline] pub fn first_endpoint(&self) -> Option<(Point, Attributes)> { self.as_slice().first_endpoint() } /// Returns the last endpoint and its custom attributes if any. #[inline] pub fn last_endpoint(&self) -> Option<(Point, Attributes)> { self.as_slice().last_endpoint() } fn apply_transform>(&mut self, transform: &T) { let iter = IdIter::new(self.num_attributes, &self.verbs[..]); for evt in iter { match evt { IdEvent::Begin { at } => { self.points[at.to_usize()] = transform.transform_point(self.points[at.to_usize()]); } IdEvent::Line { to, .. } => { self.points[to.to_usize()] = transform.transform_point(self.points[to.to_usize()]); } IdEvent::Quadratic { ctrl, to, .. } => { self.points[ctrl.to_usize()] = transform.transform_point(self.points[ctrl.to_usize()]); self.points[to.to_usize()] = transform.transform_point(self.points[to.to_usize()]); } IdEvent::Cubic { ctrl1, ctrl2, to, .. } => { self.points[ctrl1.to_usize()] = transform.transform_point(self.points[ctrl1.to_usize()]); self.points[ctrl2.to_usize()] = transform.transform_point(self.points[ctrl2.to_usize()]); self.points[to.to_usize()] = transform.transform_point(self.points[to.to_usize()]); } IdEvent::End { .. } => {} } } } } impl FromIterator for Path { fn from_iter>(iter: T) -> Path { let mut builder = Path::builder(); for event in iter.into_iter() { builder.path_event(event); } builder.build() } } impl core::ops::Index for Path { type Output = Point; fn index(&self, id: EndpointId) -> &Point { &self.points[id.to_usize()] } } impl core::ops::Index for Path { type Output = Point; fn index(&self, id: ControlPointId) -> &Point { &self.points[id.to_usize()] } } impl<'l> IntoIterator for &'l Path { type Item = PathEvent; type IntoIter = Iter<'l>; fn into_iter(self) -> Iter<'l> { self.iter() } } impl<'l> From<&'l Path> for PathSlice<'l> { fn from(path: &'l Path) -> Self { path.as_slice() } } impl PositionStore for Path { fn get_endpoint(&self, id: EndpointId) -> Point { self.points[id.to_usize()] } fn get_control_point(&self, id: ControlPointId) -> Point { self.points[id.to_usize()] } } impl AttributeStore for Path { fn get(&self, id: EndpointId) -> Attributes { interpolated_attributes(self.num_attributes, &self.points, id) } fn num_attributes(&self) -> usize { self.num_attributes } } impl fmt::Debug for Path { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.as_slice().fmt(formatter) } } /// An immutable view over a Path. impl<'l> PathSlice<'l> { pub fn first_endpoint(&self) -> Option<(Point, Attributes<'l>)> { if self.points.is_empty() { return None; } let pos = self.points[0]; let attributes = interpolated_attributes(self.num_attributes, self.points, EndpointId(0)); Some((pos, attributes)) } pub fn last_endpoint(&self) -> Option<(Point, Attributes<'l>)> { if self.points.is_empty() { return None; } let attrib_stride = (self.num_attributes() + 1) / 2; let offset = self.points.len() - attrib_stride - 1; let pos = self.points[offset]; let attributes = interpolated_attributes(self.num_attributes, self.points, EndpointId(offset as u32)); Some((pos, attributes)) } /// Iterates over the path. pub fn iter<'a>(&'a self) -> Iter<'l> { Iter::new(self.num_attributes, self.points, self.verbs) } /// Iterates over the endpoint and control point ids of the `Path`. pub fn id_iter(&self) -> IdIter { IdIter::new(self.num_attributes, self.verbs) } /// Iterates over the entire `Path` with custom attributes. pub fn iter_with_attributes(&self) -> IterWithAttributes { IterWithAttributes::new(self.num_attributes(), self.points, self.verbs) } pub fn is_empty(&self) -> bool { self.verbs.is_empty() } /// Returns a slice over an endpoint's custom attributes. #[inline] pub fn attributes(&self, endpoint: EndpointId) -> Attributes<'l> { interpolated_attributes(self.num_attributes, self.points, endpoint) } /// Returns a reversed version of this path in the form of an iterator pub fn reversed(&self) -> IterNoAttributes { IterNoAttributes(Reversed::new(*self)) } } impl<'l> fmt::Debug for PathSlice<'l> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn write_point(formatter: &mut fmt::Formatter, point: Point) -> fmt::Result { write!(formatter, " ")?; fmt::Debug::fmt(&point.x, formatter)?; write!(formatter, " ")?; fmt::Debug::fmt(&point.y, formatter) } fn write_attributes(formatter: &mut fmt::Formatter, attributes: Attributes) -> fmt::Result { for val in attributes { write!(formatter, " ")?; fmt::Debug::fmt(val, formatter)?; } Ok(()) } write!(formatter, "\"")?; for evt in self.iter_with_attributes() { match evt { Event::Begin { at: (at, attributes), } => { write!(formatter, " M")?; write_point(formatter, at)?; write_attributes(formatter, attributes)?; } Event::End { close, .. } => { if close { write!(formatter, " Z")?; } } Event::Line { to: (to, attributes), .. } => { write!(formatter, " L")?; write_point(formatter, to)?; write_attributes(formatter, attributes)?; } Event::Quadratic { ctrl, to: (to, attributes), .. } => { write!(formatter, " Q")?; write_point(formatter, ctrl)?; write_point(formatter, to)?; write_attributes(formatter, attributes)?; } Event::Cubic { ctrl1, ctrl2, to: (to, attributes), .. } => { write!(formatter, " C")?; write_point(formatter, ctrl1)?; write_point(formatter, ctrl2)?; write_point(formatter, to)?; write_attributes(formatter, attributes)?; } } } write!(formatter, "\"") } } impl<'l> core::ops::Index for PathSlice<'l> { type Output = Point; fn index(&self, id: EndpointId) -> &Point { &self.points[id.to_usize()] } } impl<'l> core::ops::Index for PathSlice<'l> { type Output = Point; fn index(&self, id: ControlPointId) -> &Point { &self.points[id.to_usize()] } } impl<'l> IntoIterator for PathSlice<'l> { type Item = PathEvent; type IntoIter = Iter<'l>; fn into_iter(self) -> Iter<'l> { self.iter() } } impl<'l, 'a> IntoIterator for &'a PathSlice<'l> { type Item = PathEvent; type IntoIter = Iter<'l>; fn into_iter(self) -> Iter<'l> { self.iter() } } impl<'l> PositionStore for PathSlice<'l> { fn get_endpoint(&self, id: EndpointId) -> Point { self.points[id.to_usize()] } fn get_control_point(&self, id: ControlPointId) -> Point { self.points[id.to_usize()] } } impl<'l> AttributeStore for PathSlice<'l> { fn get(&self, id: EndpointId) -> Attributes<'l> { interpolated_attributes(self.num_attributes, self.points, id) } fn num_attributes(&self) -> usize { self.num_attributes } } // TODO: measure the overhead of building no attributes and // see if BuilderImpl and BuilderWithAttributes can be merged. /// The default builder for `Path`. #[derive(Clone)] pub struct BuilderImpl { pub(crate) points: Vec, pub(crate) verbs: Vec, first: Point, validator: DebugValidator, } impl BuilderImpl { pub fn new() -> Self { BuilderImpl { points: Vec::new(), verbs: Vec::new(), first: point(0.0, 0.0), validator: DebugValidator::new(), } } pub fn with_capacity(points: usize, edges: usize) -> Self { BuilderImpl { points: Vec::with_capacity(points), verbs: Vec::with_capacity(edges), first: point(0.0, 0.0), validator: DebugValidator::new(), } } pub fn with_svg(self) -> WithSvg { assert!(self.verbs.is_empty()); WithSvg::new(self) } #[inline] pub fn extend_from_paths(&mut self, paths: &[PathSlice]) { concatenate_paths(&mut self.points, &mut self.verbs, paths, 0); } } impl NoAttributes { #[inline] pub fn extend_from_paths(&mut self, paths: &[PathSlice]) { concatenate_paths(&mut self.inner.points, &mut self.inner.verbs, paths, 0); } } impl PathBuilder for BuilderImpl { fn num_attributes(&self) -> usize { 0 } fn begin(&mut self, at: Point, _attributes: Attributes) -> EndpointId { self.validator.begin(); nan_check(at); let id = EndpointId(self.points.len() as u32); self.first = at; self.points.push(at); self.verbs.push(Verb::Begin); id } fn end(&mut self, close: bool) { self.validator.end(); if close { self.points.push(self.first); } self.verbs.push(if close { Verb::Close } else { Verb::End }); } fn line_to(&mut self, to: Point, _attributes: Attributes) -> EndpointId { self.validator.edge(); nan_check(to); let id = EndpointId(self.points.len() as u32); self.points.push(to); self.verbs.push(Verb::LineTo); id } fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.validator.edge(); nan_check(ctrl); nan_check(to); self.points.push(ctrl); let id = EndpointId(self.points.len() as u32); self.points.push(to); self.verbs.push(Verb::QuadraticTo); id } fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.validator.edge(); nan_check(ctrl1); nan_check(ctrl2); nan_check(to); self.points.push(ctrl1); self.points.push(ctrl2); let id = EndpointId(self.points.len() as u32); self.points.push(to); self.verbs.push(Verb::CubicTo); id } fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.points.reserve(endpoints + ctrl_points); self.verbs.reserve(endpoints); } } impl Build for BuilderImpl { type PathType = Path; fn build(self) -> Path { self.validator.build(); Path { points: self.points.into_boxed_slice(), verbs: self.verbs.into_boxed_slice(), num_attributes: 0, } } } impl Default for BuilderImpl { fn default() -> Self { BuilderImpl::new() } } /// A builder for `Path` with custom attributes. /// /// Custom attributes are a fixed number of `f32` values associated with each endpoint. /// All endpoints must have the same number of custom attributes, #[derive(Clone)] pub struct BuilderWithAttributes { pub(crate) builder: BuilderImpl, pub(crate) num_attributes: usize, pub(crate) first_attributes: Vec, } impl BuilderWithAttributes { pub fn new(num_attributes: usize) -> Self { BuilderWithAttributes { builder: BuilderImpl::new(), num_attributes, first_attributes: vec![0.0; num_attributes], } } #[inline] pub fn extend_from_paths(&mut self, paths: &[PathSlice]) { concatenate_paths( &mut self.builder.points, &mut self.builder.verbs, paths, self.num_attributes, ); } #[inline(always)] fn push_attributes_impl( points: &mut Vec, num_attributes: usize, attributes: Attributes, ) { assert_eq!(attributes.len(), num_attributes); for i in 0..(num_attributes / 2) { let x = attributes[i * 2]; let y = attributes[i * 2 + 1]; points.push(point(x, y)); } if num_attributes % 2 == 1 { let x = attributes[num_attributes - 1]; points.push(point(x, 0.0)); } } fn push_attributes(&mut self, attributes: Attributes) { Self::push_attributes_impl(&mut self.builder.points, self.num_attributes, attributes); } #[inline] pub fn num_attributes(&self) -> usize { self.num_attributes } #[inline] pub fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { let id = self.builder.begin(at, attributes); self.push_attributes(attributes); self.first_attributes.copy_from_slice(attributes); id } #[inline] pub fn end(&mut self, close: bool) { self.builder.end(close); if close { Self::push_attributes_impl( &mut self.builder.points, self.num_attributes, &self.first_attributes, ); } } #[inline] pub fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { let id = self.builder.line_to(to, attributes); self.push_attributes(attributes); id } #[inline] pub fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = self.builder.quadratic_bezier_to(ctrl, to, attributes); self.push_attributes(attributes); id } #[inline] pub fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = self.builder.cubic_bezier_to(ctrl1, ctrl2, to, attributes); self.push_attributes(attributes); id } #[inline] pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { let attr = self.num_attributes / 2 + self.num_attributes % 2; let n_points = endpoints * (1 + attr) + ctrl_points; self.builder.points.reserve(n_points); self.builder.verbs.reserve(endpoints); } #[inline] pub fn build(self) -> Path { self.builder.validator.build(); Path { points: self.builder.points.into_boxed_slice(), verbs: self.builder.verbs.into_boxed_slice(), num_attributes: self.num_attributes, } } } impl PathBuilder for BuilderWithAttributes { #[inline] fn num_attributes(&self) -> usize { self.num_attributes() } #[inline] fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { self.begin(at, attributes) } #[inline] fn end(&mut self, close: bool) { self.end(close); } #[inline] fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { self.line_to(to, attributes) } #[inline] fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.quadratic_bezier_to(ctrl, to, attributes) } #[inline] fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.cubic_bezier_to(ctrl1, ctrl2, to, attributes) } #[inline] fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.reserve(endpoints, ctrl_points) } } impl Build for BuilderWithAttributes { type PathType = Path; fn build(self) -> Path { self.build() } } #[inline] fn nan_check(p: Point) { debug_assert!(p.x.is_finite()); debug_assert!(p.y.is_finite()); } /// An iterator for `Path` and `PathSlice`. #[derive(Clone)] pub struct Iter<'l> { points: PointIter<'l>, verbs: ::core::slice::Iter<'l, Verb>, current: Point, first: Point, // Number of slots in the points array occupied by the custom attributes. attrib_stride: usize, } impl<'l> Iter<'l> { fn new(num_attributes: usize, points: &'l [Point], verbs: &'l [Verb]) -> Self { Iter { points: PointIter::new(points), verbs: verbs.iter(), current: point(0.0, 0.0), first: point(0.0, 0.0), attrib_stride: (num_attributes + 1) / 2, } } #[inline] fn skip_attributes(&mut self) { self.points.advance_n(self.attrib_stride); } } impl<'l> Iterator for Iter<'l> { type Item = PathEvent; #[inline] fn next(&mut self) -> Option { match self.verbs.next() { Some(&Verb::Begin) => { self.current = self.points.next(); self.skip_attributes(); self.first = self.current; Some(PathEvent::Begin { at: self.current }) } Some(&Verb::LineTo) => { let from = self.current; self.current = self.points.next(); self.skip_attributes(); Some(PathEvent::Line { from, to: self.current, }) } Some(&Verb::QuadraticTo) => { let from = self.current; let ctrl = self.points.next(); self.current = self.points.next(); self.skip_attributes(); Some(PathEvent::Quadratic { from, ctrl, to: self.current, }) } Some(&Verb::CubicTo) => { let from = self.current; let ctrl1 = self.points.next(); let ctrl2 = self.points.next(); self.current = self.points.next(); self.skip_attributes(); Some(PathEvent::Cubic { from, ctrl1, ctrl2, to: self.current, }) } Some(&Verb::Close) => { let last = self.current; let _ = self.points.next(); self.skip_attributes(); Some(PathEvent::End { last, first: self.first, close: true, }) } Some(&Verb::End) => { let last = self.current; self.current = self.first; Some(PathEvent::End { last, first: self.first, close: false, }) } None => None, } } } /// Manually implemented to avoid iterator overhead when skipping over /// several points where the custom attributes are stored. /// /// It makes an unfortunately large difference (the simple iterator /// benchmarks are 2 to 3 times faster). #[derive(Copy, Clone)] struct PointIter<'l> { ptr: *const Point, end: *const Point, _marker: core::marker::PhantomData<&'l Point>, } impl<'l> PointIter<'l> { fn new(slice: &'l [Point]) -> Self { let ptr = slice.as_ptr(); let end = unsafe { ptr.add(slice.len()) }; PointIter { ptr, end, _marker: core::marker::PhantomData, } } #[inline] fn remaining_len(&self) -> usize { (self.end as usize - self.ptr as usize) / core::mem::size_of::() } #[inline] fn next(&mut self) -> Point { // Don't bother panicking here. calls to next // are always followed by advance_n which will // catch the issue and panic. if self.ptr >= self.end { return point(f32::NAN, f32::NAN); } unsafe { let output = *self.ptr; self.ptr = self.ptr.offset(1); output } } #[inline] fn advance_n(&mut self, n: usize) { unsafe { assert!(self.remaining_len() >= n); self.ptr = self.ptr.add(n); } } } /// An iterator for `Path` and `PathSlice`. #[derive(Clone)] pub struct IterWithAttributes<'l> { points: PointIter<'l>, verbs: ::core::slice::Iter<'l, Verb>, current: (Point, Attributes<'l>), first: (Point, Attributes<'l>), num_attributes: usize, attrib_stride: usize, } impl<'l> IterWithAttributes<'l> { fn new(num_attributes: usize, points: &'l [Point], verbs: &'l [Verb]) -> Self { IterWithAttributes { points: PointIter::new(points), verbs: verbs.iter(), current: (point(0.0, 0.0), NO_ATTRIBUTES), first: (point(0.0, 0.0), NO_ATTRIBUTES), num_attributes, attrib_stride: (num_attributes + 1) / 2, } } pub fn points(self) -> Iter<'l> { Iter { points: self.points, verbs: self.verbs, current: self.current.0, first: self.first.0, attrib_stride: self.attrib_stride, } } /// Iterate on a flattened approximation of the path with interpolated custom attributes /// using callbacks. /// /// At the time of writing, it is impossible to implement this efficiently /// with the `Iterator` trait, because of the need to express some lifetime /// constraints in an associated type, see #701. pub fn for_each_flattened(self, tolerance: f32, callback: &mut F) where F: FnMut(&Event<(Point, Attributes), Point>), { let num_attributes = self.num_attributes; // Some scratch space for writing the interpolated custom attributes. let mut stack_buffer = [0.0; 16]; let mut vec_buffer; // No need to allocate memory if the number of custom attributes is small, // which is likely the common case. let buffer = if num_attributes <= 8 { &mut stack_buffer[..] } else { vec_buffer = vec![0.0; num_attributes * 2]; &mut vec_buffer[..] }; for evt in self { match evt { Event::Begin { at } => { callback(&Event::Begin { at }); } Event::End { last, first, close } => { callback(&Event::End { last, first, close }); } Event::Line { from, to } => { callback(&Event::Line { from, to }); } Event::Quadratic { from, ctrl, to } => { let from_attr = from.1; let to_attr = to.1; let curve = QuadraticBezierSegment { from: from.0, ctrl, to: to.0, }; let mut offset = num_attributes; buffer[0..num_attributes].copy_from_slice(from_attr); curve.for_each_flattened_with_t(tolerance, &mut |line, t| { for i in 0..num_attributes { buffer[offset + i] = (1.0 - t.end) * from_attr[i] + t.end * to_attr[i]; } let next_offset = if offset == 0 { num_attributes } else { 0 }; callback(&Event::Line { from: ( line.from, &buffer[next_offset..(next_offset + num_attributes)], ), to: (line.to, &buffer[offset..(offset + num_attributes)]), }); offset = next_offset; }); } Event::Cubic { from, ctrl1, ctrl2, to, } => { let from_attr = from.1; let to_attr = to.1; let curve = CubicBezierSegment { from: from.0, ctrl1, ctrl2, to: to.0, }; let mut offset = num_attributes; buffer[0..num_attributes].copy_from_slice(from_attr); curve.for_each_flattened_with_t(tolerance, &mut |line, t| { for i in 0..num_attributes { buffer[offset + i] = (1.0 - t.end) * from_attr[i] + t.end * to_attr[i]; } let next_offset = if offset == 0 { num_attributes } else { 0 }; callback(&Event::Line { from: ( line.from, &buffer[next_offset..(next_offset + num_attributes)], ), to: (line.to, &buffer[offset..(offset + num_attributes)]), }); offset = next_offset; }); } } } } #[inline] fn pop_endpoint(&mut self) -> (Point, Attributes<'l>) { let position = self.points.next(); let attributes_ptr = self.points.ptr as *const f32; self.points.advance_n(self.attrib_stride); let attributes = unsafe { // SAFETY: advance_n would have panicked if the slice is out of bounds core::slice::from_raw_parts(attributes_ptr, self.num_attributes) }; (position, attributes) } } impl<'l> Iterator for IterWithAttributes<'l> { type Item = Event<(Point, Attributes<'l>), Point>; #[inline] fn next(&mut self) -> Option { match self.verbs.next() { Some(&Verb::Begin) => { self.current = self.pop_endpoint(); self.first = self.current; Some(Event::Begin { at: self.current }) } Some(&Verb::LineTo) => { let from = self.current; self.current = self.pop_endpoint(); Some(Event::Line { from, to: self.current, }) } Some(&Verb::QuadraticTo) => { let from = self.current; let ctrl = self.points.next(); self.current = self.pop_endpoint(); Some(Event::Quadratic { from, ctrl, to: self.current, }) } Some(&Verb::CubicTo) => { let from = self.current; let ctrl1 = self.points.next(); let ctrl2 = self.points.next(); self.current = self.pop_endpoint(); Some(Event::Cubic { from, ctrl1, ctrl2, to: self.current, }) } Some(&Verb::Close) => { let last = self.current; self.current = self.pop_endpoint(); Some(Event::End { last, first: self.first, close: true, }) } Some(&Verb::End) => { let last = self.current; self.current = self.first; Some(Event::End { last, first: self.first, close: false, }) } None => None, } } } /// An iterator of endpoint and control point ids for `Path` and `PathSlice`. #[derive(Clone, Debug)] pub struct IdIter<'l> { verbs: ::core::slice::Iter<'l, Verb>, current: u32, first: u32, evt: u32, endpoint_stride: u32, } impl<'l> IdIter<'l> { fn new(num_attributes: usize, verbs: &'l [Verb]) -> Self { IdIter { verbs: verbs.iter(), current: 0, first: 0, evt: 0, endpoint_stride: (num_attributes as u32 + 1) / 2 + 1, } } } impl<'l> Iterator for IdIter<'l> { type Item = IdEvent; #[inline] fn next(&mut self) -> Option { match self.verbs.next() { Some(&Verb::Begin) => { let at = self.current; self.first = at; Some(IdEvent::Begin { at: EndpointId(at) }) } Some(&Verb::LineTo) => { let from = EndpointId(self.current); self.current += self.endpoint_stride; let to = EndpointId(self.current); self.evt += 1; Some(IdEvent::Line { from, to }) } Some(&Verb::QuadraticTo) => { let from = EndpointId(self.current); let base = self.current + self.endpoint_stride; let ctrl = ControlPointId(base); let to = EndpointId(base + 1); self.current = base + 1; self.evt += 1; Some(IdEvent::Quadratic { from, ctrl, to }) } Some(&Verb::CubicTo) => { let from = EndpointId(self.current); let base = self.current + self.endpoint_stride; let ctrl1 = ControlPointId(base); let ctrl2 = ControlPointId(base + 1); let to = EndpointId(base + 2); self.current = base + 2; self.evt += 1; Some(IdEvent::Cubic { from, ctrl1, ctrl2, to, }) } Some(&Verb::Close) => { let last = EndpointId(self.current); let first = EndpointId(self.first); self.current += self.endpoint_stride * 2; self.evt += 1; Some(IdEvent::End { last, first, close: true, }) } Some(&Verb::End) => { let last = EndpointId(self.current); let first = EndpointId(self.first); self.current += self.endpoint_stride; self.evt += 1; Some(IdEvent::End { last, first, close: false, }) } None => None, } } } #[inline] fn interpolated_attributes( num_attributes: usize, points: &[Point], endpoint: EndpointId, ) -> Attributes { if num_attributes == 0 { return &[]; } let idx = endpoint.0 as usize + 1; assert!(idx + (num_attributes + 1) / 2 <= points.len()); unsafe { let ptr = &points[idx].x as *const f32; core::slice::from_raw_parts(ptr, num_attributes) } } fn concatenate_paths( points: &mut Vec, verbs: &mut Vec, paths: &[PathSlice], num_attributes: usize, ) { let mut np = 0; let mut nv = 0; for path in paths { assert_eq!(path.num_attributes(), num_attributes); np += path.points.len(); nv += path.verbs.len(); } verbs.reserve(nv); points.reserve(np); for path in paths { verbs.extend_from_slice(path.verbs); points.extend_from_slice(path.points); } } /// An iterator of over a `Path` traversing the path in reverse. pub struct Reversed<'l> { verbs: core::iter::Rev>, path: PathSlice<'l>, num_attributes: usize, attrib_stride: usize, p: usize, need_close: bool, first: Option<(Point, Attributes<'l>)>, } impl<'l> Reversed<'l> { fn new(path: PathSlice<'l>) -> Self { Reversed { verbs: path.verbs.iter().rev(), num_attributes: path.num_attributes(), attrib_stride: (path.num_attributes() + 1) / 2, path, p: path.points.len(), need_close: false, first: None, } } /// Builds a `Path` from This iterator. /// /// The iterator must be at the beginning. pub fn into_path(self) -> Path { let mut builder = Path::builder_with_attributes(self.num_attributes); for event in self { builder.event(event); } builder.build() } } impl<'l> Iterator for Reversed<'l> { type Item = Event<(Point, Attributes<'l>), Point>; fn next(&mut self) -> Option { // At the beginning of each iteration, `self.p` points to the index // directly after the endpoint of the current verb. let endpoint_stride = self.attrib_stride + 1; let v = self.verbs.next()?; let event = match v { Verb::Close => { self.need_close = true; // Close event contain the first endpoint, so skip over that by // offsetting by an extra endpoint stride. let idx = self.p - 2 * endpoint_stride; let first = ( self.path.points[idx], self.path.attributes(EndpointId(idx as u32)), ); self.first = Some(first); Event::Begin { at: first } } Verb::End => { // End events don't push an endpoint, the current endpoint // is the one of the previous command (or next going in reverse). let idx = self.p - endpoint_stride; self.need_close = false; let first = ( self.path.points[idx], self.path.attributes(EndpointId(idx as u32)), ); self.first = Some(first); Event::Begin { at: first } } Verb::Begin => { let close = self.need_close; self.need_close = false; let idx = self.p - endpoint_stride; Event::End { last: ( self.path.points[idx], self.path.attributes(EndpointId(idx as u32)), ), first: self.first.take().unwrap(), close, } } Verb::LineTo => { let from = self.p - endpoint_stride; let to = from - endpoint_stride; Event::Line { from: ( self.path.points[from], self.path.attributes(EndpointId(from as u32)), ), to: ( self.path.points[to], self.path.attributes(EndpointId(to as u32)), ), } } Verb::QuadraticTo => { let from = self.p - endpoint_stride; let ctrl = from - 1; let to = ctrl - endpoint_stride; Event::Quadratic { from: ( self.path.points[from], self.path.attributes(EndpointId(from as u32)), ), ctrl: self.path.points[ctrl], to: ( self.path.points[to], self.path.attributes(EndpointId(to as u32)), ), } } Verb::CubicTo => { let from = self.p - endpoint_stride; let ctrl1 = from - 1; let ctrl2 = ctrl1 - 1; let to = ctrl2 - endpoint_stride; Event::Cubic { from: ( self.path.points[from], self.path.attributes(EndpointId(from as u32)), ), ctrl1: self.path.points[ctrl1], ctrl2: self.path.points[ctrl2], to: ( self.path.points[to], self.path.attributes(EndpointId(to as u32)), ), } } }; self.p -= n_stored_points(*v, self.attrib_stride); Some(event) } } fn n_stored_points(verb: Verb, attrib_stride: usize) -> usize { match verb { Verb::Begin => attrib_stride + 1, Verb::LineTo => attrib_stride + 1, Verb::QuadraticTo => attrib_stride + 2, Verb::CubicTo => attrib_stride + 3, Verb::Close => attrib_stride + 1, Verb::End => 0, } } #[cfg(test)] fn slice(a: &[f32]) -> &[f32] { a } #[test] fn test_reverse_path_simple() { let mut builder = Path::builder_with_attributes(1); builder.begin(point(0.0, 0.0), &[1.0]); builder.line_to(point(1.0, 0.0), &[2.0]); builder.line_to(point(1.0, 1.0), &[3.0]); builder.line_to(point(0.0, 1.0), &[4.0]); builder.end(false); let p1 = builder.build(); let p2 = p1.reversed().with_attributes().into_path(); let mut it = p2.iter_with_attributes(); // Using a function that explicitly states the argument types works around type inference issue. fn check<'l>( a: Option), Point>>, b: Option), Point>>, ) -> bool { if a != b { std::println!("left: {a:?}"); std::println!("right: {b:?}"); } a == b } assert!(check( it.next(), Some(Event::Begin { at: (point(0.0, 1.0), &[4.0]) }) )); assert!(check( it.next(), Some(Event::Line { from: (point(0.0, 1.0), &[4.0]), to: (point(1.0, 1.0), &[3.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(1.0, 1.0), &[3.0]), to: (point(1.0, 0.0), &[2.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(1.0, 0.0), &[2.0]), to: (point(0.0, 0.0), &[1.0]), }) )); assert!(check( it.next(), Some(Event::End { last: (point(0.0, 0.0), &[1.0]), first: (point(0.0, 1.0), &[4.0]), close: false }) )); assert!(check(it.next(), None)); } #[test] fn test_reverse_path_1() { let mut builder = Path::builder_with_attributes(1); builder.begin(point(0.0, 0.0), &[1.0]); builder.line_to(point(1.0, 0.0), &[2.0]); builder.line_to(point(1.0, 1.0), &[3.0]); builder.line_to(point(0.0, 1.0), &[4.0]); builder.end(false); builder.begin(point(10.0, 0.0), &[5.0]); builder.line_to(point(11.0, 0.0), &[6.0]); builder.line_to(point(11.0, 1.0), &[7.0]); builder.line_to(point(10.0, 1.0), &[8.0]); builder.end(true); builder.begin(point(20.0, 0.0), &[9.0]); builder.quadratic_bezier_to(point(21.0, 0.0), point(21.0, 1.0), &[10.0]); builder.end(false); builder.begin(point(20.0, 0.0), &[9.0]); builder.quadratic_bezier_to(point(21.0, 0.0), point(21.0, 1.0), &[10.0]); builder.end(true); let p1 = builder.build(); let mut it = p1.reversed().with_attributes(); fn check<'l>( a: Option), Point>>, b: Option), Point>>, ) -> bool { if a != b { std::println!("left: {a:?}"); std::println!("right: {b:?}"); } a == b } assert!(check( it.next(), Some(Event::Begin { at: (point(21.0, 1.0), &[10.0]), }) )); assert!(check( it.next(), Some(Event::Quadratic { from: (point(21.0, 1.0), &[10.0]), ctrl: point(21.0, 0.0), to: (point(20.0, 0.0), &[9.0]), }) )); assert!(check( it.next(), Some(Event::End { last: (point(20.0, 0.0), &[9.0]), first: (point(21.0, 1.0), &[10.0]), close: true }) )); assert!(check( it.next(), Some(Event::Begin { at: (point(21.0, 1.0), &[10.0]), }) )); assert!(check( it.next(), Some(Event::Quadratic { from: (point(21.0, 1.0), &[10.0]), ctrl: point(21.0, 0.0), to: (point(20.0, 0.0), &[9.0]), }) )); assert!(check( it.next(), Some(Event::End { last: (point(20.0, 0.0), &[9.0]), first: (point(21.0, 1.0), &[10.0]), close: false }) )); assert!(check( it.next(), Some(Event::Begin { at: (point(10.0, 1.0), &[8.0]), // <-- }) )); assert!(check( it.next(), Some(Event::Line { from: (point(10.0, 1.0), &[8.0]), to: (point(11.0, 1.0), &[7.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(11.0, 1.0), &[7.0]), to: (point(11.0, 0.0), &[6.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(11.0, 0.0), &[6.0]), to: (point(10.0, 0.0), &[5.0]), }) )); assert!(check( it.next(), Some(Event::End { last: (point(10.0, 0.0), &[5.0]), first: (point(10.0, 1.0), &[8.0]), close: true }) )); assert!(check( it.next(), Some(Event::Begin { at: (point(0.0, 1.0), &[4.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(0.0, 1.0), &[4.0]), to: (point(1.0, 1.0), &[3.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(1.0, 1.0), &[3.0]), to: (point(1.0, 0.0), &[2.0]), }) )); assert!(check( it.next(), Some(Event::Line { from: (point(1.0, 0.0), &[2.0]), to: (point(0.0, 0.0), &[1.0]), }) )); assert!(check( it.next(), Some(Event::End { last: (point(0.0, 0.0), &[1.0]), first: (point(0.0, 1.0), &[4.0]), close: false }) )); assert!(check(it.next(), None)); } #[test] fn test_reverse_path_no_close() { let mut builder = Path::builder(); builder.begin(point(0.0, 0.0)); builder.line_to(point(1.0, 0.0)); builder.line_to(point(1.0, 1.0)); builder.end(false); let p1 = builder.build(); let mut it = p1.reversed(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(1.0, 1.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(1.0, 1.0), to: point(1.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(1.0, 0.0), to: point(0.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(0.0, 0.0), first: point(1.0, 1.0), close: false }) ); assert_eq!(it.next(), None); } #[test] fn test_reverse_empty_path() { let p = Path::builder().build(); assert_eq!(p.reversed().next(), None); } #[test] fn test_reverse_single_point() { let mut builder = Path::builder(); builder.begin(point(0.0, 0.0)); builder.end(false); let p1 = builder.build(); let mut it = p1.reversed(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(0.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(0.0, 0.0), first: point(0.0, 0.0), close: false }) ); assert_eq!(it.next(), None); } #[test] fn test_path_builder_1() { let mut p = BuilderWithAttributes::new(1); p.begin(point(0.0, 0.0), &[0.0]); p.line_to(point(1.0, 0.0), &[1.0]); p.line_to(point(2.0, 0.0), &[2.0]); p.line_to(point(3.0, 0.0), &[3.0]); p.quadratic_bezier_to(point(4.0, 0.0), point(4.0, 1.0), &[4.0]); p.cubic_bezier_to(point(5.0, 0.0), point(5.0, 1.0), point(5.0, 2.0), &[5.0]); p.end(true); p.begin(point(10.0, 0.0), &[6.0]); p.line_to(point(11.0, 0.0), &[7.0]); p.line_to(point(12.0, 0.0), &[8.0]); p.line_to(point(13.0, 0.0), &[9.0]); p.quadratic_bezier_to(point(14.0, 0.0), point(14.0, 1.0), &[10.0]); p.cubic_bezier_to( point(15.0, 0.0), point(15.0, 1.0), point(15.0, 2.0), &[11.0], ); p.end(true); p.begin(point(1.0, 1.0), &[12.0]); p.end(false); p.begin(point(2.0, 2.0), &[13.0]); p.end(false); p.begin(point(3.0, 3.0), &[14.0]); p.line_to(point(4.0, 4.0), &[15.0]); p.end(false); let path = p.build(); let mut it = path.iter_with_attributes(); assert_eq!( it.next(), Some(Event::Begin { at: (point(0.0, 0.0), slice(&[0.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(0.0, 0.0), slice(&[0.0])), to: (point(1.0, 0.0), slice(&[1.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(1.0, 0.0), slice(&[1.0])), to: (point(2.0, 0.0), slice(&[2.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(2.0, 0.0), slice(&[2.0])), to: (point(3.0, 0.0), slice(&[3.0])), }) ); assert_eq!( it.next(), Some(Event::Quadratic { from: (point(3.0, 0.0), slice(&[3.0])), ctrl: point(4.0, 0.0), to: (point(4.0, 1.0), slice(&[4.0])), }) ); assert_eq!( it.next(), Some(Event::Cubic { from: (point(4.0, 1.0), slice(&[4.0])), ctrl1: point(5.0, 0.0), ctrl2: point(5.0, 1.0), to: (point(5.0, 2.0), slice(&[5.0])), }) ); assert_eq!( it.next(), Some(Event::End { last: (point(5.0, 2.0), slice(&[5.0])), first: (point(0.0, 0.0), slice(&[0.0])), close: true }) ); assert_eq!( it.next(), Some(Event::Begin { at: (point(10.0, 0.0), slice(&[6.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(10.0, 0.0), slice(&[6.0])), to: (point(11.0, 0.0), slice(&[7.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(11.0, 0.0), slice(&[7.0])), to: (point(12.0, 0.0), slice(&[8.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(12.0, 0.0), slice(&[8.0])), to: (point(13.0, 0.0), slice(&[9.0])), }) ); assert_eq!( it.next(), Some(Event::Quadratic { from: (point(13.0, 0.0), slice(&[9.0])), ctrl: point(14.0, 0.0), to: (point(14.0, 1.0), slice(&[10.0])), }) ); assert_eq!( it.next(), Some(Event::Cubic { from: (point(14.0, 1.0), slice(&[10.0])), ctrl1: point(15.0, 0.0), ctrl2: point(15.0, 1.0), to: (point(15.0, 2.0), slice(&[11.0])), }) ); assert_eq!( it.next(), Some(Event::End { last: (point(15.0, 2.0), slice(&[11.0])), first: (point(10.0, 0.0), slice(&[6.0])), close: true }) ); assert_eq!( it.next(), Some(Event::Begin { at: (point(1.0, 1.0), slice(&[12.0])) }) ); assert_eq!( it.next(), Some(Event::End { last: (point(1.0, 1.0), slice(&[12.0])), first: (point(1.0, 1.0), slice(&[12.0])), close: false }) ); assert_eq!( it.next(), Some(Event::Begin { at: (point(2.0, 2.0), slice(&[13.0])), }) ); assert_eq!( it.next(), Some(Event::End { last: (point(2.0, 2.0), slice(&[13.0])), first: (point(2.0, 2.0), slice(&[13.0])), close: false }) ); assert_eq!( it.next(), Some(Event::Begin { at: (point(3.0, 3.0), slice(&[14.0])), }) ); assert_eq!( it.next(), Some(Event::Line { from: (point(3.0, 3.0), slice(&[14.0])), to: (point(4.0, 4.0), slice(&[15.0])), }) ); assert_eq!( it.next(), Some(Event::End { last: (point(4.0, 4.0), slice(&[15.0])), first: (point(3.0, 3.0), slice(&[14.0])), close: false }) ); assert_eq!(it.next(), None); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } #[test] fn test_path_builder_empty() { let path = Path::builder_with_attributes(5).build(); let mut it = path.iter(); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } #[test] fn test_path_builder_empty_begin() { let mut p = Path::builder_with_attributes(1); p.begin(point(1.0, 2.0), &[0.0]); p.end(false); p.begin(point(3.0, 4.0), &[1.0]); p.end(false); p.begin(point(5.0, 6.0), &[2.0]); p.end(false); let path = p.build(); let mut it = path.iter(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(1.0, 2.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(1.0, 2.0), first: point(1.0, 2.0), close: false, }) ); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(3.0, 4.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(3.0, 4.0), first: point(3.0, 4.0), close: false, }) ); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(5.0, 6.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(5.0, 6.0), first: point(5.0, 6.0), close: false, }) ); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } #[test] fn test_extend_from_paths() { let mut builder = Path::builder(); builder.begin(point(0.0, 0.0)); builder.line_to(point(5.0, 0.0)); builder.line_to(point(5.0, 5.0)); builder.end(true); let path1 = builder.build(); let mut builder = Path::builder(); builder.begin(point(1.0, 1.0)); builder.line_to(point(4.0, 0.0)); builder.line_to(point(4.0, 4.0)); builder.end(true); let path2 = builder.build(); let mut builder = Path::builder(); builder.extend_from_paths(&[path1.as_slice(), path2.as_slice()]); let path = builder.build(); let mut it = path.iter(); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(0.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(0.0, 0.0), to: point(5.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(5.0, 0.0), to: point(5.0, 5.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(5.0, 5.0), first: point(0.0, 0.0), close: true }) ); assert_eq!( it.next(), Some(PathEvent::Begin { at: point(1.0, 1.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(1.0, 1.0), to: point(4.0, 0.0) }) ); assert_eq!( it.next(), Some(PathEvent::Line { from: point(4.0, 0.0), to: point(4.0, 4.0) }) ); assert_eq!( it.next(), Some(PathEvent::End { last: point(4.0, 4.0), first: point(1.0, 1.0), close: true }) ); assert_eq!(it.next(), None); } #[test] fn flattened_custom_attributes() { let mut path = Path::builder_with_attributes(1); path.begin(point(0.0, 0.0), &[0.0]); path.quadratic_bezier_to(point(1.0, 0.0), point(1.0, 1.0), &[1.0]); path.cubic_bezier_to(point(1.0, 2.0), point(0.0, 2.0), point(0.0, 1.0), &[2.0]); path.end(false); let path = path.build(); let mut prev = -1.0; path.iter_with_attributes() .for_each_flattened(0.01, &mut |evt| { let attribute = match evt { Event::Begin { at: (_, attr) } => attr[0], Event::Line { from: (_, from_attr), to: (_, to_attr), } => { assert_eq!(from_attr[0], prev); to_attr[0] } Event::End { last: (_, last_attr), .. } => { assert_eq!(last_attr[0], prev); return; } Event::Quadratic { .. } | Event::Cubic { .. } => { panic!("Should not get a curve in for_each_flattened"); } }; assert!(attribute > prev); prev = attribute; }); } #[test] fn first_last() { let mut path = Path::builder_with_attributes(1); path.begin(point(0.0, 0.0), &[1.0]); path.line_to(point(2.0, 2.0), &[3.0]); path.line_to(point(4.0, 4.0), &[5.0]); path.end(false); let path = path.build(); assert_eq!( path.first_endpoint(), Some((point(0.0, 0.0), slice(&[1.0]))) ); assert_eq!(path.last_endpoint(), Some((point(4.0, 4.0), slice(&[5.0])))); let mut path = Path::builder_with_attributes(1); path.begin(point(0.0, 0.0), &[1.0]); path.line_to(point(2.0, 2.0), &[3.0]); path.line_to(point(4.0, 4.0), &[5.0]); path.end(true); let path = path.build(); assert_eq!( path.first_endpoint(), Some((point(0.0, 0.0), slice(&[1.0]))) ); assert_eq!(path.last_endpoint(), Some((point(0.0, 0.0), slice(&[1.0])))); } #[test] fn id_events() { let mut path = Path::builder_with_attributes(1); let e1 = path.begin(point(0.0, 0.0), &[1.0]); let e2 = path.line_to(point(2.0, 2.0), &[3.0]); let e3 = path.line_to(point(4.0, 4.0), &[5.0]); path.end(false); let e4 = path.begin(point(6.0, 6.0), &[7.0]); let e5 = path.line_to(point(8.0, 8.0), &[9.0]); let e6 = path.line_to(point(10.0, 10.0), &[11.0]); path.end(true); let e7 = path.begin(point(12.0, 12.0), &[13.0]); let e8 = path.line_to(point(14.0, 14.0), &[15.0]); path.end(false); let path = path.build(); let mut iter = path.id_iter(); assert_eq!(iter.next().unwrap(), Event::Begin { at: e1 }); assert_eq!(iter.next().unwrap(), Event::Line { from: e1, to: e2 }); assert_eq!(iter.next().unwrap(), Event::Line { from: e2, to: e3 }); assert_eq!( iter.next().unwrap(), Event::End { last: e3, first: e1, close: false } ); assert_eq!(iter.next().unwrap(), Event::Begin { at: e4 }); assert_eq!(iter.next().unwrap(), Event::Line { from: e4, to: e5 }); assert_eq!(iter.next().unwrap(), Event::Line { from: e5, to: e6 }); assert_eq!( iter.next().unwrap(), Event::End { last: e6, first: e4, close: true } ); assert_eq!(iter.next().unwrap(), Event::Begin { at: e7 }); assert_eq!(iter.next().unwrap(), Event::Line { from: e7, to: e8 }); assert_eq!( iter.next().unwrap(), Event::End { last: e8, first: e7, close: false } ); assert_eq!(iter.next(), None); } lyon_path-1.0.5/src/path_buffer.rs000064400000000000000000000411261046102023000152520ustar 00000000000000//! A container to store multiple paths contiguously. use crate::builder::*; use crate::math::*; use crate::path; use crate::{Attributes, EndpointId, PathSlice, NO_ATTRIBUTES}; use core::fmt; use core::iter::{FromIterator, FusedIterator, IntoIterator}; use core::ops::Range; use alloc::vec::Vec; #[derive(Clone, Debug)] struct PathDescriptor { points: (u32, u32), verbs: (u32, u32), num_attributes: u32, } /// An object that stores multiple paths contiguously. #[derive(Clone, Default)] pub struct PathBuffer { points: Vec, verbs: Vec, paths: Vec, } impl PathBuffer { #[inline] pub fn new() -> Self { PathBuffer { points: Vec::new(), verbs: Vec::new(), paths: Vec::new(), } } #[inline] pub fn with_capacity(endpoints: usize, ctrl_points: usize, paths: usize) -> Self { let mut buffer = PathBuffer::new(); buffer.reserve(endpoints, ctrl_points, paths); buffer } #[inline] pub fn as_slice(&self) -> PathBufferSlice { PathBufferSlice { points: &self.points, verbs: &self.verbs, paths: &self.paths, } } #[inline] pub fn get(&self, index: usize) -> PathSlice { let desc = &self.paths[index]; PathSlice { points: &self.points[desc.points.0 as usize..desc.points.1 as usize], verbs: &self.verbs[desc.verbs.0 as usize..desc.verbs.1 as usize], num_attributes: desc.num_attributes as usize, } } #[inline] pub fn indices(&self) -> Range { 0..self.paths.len() } #[inline] pub fn iter(&self) -> Iter<'_> { Iter::new(&self.points, &self.verbs, &self.paths) } #[inline] /// Returns the number of paths in the path buffer. pub fn len(&self) -> usize { self.paths.len() } /// Returns whether the path buffer is empty. #[inline] pub fn is_empty(&self) -> bool { self.paths.is_empty() } #[inline] pub fn builder(&mut self) -> Builder { Builder::new(self) } #[inline] pub fn clear(&mut self) { self.points.clear(); self.verbs.clear(); self.paths.clear(); } #[inline] pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize, paths: usize) { self.points.reserve(endpoints + ctrl_points); self.verbs.reserve(endpoints); self.paths.reserve(paths); } } impl fmt::Debug for PathBuffer { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.as_slice().fmt(formatter) } } impl<'l> FromIterator> for PathBuffer { fn from_iter>>(iter: T) -> PathBuffer { iter.into_iter() .fold(PathBuffer::new(), |mut buffer, path| { let builder = buffer.builder(); path.iter() .fold(builder, |mut builder, event| { builder.path_event(event, NO_ATTRIBUTES); builder }) .build(); buffer }) } } /// A view on a `PathBuffer`. #[derive(Clone)] pub struct PathBufferSlice<'l> { points: &'l [Point], verbs: &'l [path::Verb], paths: &'l [PathDescriptor], } impl<'l> PathBufferSlice<'l> { #[inline] pub fn get(&self, index: usize) -> PathSlice { let desc = &self.paths[index]; PathSlice { points: &self.points[desc.points.0 as usize..desc.points.1 as usize], verbs: &self.verbs[desc.verbs.0 as usize..desc.verbs.1 as usize], num_attributes: desc.num_attributes as usize, } } #[inline] pub fn indices(&self) -> Range { 0..self.paths.len() } #[inline] pub fn iter(&self) -> Iter<'_> { Iter::new(self.points, self.verbs, self.paths) } /// Returns the number of paths in the path buffer. #[inline] pub fn len(&self) -> usize { self.paths.len() } /// Returns whether the path buffer is empty. #[inline] pub fn is_empty(&self) -> bool { self.paths.is_empty() } } impl<'l> fmt::Debug for PathBufferSlice<'l> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, "PathBuffer {{ paths: {:?}, points: {:?}, verbs: {:?}, ", self.paths.len(), self.points.len(), self.verbs.len(), )?; for idx in self.indices() { write!(formatter, "#{idx:?}: ")?; self.get(idx).fmt(formatter)?; write!(formatter, ", ")?; } write!(formatter, " }}") } } /// A Builder that appends a path to an existing PathBuffer. /// /// Implements the `PathBuilder` trait. pub struct Builder<'l> { buffer: &'l mut PathBuffer, builder: path::Builder, points_start: u32, verbs_start: u32, } impl<'l> Builder<'l> { #[inline] fn new(buffer: &'l mut PathBuffer) -> Self { let mut builder = path::Path::builder(); core::mem::swap(&mut buffer.points, &mut builder.inner_mut().points); core::mem::swap(&mut buffer.verbs, &mut builder.inner_mut().verbs); let points_start = builder.inner().points.len() as u32; let verbs_start = builder.inner().verbs.len() as u32; Builder { buffer, builder, points_start, verbs_start, } } #[inline] pub fn with_attributes(self, num_attributes: usize) -> BuilderWithAttributes<'l> { assert_eq!(self.builder.inner().verbs.len(), self.verbs_start as usize); BuilderWithAttributes { buffer: self.buffer, builder: path::BuilderWithAttributes { builder: self.builder.into_inner(), num_attributes, first_attributes: alloc::vec![0.0; num_attributes], }, points_start: self.points_start, verbs_start: self.verbs_start, } } #[inline] pub fn build(mut self) -> usize { let points_end = self.builder.inner().points.len() as u32; let verbs_end = self.builder.inner().verbs.len() as u32; core::mem::swap( &mut self.builder.inner_mut().points, &mut self.buffer.points, ); core::mem::swap(&mut self.builder.inner_mut().verbs, &mut self.buffer.verbs); let index = self.buffer.paths.len(); self.buffer.paths.push(PathDescriptor { points: (self.points_start, points_end), verbs: (self.verbs_start, verbs_end), num_attributes: 0, }); index } #[inline] fn adjust_id(&self, mut id: EndpointId) -> EndpointId { id.0 -= self.points_start; id } #[inline] pub fn begin(&mut self, at: Point) -> EndpointId { let id = self.builder.begin(at); self.adjust_id(id) } #[inline] pub fn end(&mut self, close: bool) { self.builder.end(close) } #[inline] pub fn line_to(&mut self, to: Point) -> EndpointId { let id = self.builder.line_to(to); self.adjust_id(id) } #[inline] pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId { let id = self.builder.quadratic_bezier_to(ctrl, to); self.adjust_id(id) } #[inline] pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId { let id = self.builder.cubic_bezier_to(ctrl1, ctrl2, to); self.adjust_id(id) } #[inline] pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints, ctrl_points); } } impl<'l> PathBuilder for Builder<'l> { #[inline] fn num_attributes(&self) -> usize { 0 } #[inline] fn begin(&mut self, at: Point, _attributes: Attributes) -> EndpointId { self.begin(at) } #[inline] fn end(&mut self, close: bool) { self.end(close); } #[inline] fn line_to(&mut self, to: Point, _attributes: Attributes) -> EndpointId { self.line_to(to) } #[inline] fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.quadratic_bezier_to(ctrl, to) } #[inline] fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, _attributes: Attributes, ) -> EndpointId { self.cubic_bezier_to(ctrl1, ctrl2, to) } #[inline] fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.reserve(endpoints, ctrl_points); } } impl<'l> Build for Builder<'l> { type PathType = usize; fn build(self) -> usize { self.build() } } /// A Builder that appends a path to an existing PathBuffer, with custom attributes. pub struct BuilderWithAttributes<'l> { buffer: &'l mut PathBuffer, builder: path::BuilderWithAttributes, points_start: u32, verbs_start: u32, } impl<'l> BuilderWithAttributes<'l> { #[inline] pub fn new(buffer: &'l mut PathBuffer, num_attributes: usize) -> Self { let mut builder = path::Path::builder().into_inner(); core::mem::swap(&mut buffer.points, &mut builder.points); core::mem::swap(&mut buffer.verbs, &mut builder.verbs); let points_start = builder.points.len() as u32; let verbs_start = builder.verbs.len() as u32; BuilderWithAttributes { buffer, builder: path::BuilderWithAttributes { builder, num_attributes, first_attributes: alloc::vec![0.0; num_attributes], }, points_start, verbs_start, } } #[inline] pub fn build(mut self) -> usize { let points_end = self.builder.builder.points.len() as u32; let verbs_end = self.builder.builder.verbs.len() as u32; core::mem::swap(&mut self.builder.builder.points, &mut self.buffer.points); core::mem::swap(&mut self.builder.builder.verbs, &mut self.buffer.verbs); let index = self.buffer.paths.len(); self.buffer.paths.push(PathDescriptor { points: (self.points_start, points_end), verbs: (self.verbs_start, verbs_end), num_attributes: 0, }); index } #[inline] fn adjust_id(&self, mut id: EndpointId) -> EndpointId { id.0 -= self.points_start; id } #[inline] pub fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { let id = self.builder.begin(at, attributes); self.adjust_id(id) } #[inline] pub fn end(&mut self, close: bool) { self.builder.end(close) } #[inline] pub fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { let id = self.builder.line_to(to, attributes); self.adjust_id(id) } #[inline] pub fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = self.builder.quadratic_bezier_to(ctrl, to, attributes); self.adjust_id(id) } #[inline] pub fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { let id = self.builder.cubic_bezier_to(ctrl1, ctrl2, to, attributes); self.adjust_id(id) } #[inline] pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.builder.reserve(endpoints, ctrl_points); } } impl<'l> PathBuilder for BuilderWithAttributes<'l> { #[inline] fn num_attributes(&self) -> usize { self.builder.num_attributes() } #[inline] fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId { self.begin(at, attributes) } #[inline] fn end(&mut self, close: bool) { self.end(close); } #[inline] fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId { self.line_to(to, attributes) } #[inline] fn quadratic_bezier_to( &mut self, ctrl: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.quadratic_bezier_to(ctrl, to, attributes) } #[inline] fn cubic_bezier_to( &mut self, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, ) -> EndpointId { self.cubic_bezier_to(ctrl1, ctrl2, to, attributes) } #[inline] fn reserve(&mut self, endpoints: usize, ctrl_points: usize) { self.reserve(endpoints, ctrl_points); } } impl<'l> Build for BuilderWithAttributes<'l> { type PathType = usize; fn build(self) -> usize { self.build() } } /// Iterator over the paths in a [`PathBufferSlice`]. #[derive(Clone)] pub struct Iter<'l> { points: &'l [Point], verbs: &'l [path::Verb], paths: ::core::slice::Iter<'l, PathDescriptor>, } impl<'l> Iter<'l> { fn new(points: &'l [Point], verbs: &'l [path::Verb], paths: &'l [PathDescriptor]) -> Iter<'l> { Iter { points, verbs, paths: paths.iter(), } } } impl<'l> Iterator for Iter<'l> { type Item = PathSlice<'l>; fn next(&mut self) -> Option> { let path = self.paths.next()?; Some(PathSlice { points: &self.points[path.points.0 as usize..path.points.1 as usize], verbs: &self.verbs[path.verbs.0 as usize..path.verbs.1 as usize], num_attributes: path.num_attributes as usize, }) } fn size_hint(&self) -> (usize, Option) { self.paths.size_hint() } } // slice::Iter is Fused and ExactSize impl<'l> FusedIterator for Iter<'l> {} impl<'l> ExactSizeIterator for Iter<'l> {} impl<'l> DoubleEndedIterator for Iter<'l> { fn next_back(&mut self) -> Option> { let path = self.paths.next_back()?; Some(PathSlice { points: &self.points[path.points.0 as usize..path.points.1 as usize], verbs: &self.verbs[path.verbs.0 as usize..path.verbs.1 as usize], num_attributes: path.num_attributes as usize, }) } } #[test] fn simple() { use crate::PathEvent; let mut buffer = PathBuffer::new(); let mut builder = buffer.builder(); builder.begin(point(0.0, 0.0)); builder.line_to(point(10.0, 0.0)); builder.line_to(point(10.0, 10.0)); let a = builder.line_to(point(0.0, 10.0)); builder.end(true); let p1 = builder.build(); let mut builder = buffer.builder(); builder.begin(point(0.0, 0.0)); builder.line_to(point(20.0, 0.0)); builder.line_to(point(20.0, 20.0)); let b = builder.line_to(point(0.0, 20.0)); builder.end(false); let p2 = builder.build(); let mut iter = buffer.get(p1).iter(); assert_eq!( iter.next(), Some(PathEvent::Begin { at: point(0.0, 0.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(0.0, 0.0), to: point(10.0, 0.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(10.0, 0.0), to: point(10.0, 10.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(10.0, 10.0), to: point(0.0, 10.0) }) ); assert_eq!( iter.next(), Some(PathEvent::End { last: point(0.0, 10.0), first: point(0.0, 0.0), close: true }) ); assert_eq!(iter.next(), None); let mut iter = buffer.get(p2).iter(); assert_eq!( iter.next(), Some(PathEvent::Begin { at: point(0.0, 0.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(0.0, 0.0), to: point(20.0, 0.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(20.0, 0.0), to: point(20.0, 20.0) }) ); assert_eq!( iter.next(), Some(PathEvent::Line { from: point(20.0, 20.0), to: point(0.0, 20.0) }) ); assert_eq!( iter.next(), Some(PathEvent::End { last: point(0.0, 20.0), first: point(0.0, 0.0), close: false }) ); assert_eq!(iter.next(), None); assert_eq!(buffer.get(p1)[a], point(0.0, 10.0)); assert_eq!(buffer.get(p2)[b], point(0.0, 20.0)); } lyon_path-1.0.5/src/polygon.rs000064400000000000000000000253041046102023000144540ustar 00000000000000//! Specific path types for polygons. use crate::math::Point; use crate::{ ControlPointId, EndpointId, Event, EventId, IdEvent, PathEvent, Position, PositionStore, }; /// A view over a sequence of endpoints forming a polygon. /// /// ## Example /// /// ``` /// use lyon_path::polygon::Polygon; /// use lyon_path::geom::point; /// /// let path = Polygon { /// points: &[ /// point(0.0, 0.0), /// point(10.0, 10.0), /// point(0.0, 10.0), /// ], /// closed: true, /// }; /// /// for event in path.path_events() { /// // same as iterating a regular `Path` object. /// } /// ``` #[derive(Clone)] pub struct Polygon<'l, T> { pub points: &'l [T], pub closed: bool, } impl<'l, T> Polygon<'l, T> { /// Returns an iterator of `Event<&T>`. pub fn iter(&self) -> PolygonIter<'l, T> { PolygonIter { points: self.points.iter(), prev: None, first: None, closed: self.closed, } } /// Returns an iterator of `IdEvent`. pub fn id_iter(&self) -> PolygonIdIter { PolygonIdIter::new(0..(self.points.len() as u32), self.closed) } /// Returns an iterator of `PathEvent`. pub fn path_events(&self) -> PathEvents where T: Position, { PathEvents { points: self.points.iter(), first: None, prev: None, closed: self.closed, } } /// Returns the event for a given event ID. pub fn event(&self, id: EventId) -> Event<&T, ()> { let idx = id.0 as usize; if idx == 0 { Event::Begin { at: &self.points[0], } } else if idx == self.points.len() - 1 { Event::End { last: &self.points[self.points.len() - 1], first: &self.points[0], close: self.closed, } } else { Event::Line { from: &self.points[idx - 1], to: &self.points[idx], } } } } impl<'l, T> core::ops::Index for Polygon<'l, T> { type Output = T; fn index(&self, id: EndpointId) -> &T { &self.points[id.to_usize()] } } /// A view over a sequence of endpoint IDs forming a polygon. #[derive(Clone)] pub struct IdPolygon<'l> { pub points: &'l [EndpointId], pub closed: bool, } impl<'l> IdPolygon<'l> { // Returns an iterator over the endpoint IDs of the polygon. pub fn iter(&self) -> IdPolygonIter<'l> { IdPolygonIter { points: self.points.iter(), idx: 0, prev: None, first: EndpointId(0), closed: self.closed, } } /// Returns the event for a given event ID. pub fn event(&self, id: EventId) -> IdEvent { let idx = id.0 as usize; if idx == 0 { IdEvent::Begin { at: self.points[0] } } else if idx == self.points.len() { IdEvent::End { last: self.points[self.points.len() - 1], first: self.points[0], close: self.closed, } } else { IdEvent::Line { from: self.points[idx - 1], to: self.points[idx], } } } } /// An iterator of `Event`. #[derive(Clone)] pub struct IdPolygonIter<'l> { points: core::slice::Iter<'l, EndpointId>, idx: u32, prev: Option, first: EndpointId, closed: bool, } impl<'l> Iterator for IdPolygonIter<'l> { type Item = IdEvent; fn next(&mut self) -> Option { match (self.prev, self.points.next()) { (Some(from), Some(to)) => { self.prev = Some(*to); self.idx += 1; Some(IdEvent::Line { from, to: *to }) } (None, Some(at)) => { self.prev = Some(*at); self.first = *at; self.idx += 1; Some(IdEvent::Begin { at: *at }) } (Some(last), None) => { self.prev = None; Some(IdEvent::End { last, first: self.first, close: self.closed, }) } (None, None) => None, } } } /// An iterator of `Event<&Endpoint, ()>`. #[derive(Clone)] pub struct PolygonIter<'l, T> { points: core::slice::Iter<'l, T>, prev: Option<&'l T>, first: Option<&'l T>, closed: bool, } impl<'l, T> Iterator for PolygonIter<'l, T> { type Item = Event<&'l T, ()>; fn next(&mut self) -> Option> { match (self.prev, self.points.next()) { (Some(from), Some(to)) => { self.prev = Some(to); Some(Event::Line { from, to }) } (None, Some(at)) => { self.prev = Some(at); self.first = Some(at); Some(Event::Begin { at }) } (Some(last), None) => { self.prev = None; Some(Event::End { last, first: self.first.unwrap(), close: self.closed, }) } (None, None) => None, } } } /// An iterator of `PathEvent`. #[derive(Clone)] pub struct PathEvents<'l, T> { points: core::slice::Iter<'l, T>, prev: Option, first: Option, closed: bool, } impl<'l, T: Position> Iterator for PathEvents<'l, T> { type Item = PathEvent; fn next(&mut self) -> Option { match (self.prev, self.points.next()) { (Some(from), Some(to)) => { let to = to.position(); self.prev = Some(to); Some(Event::Line { from, to }) } (None, Some(at)) => { let at = at.position(); self.prev = Some(at); self.first = Some(at); Some(Event::Begin { at }) } (Some(last), None) => { self.prev = None; Some(Event::End { last, first: self.first.unwrap(), close: self.closed, }) } (None, None) => None, } } } /// An iterator of `IdEvent` for `Polygon`. #[derive(Clone)] pub struct PolygonIdIter { idx: u32, start: u32, end: u32, closed: bool, } impl PolygonIdIter { #[inline] pub fn new(range: core::ops::Range, closed: bool) -> Self { PolygonIdIter { idx: range.start, start: range.start, end: range.end, closed, } } } impl Iterator for PolygonIdIter { type Item = IdEvent; fn next(&mut self) -> Option { let idx = self.idx; self.idx += 1; if idx == self.start { Some(IdEvent::Begin { at: EndpointId(self.start), }) } else if idx < self.end { Some(IdEvent::Line { from: EndpointId(idx - 1), to: EndpointId(idx), }) } else if idx == self.end { Some(IdEvent::End { last: EndpointId(self.end - 1), first: EndpointId(self.start), close: self.closed, }) } else { None } } } impl<'l, Endpoint> PositionStore for Polygon<'l, Endpoint> where Endpoint: Position, { fn get_endpoint(&self, id: EndpointId) -> Point { self.points[id.to_usize()].position() } fn get_control_point(&self, _: ControlPointId) -> Point { panic!("Polygons do not have control points."); } } #[test] fn event_ids() { let poly = IdPolygon { points: &[EndpointId(0), EndpointId(1), EndpointId(2), EndpointId(3)], closed: true, }; assert_eq!(poly.event(EventId(0)), IdEvent::Begin { at: EndpointId(0) }); assert_eq!( poly.event(EventId(1)), IdEvent::Line { from: EndpointId(0), to: EndpointId(1) } ); assert_eq!( poly.event(EventId(2)), IdEvent::Line { from: EndpointId(1), to: EndpointId(2) } ); assert_eq!( poly.event(EventId(3)), IdEvent::Line { from: EndpointId(2), to: EndpointId(3) } ); assert_eq!( poly.event(EventId(4)), IdEvent::End { last: EndpointId(3), first: EndpointId(0), close: true } ); let mut iter = poly.iter(); assert_eq!(iter.next(), Some(IdEvent::Begin { at: EndpointId(0) })); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(0), to: EndpointId(1) }) ); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(1), to: EndpointId(2) }) ); assert_eq!( iter.next(), Some(IdEvent::Line { from: EndpointId(2), to: EndpointId(3) }) ); assert_eq!( iter.next(), Some(IdEvent::End { last: EndpointId(3), first: EndpointId(0), close: true }) ); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } #[test] fn polygon_slice_id_ite() { let points: &[u32] = &[0, 1, 2, 3, 4, 5, 6]; let polygon = Polygon { points, closed: true, }; let mut it = polygon.id_iter(); assert_eq!(it.next(), Some(IdEvent::Begin { at: EndpointId(0) })); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(0), to: EndpointId(1) }) ); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(1), to: EndpointId(2) }) ); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(2), to: EndpointId(3) }) ); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(3), to: EndpointId(4) }) ); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(4), to: EndpointId(5) }) ); assert_eq!( it.next(), Some(IdEvent::Line { from: EndpointId(5), to: EndpointId(6) }) ); assert_eq!( it.next(), Some(IdEvent::End { last: EndpointId(6), first: EndpointId(0), close: true }) ); assert_eq!(it.next(), None); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } lyon_path-1.0.5/src/private.rs000064400000000000000000000064401046102023000144370ustar 00000000000000#![allow(clippy::too_many_arguments)] // This module contains a few helpers that should not be considered as part of the public API, // but are exposed for use by other lyon crates. // Changing them doesn't necessarily imply semver breaking bumps. pub use crate::geom::{CubicBezierSegment, QuadraticBezierSegment}; pub use crate::math::Point; pub use crate::traits::PathBuilder; pub use crate::{Attributes, EndpointId}; #[derive(Default, Copy, Clone, Debug, PartialEq)] pub struct DebugValidator { #[cfg(debug_assertions)] in_subpath: bool, } impl DebugValidator { #[inline(always)] pub fn new() -> Self { Self::default() } #[inline(always)] pub fn begin(&mut self) { #[cfg(debug_assertions)] { assert!(!self.in_subpath, "multiple begin() calls without end()"); self.in_subpath = true; } } #[inline(always)] pub fn end(&mut self) { #[cfg(debug_assertions)] { assert!(self.in_subpath, "end() called without begin()"); self.in_subpath = false; } } #[inline(always)] pub fn edge(&self) { #[cfg(debug_assertions)] assert!(self.in_subpath, "edge operation is made before begin()"); } /// TODO: this should consume `self` to ensure it is dropped after this call /// TODO: also, DebugValidator probably should not be exposed in the public API. #[inline(always)] pub fn build(&self) { #[cfg(debug_assertions)] assert!(!self.in_subpath, "build() called before end()"); } } pub fn flatten_quadratic_bezier( tolerance: f32, from: Point, ctrl: Point, to: Point, attributes: Attributes, prev_attributes: Attributes, builder: &mut impl PathBuilder, buffer: &mut [f32], ) -> EndpointId { let curve = QuadraticBezierSegment { from, ctrl, to }; let n = attributes.len(); let mut id = EndpointId::INVALID; curve.for_each_flattened_with_t(tolerance, &mut |line, t| { let attr = if t.end == 1.0 { attributes } else { for i in 0..n { buffer[i] = prev_attributes[i] * (1.0 - t.end) + attributes[i] * t.end; } // BUG: https://github.com/rust-lang/rust-clippy/issues/10608 #[allow(clippy::redundant_slicing)] &buffer[..] }; id = builder.line_to(line.to, attr); }); id } pub fn flatten_cubic_bezier( tolerance: f32, from: Point, ctrl1: Point, ctrl2: Point, to: Point, attributes: Attributes, prev_attributes: Attributes, builder: &mut impl PathBuilder, buffer: &mut [f32], ) -> EndpointId { let curve = CubicBezierSegment { from, ctrl1, ctrl2, to, }; let n = attributes.len(); let mut id = EndpointId::INVALID; curve.for_each_flattened_with_t(tolerance, &mut |line, t| { let attr = if t.end == 1.0 { attributes } else { for i in 0..n { buffer[i] = prev_attributes[i] * (1.0 - t.end) + attributes[i] * t.end; } // BUG: https://github.com/rust-lang/rust-clippy/issues/10608 #[allow(clippy::redundant_slicing)] &buffer[..] }; id = builder.line_to(line.to, attr); }); id }