ressa_path/
path.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use alloc::vec::Vec;
8
9use crate::path_builder::PathBuilder;
10use crate::transform::Transform;
11use crate::{Point, Rect};
12
13/// A path verb.
14#[allow(missing_docs)]
15#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
16pub enum PathVerb {
17    Move,
18    Line,
19    Quad,
20    Cubic,
21    Close,
22}
23
24/// A Bezier path.
25///
26/// Can be created via [`PathBuilder`].
27/// Where [`PathBuilder`] can be created from the [`Path`] using [`clear`] to reuse the allocation.
28///
29/// Path is immutable and uses compact storage, where segment types and numbers are stored
30/// separately. Use can access path segments via [`Path::verbs`] and [`Path::points`],
31/// or via [`Path::segments`]
32///
33/// # Guarantees
34///
35/// - Has a valid, precomputed bounds.
36/// - All points are finite.
37/// - Has at least two segments.
38/// - Each contour starts with a MoveTo.
39/// - No duplicated Move.
40/// - No duplicated Close.
41/// - Zero-length contours are allowed.
42///
43/// [`PathBuilder`]: struct.PathBuilder.html
44/// [`clear`]: struct.Path.html#method.clear
45#[derive(Clone, PartialEq)]
46pub struct Path {
47    pub(crate) verbs: Vec<PathVerb>,
48    pub(crate) points: Vec<Point>,
49    pub(crate) bounds: Rect,
50}
51
52impl Path {
53    /// Returns the number of segments in the path.
54    pub fn len(&self) -> usize {
55        self.verbs.len()
56    }
57
58    /// Checks if path is empty.
59    pub fn is_empty(&self) -> bool {
60        self.len() == 0
61    }
62
63    /// Returns the bounds of the path's points.
64    ///
65    /// The value is already calculated.
66    pub fn bounds(&self) -> Rect {
67        self.bounds
68    }
69
70    /// Returns an internal vector of verbs.
71    pub fn verbs(&self) -> &[PathVerb] {
72        &self.verbs
73    }
74
75    /// Returns an internal vector of points.
76    pub fn points(&self) -> &[Point] {
77        &self.points
78    }
79
80    /// Returns a transformed in-place path.
81    ///
82    /// Some points may become NaN/inf therefore this method can fail.
83    pub fn transform(mut self, ts: Transform) -> Option<Self> {
84        if ts.is_identity() {
85            return Some(self);
86        }
87
88        ts.map_points(&mut self.points);
89
90        // Update bounds.
91        self.bounds = Rect::from_points(&self.points)?;
92
93        Some(self)
94    }
95
96    /// Returns an iterator over path's segments.
97    pub fn segments(&self) -> PathSegmentsIter {
98        PathSegmentsIter {
99            path: self,
100            verb_index: 0,
101            points_index: 0,
102            is_auto_close: false,
103            last_move_to: Point::zero(),
104            last_point: Point::zero(),
105        }
106    }
107
108    /// Clears the path and returns a `PathBuilder` that will reuse an allocated memory.
109    pub fn clear(mut self) -> PathBuilder {
110        self.verbs.clear();
111        self.points.clear();
112
113        PathBuilder {
114            verbs: self.verbs,
115            points: self.points,
116            last_move_to_index: 0,
117            move_to_required: true,
118        }
119    }
120}
121
122impl core::fmt::Debug for Path {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        use core::fmt::Write;
125
126        let mut s = alloc::string::String::new();
127        for segment in self.segments() {
128            match segment {
129                PathSegment::MoveTo(p) => s.write_fmt(format_args!("M {} {} ", p.x, p.y))?,
130                PathSegment::LineTo(p) => s.write_fmt(format_args!("L {} {} ", p.x, p.y))?,
131                PathSegment::QuadTo(p0, p1) => {
132                    s.write_fmt(format_args!("Q {} {} {} {} ", p0.x, p0.y, p1.x, p1.y))?
133                }
134                PathSegment::CubicTo(p0, p1, p2) => s.write_fmt(format_args!(
135                    "C {} {} {} {} {} {} ",
136                    p0.x, p0.y, p1.x, p1.y, p2.x, p2.y
137                ))?,
138                PathSegment::Close => s.write_fmt(format_args!("Z "))?,
139            }
140        }
141
142        s.pop(); // ' '
143
144        f.debug_struct("Path")
145            .field("segments", &s)
146            .field("bounds", &self.bounds)
147            .finish()
148    }
149}
150
151/// A path segment.
152#[allow(missing_docs)]
153#[derive(Copy, Clone, PartialEq, Debug)]
154pub enum PathSegment {
155    MoveTo(Point),
156    LineTo(Point),
157    QuadTo(Point, Point),
158    CubicTo(Point, Point, Point),
159    Close,
160}
161
162/// A path segments iterator.
163#[allow(missing_debug_implementations)]
164#[derive(Clone)]
165pub struct PathSegmentsIter<'a> {
166    path: &'a Path,
167    verb_index: usize,
168    points_index: usize,
169
170    is_auto_close: bool,
171    last_move_to: Point,
172    last_point: Point,
173}
174
175impl<'a> PathSegmentsIter<'a> {
176    /// Sets the auto closing mode. Off by default.
177    ///
178    /// When enabled, emits an additional `PathSegment::Line` from the current position
179    /// to the previous `PathSegment::Move`. And only then emits `PathSegment::Close`.
180    pub fn set_auto_close(&mut self, flag: bool) {
181        self.is_auto_close = flag;
182    }
183
184    pub(crate) fn auto_close(&mut self) -> PathSegment {
185        if self.is_auto_close && self.last_point != self.last_move_to {
186            self.verb_index -= 1;
187            PathSegment::LineTo(self.last_move_to)
188        } else {
189            PathSegment::Close
190        }
191    }
192
193    pub(crate) fn has_valid_tangent(&self) -> bool {
194        let mut iter = self.clone();
195        while let Some(segment) = iter.next() {
196            match segment {
197                PathSegment::MoveTo(_) => {
198                    return false;
199                }
200                PathSegment::LineTo(p) => {
201                    if iter.last_point == p {
202                        continue;
203                    }
204
205                    return true;
206                }
207                PathSegment::QuadTo(p1, p2) => {
208                    if iter.last_point == p1 && iter.last_point == p2 {
209                        continue;
210                    }
211
212                    return true;
213                }
214                PathSegment::CubicTo(p1, p2, p3) => {
215                    if iter.last_point == p1 && iter.last_point == p2 && iter.last_point == p3 {
216                        continue;
217                    }
218
219                    return true;
220                }
221                PathSegment::Close => {
222                    return false;
223                }
224            }
225        }
226
227        false
228    }
229
230    /// Returns the current verb.
231    pub fn curr_verb(&self) -> PathVerb {
232        self.path.verbs[self.verb_index - 1]
233    }
234
235    /// Returns the next verb.
236    pub fn next_verb(&self) -> Option<PathVerb> {
237        self.path.verbs.get(self.verb_index).cloned()
238    }
239}
240
241impl<'a> Iterator for PathSegmentsIter<'a> {
242    type Item = PathSegment;
243
244    fn next(&mut self) -> Option<Self::Item> {
245        if self.verb_index < self.path.verbs.len() {
246            let verb = self.path.verbs[self.verb_index];
247            self.verb_index += 1;
248
249            match verb {
250                PathVerb::Move => {
251                    self.points_index += 1;
252                    self.last_move_to = self.path.points[self.points_index - 1];
253                    self.last_point = self.last_move_to;
254                    Some(PathSegment::MoveTo(self.last_move_to))
255                }
256                PathVerb::Line => {
257                    self.points_index += 1;
258                    self.last_point = self.path.points[self.points_index - 1];
259                    Some(PathSegment::LineTo(self.last_point))
260                }
261                PathVerb::Quad => {
262                    self.points_index += 2;
263                    self.last_point = self.path.points[self.points_index - 1];
264                    Some(PathSegment::QuadTo(
265                        self.path.points[self.points_index - 2],
266                        self.last_point,
267                    ))
268                }
269                PathVerb::Cubic => {
270                    self.points_index += 3;
271                    self.last_point = self.path.points[self.points_index - 1];
272                    Some(PathSegment::CubicTo(
273                        self.path.points[self.points_index - 3],
274                        self.path.points[self.points_index - 2],
275                        self.last_point,
276                    ))
277                }
278                PathVerb::Close => {
279                    let seg = self.auto_close();
280                    self.last_point = self.last_move_to;
281                    Some(seg)
282                }
283            }
284        } else {
285            None
286        }
287    }
288}