Skip to main content

femto_mesh/
path.rs

1use crate::style::{FillStyle, StrokeStyle, Style};
2
3use data_stream::{
4    FromStream, ToStream, default_settings::PortableSettings, from_stream, numbers::EndianSettings,
5    to_stream,
6};
7use femto_g::{Canvas, FillRule, Path, Renderer};
8use ga2::Vector;
9
10use std::io::{Error, ErrorKind, Read, Result, Write};
11
12#[derive(Copy, Clone, Debug)]
13enum IndexedVerb {
14    MoveTo(usize),
15    LineTo(usize),
16    QuadTo(usize, usize),
17    BezierTo(usize, usize, usize),
18    Close,
19}
20
21impl ToStream<PortableSettings> for IndexedVerb {
22    fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
23        use IndexedVerb::*;
24        match self {
25            MoveTo(index) => {
26                to_stream::<PortableSettings, _, _>(&0u8, stream)?;
27                to_stream::<PortableSettings, _, _>(index, stream)
28            }
29            LineTo(index) => {
30                to_stream::<PortableSettings, _, _>(&1u8, stream)?;
31                to_stream::<PortableSettings, _, _>(index, stream)
32            }
33            QuadTo(index1, index2) => {
34                to_stream::<PortableSettings, _, _>(&2u8, stream)?;
35                to_stream::<PortableSettings, _, _>(index1, stream)?;
36                to_stream::<PortableSettings, _, _>(index2, stream)
37            }
38            BezierTo(index1, index2, index3) => {
39                to_stream::<PortableSettings, _, _>(&3u8, stream)?;
40                to_stream::<PortableSettings, _, _>(index1, stream)?;
41                to_stream::<PortableSettings, _, _>(index2, stream)?;
42                to_stream::<PortableSettings, _, _>(index3, stream)
43            }
44            Close => to_stream::<PortableSettings, _, _>(&4u8, stream),
45        }
46    }
47}
48
49impl FromStream<PortableSettings> for IndexedVerb {
50    fn from_stream<R: Read>(stream: &mut R) -> Result<Self> {
51        let verb_kind: u8 = from_stream::<PortableSettings, _, _>(stream)?;
52
53        use IndexedVerb::*;
54        Ok(match verb_kind {
55            0 => MoveTo(from_stream::<PortableSettings, _, _>(stream)?),
56            1 => LineTo(from_stream::<PortableSettings, _, _>(stream)?),
57            2 => QuadTo(
58                from_stream::<PortableSettings, _, _>(stream)?,
59                from_stream::<PortableSettings, _, _>(stream)?,
60            ),
61            3 => BezierTo(
62                from_stream::<PortableSettings, _, _>(stream)?,
63                from_stream::<PortableSettings, _, _>(stream)?,
64                from_stream::<PortableSettings, _, _>(stream)?,
65            ),
66            4 => Close,
67            _ => {
68                return Err(Error::new(
69                    ErrorKind::InvalidData,
70                    "Trying to read invalid enum variant",
71                ));
72            }
73        })
74    }
75}
76
77/// An indexed path.
78///
79/// Used to represent different vector graphics elements using indices to a point list.
80/// Each path also specifies its own render style.
81#[derive(Clone)]
82pub struct IndexedPath {
83    verbs: Vec<IndexedVerb>,
84    style: Style,
85}
86
87impl IndexedPath {
88    /// Creates a stroke only path.
89    pub fn stroke(style: StrokeStyle) -> Self {
90        Self {
91            verbs: Vec::new(),
92            style: Style::Stroke(style),
93        }
94    }
95
96    /// Creates a fill only path.
97    pub fn fill(style: FillStyle) -> Self {
98        Self {
99            verbs: Vec::new(),
100            style: Style::Fill(style),
101        }
102    }
103
104    /// Creates a filled path.
105    pub fn new(fill: FillStyle, stroke: StrokeStyle) -> Self {
106        Self {
107            verbs: Vec::new(),
108            style: Style::Colored { fill, stroke },
109        }
110    }
111
112    /// Move the cursor to the position at `index`.
113    pub fn move_to(&mut self, index: usize) {
114        self.verbs.push(IndexedVerb::MoveTo(index));
115    }
116
117    /// Draw a line to the position at `index`.
118    pub fn line_to(&mut self, index: usize) {
119        self.verbs.push(IndexedVerb::LineTo(index));
120    }
121
122    /// Draw a quadratic curve along the position at `index1` to the position at `index2`.
123    pub fn quad_to(&mut self, index1: usize, index2: usize) {
124        self.verbs.push(IndexedVerb::QuadTo(index1, index2));
125    }
126
127    /// Draw a quadratic curve along the positions at `index1` and `index2` to the position at `index3`.
128    pub fn bezier_to(&mut self, index1: usize, index2: usize, index3: usize) {
129        self.verbs
130            .push(IndexedVerb::BezierTo(index1, index2, index3));
131    }
132
133    /// Close the current path.
134    pub fn close(&mut self) {
135        self.verbs.push(IndexedVerb::Close);
136    }
137
138    /// Render the indexed path.
139    pub fn render<R: Renderer>(&self, canvas: &mut Canvas<R>, points: &[Vector<f32>]) {
140        let mut path = Path::new();
141        for verb in &self.verbs {
142            use IndexedVerb::*;
143            match *verb {
144                MoveTo(index) => {
145                    let point = points[index];
146                    path.move_to([point.x, point.y]);
147                }
148                LineTo(index) => {
149                    let point = points[index];
150                    path.line_to([point.x, point.y]);
151                }
152                QuadTo(index1, index2) => {
153                    let point1 = points[index1];
154                    let point2 = points[index2];
155                    path.quad_to([point1.x, point1.y], [point2.x, point2.y]);
156                }
157                BezierTo(index1, index2, index3) => {
158                    let point1 = points[index1];
159                    let point2 = points[index2];
160                    let point3 = points[index3];
161                    path.bezier_to(
162                        [point1.x, point1.y],
163                        [point2.x, point2.y],
164                        [point3.x, point3.y],
165                    );
166                }
167                Close => path.close(),
168            }
169        }
170
171        use Style::*;
172        match &self.style {
173            Stroke(style) => {
174                canvas.stroke_path(&path, &style.to_paint(), &style.to_stroke_settings())
175            }
176            Fill(style) => canvas.fill_path(&path, &style.to_paint(), FillRule::NonZero),
177            Colored { fill, stroke } => {
178                canvas.fill_path(&path, &fill.to_paint(), FillRule::NonZero);
179                canvas.stroke_path(&path, &stroke.to_paint(), &stroke.to_stroke_settings());
180            }
181        }
182    }
183}
184
185impl<S: EndianSettings> ToStream<S> for IndexedPath {
186    fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
187        to_stream(&self.verbs, stream)?;
188        to_stream::<S, _, _>(&self.style, stream)
189    }
190}
191
192impl<S: EndianSettings> FromStream<S> for IndexedPath {
193    fn from_stream<R: Read>(stream: &mut R) -> Result<Self> {
194        Ok(Self {
195            verbs: from_stream(stream)?,
196            style: from_stream::<S, _, _>(stream)?,
197        })
198    }
199}