1use crate::OutlinePoint;
2use makepad_geometry::{Point, Transform, Transformation};
3use makepad_internal_iter::{
4 ExtendFromInternalIterator, InternalIterator, IntoInternalIterator,
5};
6use makepad_path::PathCommand;
7use std::iter::Cloned;
8use std::slice::Iter;
9
10#[derive(Clone, Debug, Default, PartialEq)]
15pub struct Outline {
16 contour_ends: Vec<usize>,
17 points: Vec<OutlinePoint>,
18}
19
20impl Outline {
21 pub fn new() -> Outline {
23 Outline::default()
24 }
25
26 pub fn contours(&self) -> Contours {
28 Contours {
29 contour_start: 0,
30 contour_ends: self.contour_ends.iter().cloned(),
31 points: &self.points,
32 }
33 }
34
35 pub fn points(&self) -> &[OutlinePoint] {
37 &self.points
38 }
39
40 pub fn commands(&self) -> Commands {
42 Commands {
43 contours: self.contours(),
44 }
45 }
46
47 pub fn points_mut(&mut self) -> &mut [OutlinePoint] {
49 &mut self.points
50 }
51
52 pub fn begin_contour(&mut self) -> ContourBuilder {
54 ContourBuilder {
55 contour_ends: &mut self.contour_ends,
56 points: &mut self.points,
57 }
58 }
59}
60
61impl<'a> ExtendFromInternalIterator<Contour<'a>> for Outline {
62 fn extend_from_internal_iter<I>(&mut self, internal_iter: I)
63 where
64 I: IntoInternalIterator<Item = Contour<'a>>,
65 {
66 internal_iter
67 .into_internal_iter()
68 .for_each(&mut |other_contour| {
69 let mut contour = self.begin_contour();
70 contour.extend_from_internal_iter(other_contour.points().iter().cloned());
71 contour.end();
72 true
73 });
74 }
75}
76
77impl Transform for Outline {
78 fn transform<T>(mut self, t: &T) -> Outline
79 where
80 T: Transformation,
81 {
82 self.transform_mut(t);
83 self
84 }
85
86 fn transform_mut<T>(&mut self, t: &T)
87 where
88 T: Transformation,
89 {
90 for point in self.points_mut() {
91 point.transform_mut(t);
92 }
93 }
94}
95
96#[derive(Clone, Debug)]
98pub struct Contours<'a> {
99 contour_start: usize,
100 contour_ends: Cloned<Iter<'a, usize>>,
101 points: &'a [OutlinePoint],
102}
103
104impl<'a> Iterator for Contours<'a> {
105 type Item = Contour<'a>;
106
107 fn next(&mut self) -> Option<Contour<'a>> {
108 self.contour_ends.next().map(|contour_end| {
109 let contour_start = self.contour_start;
110 self.contour_start = contour_end;
111 Contour {
112 points: &self.points[contour_start..contour_end],
113 }
114 })
115 }
116}
117
118#[derive(Clone, Copy, Debug)]
120pub struct Contour<'a> {
121 points: &'a [OutlinePoint],
122}
123
124impl<'a> Contour<'a> {
125 pub fn points(&self) -> &'a [OutlinePoint] {
126 &self.points
127 }
128}
129
130#[derive(Clone, Debug)]
132pub struct Commands<'a> {
133 contours: Contours<'a>,
134}
135
136impl<'a> InternalIterator for Commands<'a> {
137 type Item = PathCommand;
138
139 fn for_each<F>(self, f: &mut F) -> bool
140 where
141 F: FnMut(PathCommand) -> bool,
142 {
143 for contour in self.contours {
147 let mut first_off_curve_point: Option<Point> = None;
149 let mut first_on_curve_point: Option<Point> = None;
151 let mut last_off_curve_point: Option<Point> = None;
153 for point in contour.points() {
154 if first_on_curve_point.is_none() {
155 if point.is_on_curve {
156 if !f(PathCommand::MoveTo(point.point)) {
157 return false;
158 }
159 first_on_curve_point = Some(point.point);
160 } else {
161 if let Some(first_off_curve_point) = first_off_curve_point {
162 let midpoint = first_off_curve_point.lerp(point.point, 0.5);
163 if !f(PathCommand::MoveTo(midpoint)) {
164 return false;
165 }
166 first_on_curve_point = Some(midpoint);
167 last_off_curve_point = Some(point.point);
168 } else {
169 first_off_curve_point = Some(point.point);
170 }
171 }
172 } else {
173 match (last_off_curve_point, point.is_on_curve) {
174 (None, false) => {
175 last_off_curve_point = Some(point.point);
176 }
177 (None, true) => {
178 if !f(PathCommand::LineTo(point.point)) {
179 return false;
180 }
181 }
182 (Some(last_point), false) => {
183 if !f(PathCommand::QuadraticTo(
184 last_point,
185 last_point.lerp(point.point, 0.5),
186 )) {
187 return false;
188 }
189 last_off_curve_point = Some(point.point);
190 }
191 (Some(last_point), true) => {
192 if !f(PathCommand::QuadraticTo(last_point, point.point)) {
193 return false;
194 }
195 last_off_curve_point = None;
196 }
197 }
198 }
199 }
200 if let Some(first_on_curve_point) = first_on_curve_point {
201 match (last_off_curve_point, first_off_curve_point) {
202 (None, None) => {
203 if !f(PathCommand::LineTo(first_on_curve_point)) {
204 return false;
205 }
206 }
207 (None, Some(first_off_curve_point)) => {
208 if !f(PathCommand::QuadraticTo(
209 first_off_curve_point,
210 first_on_curve_point,
211 )) {
212 return false;
213 }
214 }
215 (Some(last_point), None) => {
216 if !f(PathCommand::QuadraticTo(last_point, first_on_curve_point)) {
217 return false;
218 }
219 }
220 (Some(last_point), Some(first_off_curve_point)) => {
221 let midpoint = last_point.lerp(first_off_curve_point, 0.5);
222 if !f(PathCommand::QuadraticTo(last_point, midpoint)) {
223 return false;
224 }
225 if !f(PathCommand::QuadraticTo(
226 first_off_curve_point,
227 first_on_curve_point,
228 )) {
229 return false;
230 }
231 }
232 }
233 if !f(PathCommand::Close) {
234 return false;
235 }
236 }
237 }
238 true
239 }
240}
241
242#[derive(Debug)]
243pub struct ContourBuilder<'a> {
244 contour_ends: &'a mut Vec<usize>,
245 points: &'a mut Vec<OutlinePoint>,
246}
247
248impl<'a> ContourBuilder<'a> {
249 pub fn end(self) {}
250
251 pub fn push(&mut self, point: OutlinePoint) {
252 self.points.push(point);
253 }
254}
255
256impl<'a> Drop for ContourBuilder<'a> {
257 fn drop(&mut self) {
258 if self.points.len() != self.contour_ends.last().cloned().unwrap_or(0) {
259 self.contour_ends.push(self.points.len());
260 }
261 }
262}
263
264impl<'a> ExtendFromInternalIterator<OutlinePoint> for ContourBuilder<'a> {
265 fn extend_from_internal_iter<I>(&mut self, internal_iter: I)
266 where
267 I: IntoInternalIterator<Item = OutlinePoint>,
268 {
269 internal_iter.into_internal_iter().for_each(&mut |point| {
270 self.push(point);
271 true
272 });
273 }
274}