path_builder.rs

  1use anyhow::Error;
  2use etagere::euclid::{Point2D, Vector2D};
  3use lyon::geom::Angle;
  4use lyon::math::{Vector, vector};
  5use lyon::path::traits::SvgPathBuilder;
  6use lyon::path::{ArcFlags, Polygon};
  7use lyon::tessellation::{
  8    BuffersBuilder, FillTessellator, FillVertex, StrokeTessellator, StrokeVertex, VertexBuffers,
  9};
 10
 11pub use lyon::math::Transform;
 12pub use lyon::tessellation::{FillOptions, FillRule, StrokeOptions};
 13
 14use crate::{Path, Pixels, Point, point, px};
 15
 16/// Style of the PathBuilder
 17pub enum PathStyle {
 18    /// Stroke style
 19    Stroke(StrokeOptions),
 20    /// Fill style
 21    Fill(FillOptions),
 22}
 23
 24/// A [`Path`] builder.
 25pub struct PathBuilder {
 26    raw: lyon::path::builder::WithSvg<lyon::path::BuilderImpl>,
 27    transform: Option<lyon::math::Transform>,
 28    /// PathStyle of the PathBuilder
 29    pub style: PathStyle,
 30}
 31
 32impl From<lyon::path::Builder> for PathBuilder {
 33    fn from(builder: lyon::path::Builder) -> Self {
 34        Self {
 35            raw: builder.with_svg(),
 36            ..Default::default()
 37        }
 38    }
 39}
 40
 41impl From<lyon::path::builder::WithSvg<lyon::path::BuilderImpl>> for PathBuilder {
 42    fn from(raw: lyon::path::builder::WithSvg<lyon::path::BuilderImpl>) -> Self {
 43        Self {
 44            raw,
 45            ..Default::default()
 46        }
 47    }
 48}
 49
 50impl From<lyon::math::Point> for Point<Pixels> {
 51    fn from(p: lyon::math::Point) -> Self {
 52        point(px(p.x), px(p.y))
 53    }
 54}
 55
 56impl From<Point<Pixels>> for lyon::math::Point {
 57    fn from(p: Point<Pixels>) -> Self {
 58        lyon::math::point(p.x.0, p.y.0)
 59    }
 60}
 61
 62impl From<Point<Pixels>> for Vector {
 63    fn from(p: Point<Pixels>) -> Self {
 64        vector(p.x.0, p.y.0)
 65    }
 66}
 67
 68impl From<Point<Pixels>> for Point2D<f32, Pixels> {
 69    fn from(p: Point<Pixels>) -> Self {
 70        Point2D::new(p.x.0, p.y.0)
 71    }
 72}
 73
 74impl Default for PathBuilder {
 75    fn default() -> Self {
 76        Self {
 77            raw: lyon::path::Path::builder().with_svg(),
 78            style: PathStyle::Fill(FillOptions::default()),
 79            transform: None,
 80        }
 81    }
 82}
 83
 84impl PathBuilder {
 85    /// Creates a new [`PathBuilder`] to build a Stroke path.
 86    pub fn stroke(width: Pixels) -> Self {
 87        Self {
 88            style: PathStyle::Stroke(StrokeOptions::default().with_line_width(width.0)),
 89            ..Self::default()
 90        }
 91    }
 92
 93    /// Creates a new [`PathBuilder`] to build a Fill path.
 94    pub fn fill() -> Self {
 95        Self::default()
 96    }
 97
 98    /// Sets the style of the [`PathBuilder`].
 99    pub fn with_style(self, style: PathStyle) -> Self {
100        Self { style, ..self }
101    }
102
103    /// Move the current point to the given point.
104    #[inline]
105    pub fn move_to(&mut self, to: Point<Pixels>) {
106        self.raw.move_to(to.into());
107    }
108
109    /// Draw a straight line from the current point to the given point.
110    #[inline]
111    pub fn line_to(&mut self, to: Point<Pixels>) {
112        self.raw.line_to(to.into());
113    }
114
115    /// Draw a curve from the current point to the given point, using the given control point.
116    #[inline]
117    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
118        self.raw.quadratic_bezier_to(ctrl.into(), to.into());
119    }
120
121    /// Adds a cubic Bézier to the [`Path`] given its two control points
122    /// and its end point.
123    #[inline]
124    pub fn cubic_bezier_to(
125        &mut self,
126        to: Point<Pixels>,
127        control_a: Point<Pixels>,
128        control_b: Point<Pixels>,
129    ) {
130        self.raw
131            .cubic_bezier_to(control_a.into(), control_b.into(), to.into());
132    }
133
134    /// Adds an elliptical arc.
135    pub fn arc_to(
136        &mut self,
137        radii: Point<Pixels>,
138        x_rotation: Pixels,
139        large_arc: bool,
140        sweep: bool,
141        to: Point<Pixels>,
142    ) {
143        self.raw.arc_to(
144            radii.into(),
145            Angle::degrees(x_rotation.into()),
146            ArcFlags { large_arc, sweep },
147            to.into(),
148        );
149    }
150
151    /// Equivalent to `arc_to` in relative coordinates.
152    pub fn relative_arc_to(
153        &mut self,
154        radii: Point<Pixels>,
155        x_rotation: Pixels,
156        large_arc: bool,
157        sweep: bool,
158        to: Point<Pixels>,
159    ) {
160        self.raw.relative_arc_to(
161            radii.into(),
162            Angle::degrees(x_rotation.into()),
163            ArcFlags { large_arc, sweep },
164            to.into(),
165        );
166    }
167
168    /// Adds a polygon.
169    pub fn add_polygon(&mut self, points: &[Point<Pixels>], closed: bool) {
170        let points = points.iter().copied().map(|p| p.into()).collect::<Vec<_>>();
171        self.raw.add_polygon(Polygon {
172            points: points.as_ref(),
173            closed,
174        });
175    }
176
177    /// Close the current sub-path.
178    #[inline]
179    pub fn close(&mut self) {
180        self.raw.close();
181    }
182
183    /// Applies a transform to the path.
184    #[inline]
185    pub fn transform(&mut self, transform: Transform) {
186        self.transform = Some(transform);
187    }
188
189    /// Applies a translation to the path.
190    #[inline]
191    pub fn translate(&mut self, to: Point<Pixels>) {
192        if let Some(transform) = self.transform {
193            self.transform = Some(transform.then_translate(Vector2D::new(to.x.0, to.y.0)));
194        } else {
195            self.transform = Some(Transform::translation(to.x.0, to.y.0))
196        }
197    }
198
199    /// Applies a scale to the path.
200    #[inline]
201    pub fn scale(&mut self, scale: f32) {
202        if let Some(transform) = self.transform {
203            self.transform = Some(transform.then_scale(scale, scale));
204        } else {
205            self.transform = Some(Transform::scale(scale, scale));
206        }
207    }
208
209    /// Applies a rotation to the path.
210    ///
211    /// The `angle` is in degrees value in the range 0.0 to 360.0.
212    #[inline]
213    pub fn rotate(&mut self, angle: f32) {
214        let radians = angle.to_radians();
215        if let Some(transform) = self.transform {
216            self.transform = Some(transform.then_rotate(Angle::radians(radians)));
217        } else {
218            self.transform = Some(Transform::rotation(Angle::radians(radians)));
219        }
220    }
221
222    /// Builds into a [`Path`].
223    #[inline]
224    pub fn build(self) -> Result<Path<Pixels>, Error> {
225        let path = if let Some(transform) = self.transform {
226            self.raw.build().transformed(&transform)
227        } else {
228            self.raw.build()
229        };
230
231        match self.style {
232            PathStyle::Stroke(options) => Self::tessellate_stroke(&path, &options),
233            PathStyle::Fill(options) => Self::tessellate_fill(&path, &options),
234        }
235    }
236
237    fn tessellate_fill(
238        path: &lyon::path::Path,
239        options: &FillOptions,
240    ) -> Result<Path<Pixels>, Error> {
241        // Will contain the result of the tessellation.
242        let mut buf: VertexBuffers<lyon::math::Point, u16> = VertexBuffers::new();
243        let mut tessellator = FillTessellator::new();
244
245        // Compute the tessellation.
246        tessellator.tessellate_path(
247            path,
248            options,
249            &mut BuffersBuilder::new(&mut buf, |vertex: FillVertex| vertex.position()),
250        )?;
251
252        Ok(Self::build_path(buf))
253    }
254
255    fn tessellate_stroke(
256        path: &lyon::path::Path,
257        options: &StrokeOptions,
258    ) -> Result<Path<Pixels>, Error> {
259        // Will contain the result of the tessellation.
260        let mut buf: VertexBuffers<lyon::math::Point, u16> = VertexBuffers::new();
261        let mut tessellator = StrokeTessellator::new();
262
263        // Compute the tessellation.
264        tessellator.tessellate_path(
265            path,
266            options,
267            &mut BuffersBuilder::new(&mut buf, |vertex: StrokeVertex| vertex.position()),
268        )?;
269
270        Ok(Self::build_path(buf))
271    }
272
273    /// Builds a [`Path`] from a [`lyon::VertexBuffers`].
274    pub fn build_path(buf: VertexBuffers<lyon::math::Point, u16>) -> Path<Pixels> {
275        if buf.vertices.is_empty() {
276            return Path::new(Point::default());
277        }
278
279        let first_point = buf.vertices[0];
280
281        let mut path = Path::new(first_point.into());
282        for i in 0..buf.indices.len() / 3 {
283            let i0 = buf.indices[i * 3] as usize;
284            let i1 = buf.indices[i * 3 + 1] as usize;
285            let i2 = buf.indices[i * 3 + 2] as usize;
286
287            let v0 = buf.vertices[i0];
288            let v1 = buf.vertices[i1];
289            let v2 = buf.vertices[i2];
290
291            path.push_triangle(
292                (v0.into(), v1.into(), v2.into()),
293                (point(0., 1.), point(0., 1.), point(0., 1.)),
294            );
295        }
296
297        path
298    }
299}