esvg/
path.rs

1//! Helpers for handling Path data
2use crate::Element;
3use ::polygonical::point::Point;
4use ::polygonical::polygon::Polygon;
5
6/// Represents the data attribute of a svg path
7pub struct Data {
8    segments: Vec<String>,
9}
10
11/// Create a data attribute from a series of points
12pub fn create(points: &[Point]) -> Element {
13    Data::from_points(points).to_path()
14}
15
16/// Create a closed loop data attribute from a series of points
17pub fn create_closed(points: &[Point]) -> Element {
18    Data::from_points(points).close().to_path()
19}
20
21/// Create a closed loop data attribute from a polygon
22pub fn create_polygon(poly: &Polygon) -> Element {
23    Data::from_points(&poly.points).close().to_path()
24}
25
26impl Data {
27    /// a new empty data element
28    pub fn new() -> Self {
29        Data { segments: vec![] }
30    }
31
32    /// Create the element content from a series of points
33    /// Note: there must be more than one point for this to do anything.
34    pub fn from_points(points: &[Point]) -> Self {
35        let mut data = Data::new();
36        if points.len() > 1 {
37            data.move_to(points[0]);
38            for p in points[1..].iter() {
39                data.line_to(*p);
40            }
41        }
42
43        data
44    }
45
46    /// Add a Move To step to this path.
47    pub fn move_to(&mut self, p: Point) -> &mut Data {
48        self.segments.push(format!("M{:.3} {:.3}", p.x, p.y));
49
50        self
51    }
52
53    /// Add a line to step to this path
54    pub fn line_to(&mut self, p: Point) -> &mut Data {
55        self.segments.push(format!("L{:.3} {:.3}", p.x, p.y));
56
57        self
58    }
59
60    /// Add an arc to step to this path
61    pub fn arc_to<RX, RY, ROT>(
62        &mut self,
63        p: Point,
64        rx: RX,
65        ry: RY,
66        rotation: ROT,
67        large: bool,
68        sweep: bool,
69    ) -> &mut Data
70    where
71        RX: std::fmt::Display,
72        RY: std::fmt::Display,
73        ROT: std::fmt::Display,
74    {
75        let lv = match large {
76            true => 1,
77            false => 0,
78        };
79
80        let sv = match sweep {
81            true => 1,
82            false => 0,
83        };
84
85        self.segments.push(format!(
86            "A{} {} {:.3} {} {} {:.3} {:.3}",
87            rx, ry, rotation, lv, sv, p.x, p.y
88        ));
89
90        self
91    }
92
93    /// Close the loop of this path.
94    pub fn close(&mut self) -> &mut Data {
95        self.segments.push("z".to_string());
96        self
97    }
98
99    /// Turn this data attribute into a string.
100    pub fn build(&self) -> String {
101        self.segments.join(" ")
102    }
103
104    /// build a path element using this data attribute
105    pub fn to_path(&self) -> Element {
106        let mut el = Element::new("path");
107        el.set("fill", "none");
108        el.set("d", self.build());
109        el
110    }
111}
112
113impl Default for Data {
114    fn default() -> Self {
115        Self::new()
116    }
117}