1use alloc::vec::Vec;
8
9use crate::path_builder::PathBuilder;
10use crate::transform::Transform;
11use crate::{Point, Rect};
12
13#[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#[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 pub fn len(&self) -> usize {
55 self.verbs.len()
56 }
57
58 pub fn is_empty(&self) -> bool {
60 self.len() == 0
61 }
62
63 pub fn bounds(&self) -> Rect {
67 self.bounds
68 }
69
70 pub fn verbs(&self) -> &[PathVerb] {
72 &self.verbs
73 }
74
75 pub fn points(&self) -> &[Point] {
77 &self.points
78 }
79
80 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 self.bounds = Rect::from_points(&self.points)?;
92
93 Some(self)
94 }
95
96 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 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(); f.debug_struct("Path")
145 .field("segments", &s)
146 .field("bounds", &self.bounds)
147 .finish()
148 }
149}
150
151#[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#[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 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 pub fn curr_verb(&self) -> PathVerb {
232 self.path.verbs[self.verb_index - 1]
233 }
234
235 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}