diff options
Diffstat (limited to 'graphics/src/widget/canvas/path')
| -rw-r--r-- | graphics/src/widget/canvas/path/arc.rs | 44 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/path/builder.rs | 180 | 
2 files changed, 224 insertions, 0 deletions
diff --git a/graphics/src/widget/canvas/path/arc.rs b/graphics/src/widget/canvas/path/arc.rs new file mode 100644 index 00000000..343191f1 --- /dev/null +++ b/graphics/src/widget/canvas/path/arc.rs @@ -0,0 +1,44 @@ +//! Build and draw curves. +use iced_native::{Point, Vector}; + +/// A segment of a differentiable curve. +#[derive(Debug, Clone, Copy)] +pub struct Arc { +    /// The center of the arc. +    pub center: Point, +    /// The radius of the arc. +    pub radius: f32, +    /// The start of the segment's angle, clockwise rotation. +    pub start_angle: f32, +    /// The end of the segment's angle, clockwise rotation. +    pub end_angle: f32, +} + +/// An elliptical [`Arc`]. +/// +/// [`Arc`]: struct.Arc.html +#[derive(Debug, Clone, Copy)] +pub struct Elliptical { +    /// The center of the arc. +    pub center: Point, +    /// The radii of the arc's ellipse, defining its axes. +    pub radii: Vector, +    /// The rotation of the arc's ellipse. +    pub rotation: f32, +    /// The start of the segment's angle, clockwise rotation. +    pub start_angle: f32, +    /// The end of the segment's angle, clockwise rotation. +    pub end_angle: f32, +} + +impl From<Arc> for Elliptical { +    fn from(arc: Arc) -> Elliptical { +        Elliptical { +            center: arc.center, +            radii: Vector::new(arc.radius, arc.radius), +            rotation: 0.0, +            start_angle: arc.start_angle, +            end_angle: arc.end_angle, +        } +    } +} diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs new file mode 100644 index 00000000..6511fa52 --- /dev/null +++ b/graphics/src/widget/canvas/path/builder.rs @@ -0,0 +1,180 @@ +use crate::canvas::path::{arc, Arc, Path}; + +use iced_native::{Point, Size}; +use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder}; + +/// A [`Path`] builder. +/// +/// Once a [`Path`] is built, it can no longer be mutated. +/// +/// [`Path`]: struct.Path.html +#[allow(missing_debug_implementations)] +pub struct Builder { +    raw: lyon::path::builder::SvgPathBuilder<lyon::path::Builder>, +} + +impl Builder { +    /// Creates a new [`Builder`]. +    /// +    /// [`Builder`]: struct.Builder.html +    pub fn new() -> Builder { +        Builder { +            raw: lyon::path::Path::builder().with_svg(), +        } +    } + +    /// Moves the starting point of a new sub-path to the given `Point`. +    #[inline] +    pub fn move_to(&mut self, point: Point) { +        let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y)); +    } + +    /// Connects the last point in the [`Path`] to the given `Point` with a +    /// straight line. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn line_to(&mut self, point: Point) { +        let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); +    } + +    /// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in +    /// a clockwise direction. +    /// +    /// [`Arc`]: struct.Arc.html +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn arc(&mut self, arc: Arc) { +        self.ellipse(arc.into()); +    } + +    /// Adds a circular arc to the [`Path`] with the given control points and +    /// radius. +    /// +    /// The arc is connected to the previous point by a straight line, if +    /// necessary. +    /// +    /// [`Path`]: struct.Path.html +    pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { +        use lyon::{math, path}; + +        let a = math::Point::new(a.x, a.y); + +        if self.raw.current_position() != a { +            let _ = self.raw.line_to(a); +        } + +        let _ = self.raw.arc_to( +            math::Vector::new(radius, radius), +            math::Angle::radians(0.0), +            path::ArcFlags::default(), +            math::Point::new(b.x, b.y), +        ); +    } + +    /// Adds an [`Ellipse`] to the [`Path`] using a clockwise direction. +    /// +    /// [`Ellipse`]: struct.Arc.html +    /// [`Path`]: struct.Path.html +    pub fn ellipse(&mut self, arc: arc::Elliptical) { +        use lyon::{geom, math}; + +        let arc = geom::Arc { +            center: math::Point::new(arc.center.x, arc.center.y), +            radii: math::Vector::new(arc.radii.x, arc.radii.y), +            x_rotation: math::Angle::radians(arc.rotation), +            start_angle: math::Angle::radians(arc.start_angle), +            sweep_angle: math::Angle::radians(arc.end_angle), +        }; + +        let _ = self.raw.move_to(arc.sample(0.0)); + +        arc.for_each_quadratic_bezier(&mut |curve| { +            let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to); +        }); +    } + +    /// Adds a cubic Bézier curve to the [`Path`] given its two control points +    /// and its end point. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn bezier_curve_to( +        &mut self, +        control_a: Point, +        control_b: Point, +        to: Point, +    ) { +        use lyon::math; + +        let _ = self.raw.cubic_bezier_to( +            math::Point::new(control_a.x, control_a.y), +            math::Point::new(control_b.x, control_b.y), +            math::Point::new(to.x, to.y), +        ); +    } + +    /// Adds a quadratic Bézier curve to the [`Path`] given its control point +    /// and its end point. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn quadratic_curve_to(&mut self, control: Point, to: Point) { +        use lyon::math; + +        let _ = self.raw.quadratic_bezier_to( +            math::Point::new(control.x, control.y), +            math::Point::new(to.x, to.y), +        ); +    } + +    /// Adds a rectangle to the [`Path`] given its top-left corner coordinate +    /// and its `Size`. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn rectangle(&mut self, top_left: Point, size: Size) { +        self.move_to(top_left); +        self.line_to(Point::new(top_left.x + size.width, top_left.y)); +        self.line_to(Point::new( +            top_left.x + size.width, +            top_left.y + size.height, +        )); +        self.line_to(Point::new(top_left.x, top_left.y + size.height)); +        self.close(); +    } + +    /// Adds a circle to the [`Path`] given its center coordinate and its +    /// radius. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn circle(&mut self, center: Point, radius: f32) { +        self.arc(Arc { +            center, +            radius, +            start_angle: 0.0, +            end_angle: 2.0 * std::f32::consts::PI, +        }); +    } + +    /// Closes the current sub-path in the [`Path`] with a straight line to +    /// the starting point. +    /// +    /// [`Path`]: struct.Path.html +    #[inline] +    pub fn close(&mut self) { +        self.raw.close() +    } + +    /// Builds the [`Path`] of this [`Builder`]. +    /// +    /// [`Path`]: struct.Path.html +    /// [`Builder`]: struct.Builder.html +    #[inline] +    pub fn build(self) -> Path { +        Path { +            raw: self.raw.build(), +        } +    } +}  | 
