241 lines · 6.9 KB · Rust
RawDownload
use anyhow::Error;use etagere::euclid::Vector2D;use lyon::geom::Angle;use lyon::tessellation::{    BuffersBuilder, FillTessellator, FillVertex, StrokeTessellator, StrokeVertex, VertexBuffers,}; pub use lyon::math::Transform;pub use lyon::tessellation::{FillOptions, FillRule, StrokeOptions}; use crate::{point, px, Path, Pixels, Point}; /// Style of the PathBuilderpub enum PathStyle {    /// Stroke style    Stroke(StrokeOptions),    /// Fill style    Fill(FillOptions),} /// A [`Path`] builder.pub struct PathBuilder {    raw: lyon::path::builder::WithSvg<lyon::path::BuilderImpl>,    transform: Option<lyon::math::Transform>,    /// PathStyle of the PathBuilder    pub style: PathStyle,} impl From<lyon::path::Builder> for PathBuilder {    fn from(builder: lyon::path::Builder) -> Self {        Self {            raw: builder.with_svg(),            ..Default::default()        }    }} impl From<lyon::path::builder::WithSvg<lyon::path::BuilderImpl>> for PathBuilder {    fn from(raw: lyon::path::builder::WithSvg<lyon::path::BuilderImpl>) -> Self {        Self {            raw,            ..Default::default()        }    }} impl From<lyon::math::Point> for Point<Pixels> {    fn from(p: lyon::math::Point) -> Self {        point(px(p.x), px(p.y))    }} impl From<Point<Pixels>> for lyon::math::Point {    fn from(p: Point<Pixels>) -> Self {        lyon::math::point(p.x.0, p.y.0)    }} impl Default for PathBuilder {    fn default() -> Self {        Self {            raw: lyon::path::Path::builder().with_svg(),            style: PathStyle::Fill(FillOptions::default()),            transform: None,        }    }} impl PathBuilder {    /// Creates a new [`PathBuilder`] to build a Stroke path.    pub fn stroke(width: Pixels) -> Self {        Self {            style: PathStyle::Stroke(StrokeOptions::default().with_line_width(width.0)),            ..Self::default()        }    }     /// Creates a new [`PathBuilder`] to build a Fill path.    pub fn fill() -> Self {        Self::default()    }     /// Sets the style of the [`PathBuilder`].    pub fn with_style(self, style: PathStyle) -> Self {        Self { style, ..self }    }     /// Move the current point to the given point.    #[inline]    pub fn move_to(&mut self, to: Point<Pixels>) {        self.raw.move_to(to.into());    }     /// Draw a straight line from the current point to the given point.    #[inline]    pub fn line_to(&mut self, to: Point<Pixels>) {        self.raw.line_to(to.into());    }     /// Draw a curve from the current point to the given point, using the given control point.    #[inline]    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {        self.raw.quadratic_bezier_to(ctrl.into(), to.into());    }     /// Adds a cubic Bézier to the [`Path`] given its two control points    /// and its end point.    #[inline]    pub fn cubic_bezier_to(        &mut self,        to: Point<Pixels>,        control_a: Point<Pixels>,        control_b: Point<Pixels>,    ) {        self.raw            .cubic_bezier_to(control_a.into(), control_b.into(), to.into());    }     /// Close the current sub-path.    #[inline]    pub fn close(&mut self) {        self.raw.close();    }     /// Applies a transform to the path.    #[inline]    pub fn transform(&mut self, transform: Transform) {        self.transform = Some(transform);    }     /// Applies a translation to the path.    #[inline]    pub fn translate(&mut self, to: Point<Pixels>) {        if let Some(transform) = self.transform {            self.transform = Some(transform.then_translate(Vector2D::new(to.x.0, to.y.0)));        } else {            self.transform = Some(Transform::translation(to.x.0, to.y.0))        }    }     /// Applies a scale to the path.    #[inline]    pub fn scale(&mut self, scale: f32) {        if let Some(transform) = self.transform {            self.transform = Some(transform.then_scale(scale, scale));        } else {            self.transform = Some(Transform::scale(scale, scale));        }    }     /// Applies a rotation to the path.    ///    /// The `angle` is in degrees value in the range 0.0 to 360.0.    #[inline]    pub fn rotate(&mut self, angle: f32) {        let radians = angle.to_radians();        if let Some(transform) = self.transform {            self.transform = Some(transform.then_rotate(Angle::radians(radians)));        } else {            self.transform = Some(Transform::rotation(Angle::radians(radians)));        }    }     /// Builds into a [`Path`].    #[inline]    pub fn build(self) -> Result<Path<Pixels>, Error> {        let path = if let Some(transform) = self.transform {            self.raw.build().transformed(&transform)        } else {            self.raw.build()        };         match self.style {            PathStyle::Stroke(options) => Self::tessellate_stroke(&path, &options),            PathStyle::Fill(options) => Self::tessellate_fill(&path, &options),        }    }     fn tessellate_fill(        path: &lyon::path::Path,        options: &FillOptions,    ) -> Result<Path<Pixels>, Error> {        // Will contain the result of the tessellation.        let mut buf: VertexBuffers<lyon::math::Point, u16> = VertexBuffers::new();        let mut tessellator = FillTessellator::new();         // Compute the tessellation.        tessellator.tessellate_path(            path,            options,            &mut BuffersBuilder::new(&mut buf, |vertex: FillVertex| vertex.position()),        )?;         Ok(Self::build_path(buf))    }     fn tessellate_stroke(        path: &lyon::path::Path,        options: &StrokeOptions,    ) -> Result<Path<Pixels>, Error> {        // Will contain the result of the tessellation.        let mut buf: VertexBuffers<lyon::math::Point, u16> = VertexBuffers::new();        let mut tessellator = StrokeTessellator::new();         // Compute the tessellation.        tessellator.tessellate_path(            path,            options,            &mut BuffersBuilder::new(&mut buf, |vertex: StrokeVertex| vertex.position()),        )?;         Ok(Self::build_path(buf))    }     /// Builds a [`Path`] from a [`lyon::VertexBuffers`].    pub fn build_path(buf: VertexBuffers<lyon::math::Point, u16>) -> Path<Pixels> {        if buf.vertices.is_empty() {            return Path::new(Point::default());        }         let first_point = buf.vertices[0];         let mut path = Path::new(first_point.into());        for i in 0..buf.indices.len() / 3 {            let i0 = buf.indices[i * 3] as usize;            let i1 = buf.indices[i * 3 + 1] as usize;            let i2 = buf.indices[i * 3 + 2] as usize;             let v0 = buf.vertices[i0];            let v1 = buf.vertices[i1];            let v2 = buf.vertices[i2];             path.push_triangle(                (v0.into(), v1.into(), v2.into()),                (point(0., 1.), point(0., 1.), point(0., 1.)),            );        }         path    }}