1use lyon_geom::Angle;
2use lyon_geom::Arc;
3use lyon_geom::CubicBezierSegment;
4use lyon_geom::QuadraticBezierSegment;
5
6use crate::{Point, Transform, Vector};
7
8#[derive(Clone, Copy, PartialEq, Debug)]
9pub enum Winding {
10 EvenOdd,
11 NonZero,
12}
13
14#[derive(Clone, Copy, Debug)]
15pub enum PathOp {
16 MoveTo(Point),
17 LineTo(Point),
18 QuadTo(Point, Point),
19 CubicTo(Point, Point, Point),
20 Close,
21}
22
23impl PathOp {
24 fn transform(self, xform: &Transform) -> PathOp {
25 match self {
26 PathOp::MoveTo(p) => PathOp::MoveTo(xform.transform_point(p)),
27 PathOp::LineTo(p) => PathOp::LineTo(xform.transform_point(p)),
28 PathOp::QuadTo(p1, p2) => PathOp::QuadTo(
29 xform.transform_point(p1),
30 xform.transform_point(p2)
31 ),
32 PathOp::CubicTo(p1, p2, p3) => PathOp::CubicTo(
33 xform.transform_point(p1),
34 xform.transform_point(p2),
35 xform.transform_point(p3),
36 ),
37 PathOp::Close => PathOp::Close,
38 }
39 }
40}
41
42#[derive(Clone, Debug)]
44pub struct Path {
45 pub ops: Vec<PathOp>,
46 pub winding: Winding,
47}
48
49impl Path {
50 pub fn flatten(&self, tolerance: f32) -> Path {
54 let mut cur_pt = None;
55 let mut flattened = Path { ops: Vec::new(), winding: Winding::NonZero };
56 for op in &self.ops {
57 match *op {
58 PathOp::MoveTo(pt) | PathOp::LineTo(pt) => {
59 cur_pt = Some(pt);
60 flattened.ops.push(op.clone())
61 }
62 PathOp::Close => {
63 cur_pt = None;
64 flattened.ops.push(op.clone())
65 }
66 PathOp::QuadTo(cpt, pt) => {
67 let start = cur_pt.unwrap_or(cpt);
68 let c = QuadraticBezierSegment {
69 from: start,
70 ctrl: cpt,
71 to: pt,
72 };
73 for l in c.flattened(tolerance) {
74 flattened.ops.push(PathOp::LineTo(l));
75 }
76 cur_pt = Some(pt);
77 }
78 PathOp::CubicTo(cpt1, cpt2, pt) => {
79 let start = cur_pt.unwrap_or(cpt1);
80 let c = CubicBezierSegment {
81 from: start,
82 ctrl1: cpt1,
83 ctrl2: cpt2,
84 to: pt,
85 };
86 for l in c.flattened(tolerance) {
87 flattened.ops.push(PathOp::LineTo(l));
88 }
89 cur_pt = Some(pt);
90 }
91 }
92 }
93 flattened
94 }
95
96 pub fn contains_point(&self, tolerance: f32, x: f32, y: f32) -> bool {
101 let flat_path = self.flatten(tolerance);
103 struct WindState {
104 first_point: Option<Point>,
105 current_point: Option<Point>,
106 count: i32,
107 on_edge: bool,
108
109 x: f32,
110 y: f32,
111 }
112
113 impl WindState {
114 fn close(&mut self) {
115 if let (Some(first_point), Some(current_point)) = (self.first_point, self.current_point) {
116 self.add_edge(
117 current_point,
118 first_point,
119 );
120 }
121 self.first_point = None;
122 }
123
124 fn add_edge(&mut self, p1: Point, p2: Point) {
126 let (x1, y1) = (p1.x, p1.y);
127 let (x2, y2) = (p2.x, p2.y);
128
129 let dir = if y1 < y2 { -1 } else { 1 };
130
131 if x1 > self.x && x2 > self.x {
133 return
134 }
135
136 if y1 > self.y && y2 > self.y {
138 return
139 }
140
141 if y1 < self.y && y2 < self.y {
143 return
144 }
145
146 if x1 < self.x && x2 < self.x {
148 if y1 > self.y && y2 < self.y {
149 self.count += 1;
150 return;
151 }
152 if y2 > self.y && y1 < self.y {
153 self.count -= 1;
154 return;
155 }
156 }
157
158 let dx = x2 - x1;
159 let dy = y2 - y1;
160
161 let cross = dx * (self.y - y1) - dy * (self.x - x1);
163
164 if cross == 0. {
165 self.on_edge = true;
166 } else if (cross > 0. && dir > 0) || (cross < 0. && dir < 0) {
167 self.count += dir;
168 }
169 }
170 }
171
172 let mut ws = WindState { count: 0, first_point: None, current_point: None, x, y, on_edge: false};
173
174 for op in &flat_path.ops {
175 match *op {
176 PathOp::MoveTo(pt) => {
177 ws.close();
178 ws.current_point = Some(pt);
179 ws.first_point = Some(pt);
180 },
181 PathOp::LineTo(pt) => {
182 if let Some(current_point) = ws.current_point {
183 ws.add_edge(current_point, pt);
184 } else {
185 ws.first_point = Some(pt);
186 }
187 ws.current_point = Some(pt);
188 },
189 PathOp::QuadTo(..) |
190 PathOp::CubicTo(..) => panic!(),
191 PathOp::Close => ws.close(),
192 }
193 }
194 ws.close();
196
197 let inside = match self.winding {
198 Winding::EvenOdd => ws.count & 1 != 0,
199 Winding::NonZero => ws.count != 0,
200 };
201 inside || ws.on_edge
202 }
203
204 pub fn transform(self, transform: &Transform) -> Path {
205 let Path { ops, winding } = self;
206 let ops = ops.into_iter().map(|op| op.transform(transform)).collect();
207 Path { ops, winding }
208 }
209}
210
211pub struct PathBuilder {
213 path: Path,
214}
215
216impl From<Path> for PathBuilder {
217 fn from(path: Path) -> Self {
218 PathBuilder {
219 path
220 }
221 }
222}
223
224impl PathBuilder {
225 pub fn new() -> PathBuilder {
226 PathBuilder {
227 path: Path {
228 ops: Vec::new(),
229 winding: Winding::NonZero,
230 },
231 }
232 }
233
234 pub fn move_to(&mut self, x: f32, y: f32) {
236 self.path.ops.push(PathOp::MoveTo(Point::new(x, y)))
237 }
238
239 pub fn line_to(&mut self, x: f32, y: f32) {
241 self.path.ops.push(PathOp::LineTo(Point::new(x, y)))
242 }
243
244 pub fn quad_to(&mut self, cx: f32, cy: f32, x: f32, y: f32) {
247 self.path
248 .ops
249 .push(PathOp::QuadTo(Point::new(cx, cy), Point::new(x, y)))
250 }
251
252 pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
254 self.move_to(x, y);
255 self.line_to(x + width, y);
256 self.line_to(x + width, y + height);
257 self.line_to(x, y + height);
258 self.close();
259 }
260
261 pub fn cubic_to(&mut self, cx1: f32, cy1: f32, cx2: f32, cy2: f32, x: f32, y: f32) {
264 self.path.ops.push(PathOp::CubicTo(
265 Point::new(cx1, cy1),
266 Point::new(cx2, cy2),
267 Point::new(x, y),
268 ))
269 }
270
271 pub fn close(&mut self) {
273 self.path.ops.push(PathOp::Close)
274 }
275
276
277 pub fn arc(&mut self, x: f32, y: f32, r: f32, start_angle: f32, sweep_angle: f32) {
282 let a: Arc<f32> = Arc {
284 center: Point::new(x, y),
285 radii: Vector::new(r, r),
286 start_angle: Angle::radians(start_angle),
287 sweep_angle: Angle::radians(sweep_angle),
288 x_rotation: Angle::zero(),
289 };
290 let start = a.from();
291 self.line_to(start.x, start.y);
292 a.for_each_quadratic_bezier(&mut |q| {
293 self.quad_to(q.ctrl.x, q.ctrl.y, q.to.x, q.to.y);
294 });
295 }
296
297 pub fn finish(self) -> Path {
299 self.path
300 }
301}