1use easygpu_lyon::lyon_tessellation::path::PathEvent as LyonPathEvent;
2use easygpu_lyon::lyon_tessellation::{self};
3use figures::{Displayable, Points};
4
5use super::lyon_point;
6use crate::math::{Pixels, Point, Scale, Scaled};
7use crate::scene::Target;
8use crate::shape::{Fill, Stroke};
9use crate::Error;
10
11pub type Endpoint<S> = Point<f32, S>;
13pub type ControlPoint<S> = Point<f32, S>;
15
16#[derive(Debug, Clone, Copy)]
18pub enum PathEvent<S> {
19 Begin {
21 at: Endpoint<S>,
23 },
24 Line {
26 from: Endpoint<S>,
28 to: Endpoint<S>,
30 },
31 Quadratic {
33 from: Endpoint<S>,
35 ctrl: ControlPoint<S>,
37 to: Endpoint<S>,
39 },
40 Cubic {
42 from: Endpoint<S>,
44 ctrl1: ControlPoint<S>,
46 ctrl2: ControlPoint<S>,
48 to: Endpoint<S>,
50 },
51 End {
53 last: Endpoint<S>,
55 first: Endpoint<S>,
57 close: bool,
59 },
60}
61
62impl From<PathEvent<Pixels>> for LyonPathEvent {
63 fn from(event: PathEvent<Pixels>) -> Self {
64 match event {
65 PathEvent::Begin { at } => Self::Begin { at: lyon_point(at) },
66 PathEvent::Line { from, to } => Self::Line {
67 from: lyon_point(from),
68 to: lyon_point(to),
69 },
70 PathEvent::Quadratic { from, ctrl, to } => Self::Quadratic {
71 from: lyon_point(from),
72 ctrl: lyon_point(ctrl),
73 to: lyon_point(to),
74 },
75 PathEvent::Cubic {
76 from,
77 ctrl1,
78 ctrl2,
79 to,
80 } => Self::Cubic {
81 from: lyon_point(from),
82 ctrl1: lyon_point(ctrl1),
83 ctrl2: lyon_point(ctrl2),
84 to: lyon_point(to),
85 },
86 PathEvent::End { last, first, close } => Self::End {
87 last: lyon_point(last),
88 first: lyon_point(first),
89 close,
90 },
91 }
92 }
93}
94
95impl<U> PathEvent<U> {
96 #[must_use]
99 pub fn cast_unit<V>(self) -> PathEvent<V> {
100 match self {
101 Self::Begin { at } => PathEvent::Begin { at: at.cast_unit() },
102 Self::Line { from, to } => PathEvent::Line {
103 from: from.cast_unit(),
104 to: to.cast_unit(),
105 },
106 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
107 from: from.cast_unit(),
108 ctrl: ctrl.cast_unit(),
109 to: to.cast_unit(),
110 },
111 Self::Cubic {
112 from,
113 ctrl1,
114 ctrl2,
115 to,
116 } => PathEvent::Cubic {
117 from: from.cast_unit(),
118 ctrl1: ctrl1.cast_unit(),
119 ctrl2: ctrl2.cast_unit(),
120 to: to.cast_unit(),
121 },
122 Self::End { last, first, close } => PathEvent::End {
123 last: last.cast_unit(),
124 first: first.cast_unit(),
125 close,
126 },
127 }
128 }
129}
130
131#[derive(Default, Debug, Clone)]
133pub struct Path<S> {
134 events: Vec<PathEvent<S>>,
135}
136
137impl<U> Path<U> {
138 #[must_use]
141 pub fn cast_unit<V>(self) -> Path<V> {
142 Path {
143 events: self.events.into_iter().map(PathEvent::cast_unit).collect(),
144 }
145 }
146}
147
148impl Path<Pixels> {
149 pub(crate) fn translate_and_convert_to_device(
150 &self,
151 location: Point<f32, Pixels>,
152 scene: &Target,
153 ) -> Self {
154 let location = scene.offset_point_raw(location);
155 let mut events = Vec::new();
156
157 for event in &self.events {
158 events.push(match event {
161 PathEvent::Begin { at } => PathEvent::Begin { at: *at + location },
162 PathEvent::Line { from, to } => PathEvent::Line {
163 from: *from + location,
164 to: *to + location,
165 },
166 PathEvent::End { first, last, close } => PathEvent::End {
167 first: *first + location,
168 last: *last + location,
169 close: *close,
170 },
171 PathEvent::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
172 from: *from + location,
173 ctrl: *ctrl + location,
174 to: *to + location,
175 },
176 PathEvent::Cubic {
177 from,
178 ctrl1,
179 ctrl2,
180 to,
181 } => PathEvent::Cubic {
182 from: *from + location,
183 ctrl1: *ctrl1 + location,
184 ctrl2: *ctrl2 + location,
185 to: *to + location,
186 },
187 });
188 }
189
190 Self { events }
191 }
192}
193
194impl Path<Pixels> {
195 pub(crate) fn build(
196 &self,
197 builder: &mut easygpu_lyon::ShapeBuilder,
198 stroke: &Option<Stroke>,
199 fill: &Option<Fill>,
200 ) -> crate::Result<()> {
201 let path = self.as_lyon();
202 if let Some(fill) = fill {
203 builder.default_color = fill.color.rgba();
204 builder
205 .fill(&path, &fill.options)
206 .map_err(Error::Tessellation)?;
207 }
208
209 if let Some(stroke) = stroke {
210 builder.default_color = stroke.color.rgba();
211 builder
212 .stroke(&path, &stroke.options)
213 .map_err(Error::Tessellation)?;
214 }
215
216 Ok(())
217 }
218
219 pub(crate) fn as_lyon(&self) -> lyon_tessellation::path::Path {
220 let mut builder = lyon_tessellation::path::Path::builder();
221 for &event in &self.events {
222 builder.path_event(event.into());
223 }
224 builder.build()
225 }
226}
227
228impl<S, T> From<T> for Path<S>
229where
230 T: IntoIterator<Item = PathEvent<S>>,
231{
232 fn from(source: T) -> Self {
233 Self {
234 events: source.into_iter().collect(),
235 }
236 }
237}
238
239pub struct PathBuilder<S> {
241 path: Path<S>,
242 start_at: Endpoint<S>,
243 current_location: Endpoint<S>,
244 close: bool,
245}
246
247impl<S> PathBuilder<S> {
248 #[must_use]
250 pub fn new(start_at: Endpoint<S>) -> Self {
251 let events = vec![PathEvent::Begin { at: start_at }];
252 Self {
253 path: Path::from(events),
254 start_at,
255 current_location: start_at,
256 close: false,
257 }
258 }
259
260 #[must_use]
262 pub fn build(mut self) -> Path<S> {
263 self.path.events.push(PathEvent::End {
264 first: self.start_at,
265 last: self.current_location,
266 close: self.close,
267 });
268 self.path
269 }
270
271 #[must_use]
273 pub fn line_to(mut self, end_at: Endpoint<S>) -> Self {
274 self.path.events.push(PathEvent::Line {
275 from: self.current_location,
276 to: end_at,
277 });
278 self.current_location = end_at;
279 self
280 }
281
282 #[must_use]
285 pub fn quadratic_curve_to(mut self, control: ControlPoint<S>, end_at: Endpoint<S>) -> Self {
286 self.path.events.push(PathEvent::Quadratic {
287 from: self.current_location,
288 ctrl: control,
289 to: end_at,
290 });
291 self.current_location = end_at;
292 self
293 }
294
295 #[must_use]
298 pub fn cubic_curve_to(
299 mut self,
300 control1: ControlPoint<S>,
301 control2: ControlPoint<S>,
302 end_at: Endpoint<S>,
303 ) -> Self {
304 self.path.events.push(PathEvent::Cubic {
305 from: self.current_location,
306 ctrl1: control1,
307 ctrl2: control2,
308 to: end_at,
309 });
310 self.current_location = end_at;
311 self
312 }
313
314 #[must_use]
317 pub const fn close(mut self) -> Self {
318 self.close = true;
319 self
320 }
321}
322
323impl<Src, Dst> std::ops::Mul<Scale<f32, Src, Dst>> for Path<Src> {
324 type Output = Path<Dst>;
325
326 fn mul(self, scale: Scale<f32, Src, Dst>) -> Self::Output {
327 Self::Output {
328 events: self.events.into_iter().map(|event| event * scale).collect(),
329 }
330 }
331}
332
333impl<Src, Dst> std::ops::Mul<Scale<f32, Src, Dst>> for PathEvent<Src> {
334 type Output = PathEvent<Dst>;
335
336 fn mul(self, scale: Scale<f32, Src, Dst>) -> Self::Output {
337 match self {
338 PathEvent::Begin { at } => Self::Output::Begin { at: at * scale },
339 PathEvent::Line { from, to } => Self::Output::Line {
340 from: from * scale,
341 to: to * scale,
342 },
343 PathEvent::Quadratic { from, ctrl, to } => Self::Output::Quadratic {
344 from: from * scale,
345 ctrl: ctrl * scale,
346 to: to * scale,
347 },
348 PathEvent::Cubic {
349 from,
350 ctrl1,
351 ctrl2,
352 to,
353 } => Self::Output::Cubic {
354 from: from * scale,
355 ctrl1: ctrl1 * scale,
356 ctrl2: ctrl2 * scale,
357 to: to * scale,
358 },
359 PathEvent::End { last, first, close } => Self::Output::End {
360 last: last * scale,
361 first: first * scale,
362 close,
363 },
364 }
365 }
366}
367
368impl Displayable<f32> for Path<Pixels> {
369 type Pixels = Self;
370 type Points = Path<Points>;
371 type Scaled = Path<Scaled>;
372
373 fn to_pixels(&self, _scale: &figures::DisplayScale<f32>) -> Self::Pixels {
374 self.clone()
375 }
376
377 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
378 Path {
379 events: self.events.iter().map(|e| e.to_points(scale)).collect(),
380 }
381 }
382
383 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
384 Path {
385 events: self.events.iter().map(|e| e.to_scaled(scale)).collect(),
386 }
387 }
388}
389
390impl Displayable<f32> for Path<Points> {
391 type Pixels = Path<Pixels>;
392 type Points = Self;
393 type Scaled = Path<Scaled>;
394
395 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
396 Path {
397 events: self.events.iter().map(|e| e.to_pixels(scale)).collect(),
398 }
399 }
400
401 fn to_points(&self, _scale: &figures::DisplayScale<f32>) -> Self::Points {
402 self.clone()
403 }
404
405 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
406 Path {
407 events: self.events.iter().map(|e| e.to_scaled(scale)).collect(),
408 }
409 }
410}
411
412impl Displayable<f32> for Path<Scaled> {
413 type Pixels = Path<Pixels>;
414 type Points = Path<Points>;
415 type Scaled = Self;
416
417 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
418 Path {
419 events: self.events.iter().map(|e| e.to_pixels(scale)).collect(),
420 }
421 }
422
423 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
424 Path {
425 events: self.events.iter().map(|e| e.to_points(scale)).collect(),
426 }
427 }
428
429 fn to_scaled(&self, _scale: &figures::DisplayScale<f32>) -> Self::Scaled {
430 self.clone()
431 }
432}
433
434impl Displayable<f32> for PathEvent<Pixels> {
435 type Pixels = Self;
436 type Points = PathEvent<Points>;
437 type Scaled = PathEvent<Scaled>;
438
439 fn to_pixels(&self, _scale: &figures::DisplayScale<f32>) -> Self::Pixels {
440 *self
441 }
442
443 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
444 match self {
445 Self::Begin { at } => PathEvent::Begin {
446 at: at.to_points(scale),
447 },
448 Self::Line { from, to } => PathEvent::Line {
449 from: from.to_points(scale),
450 to: to.to_points(scale),
451 },
452 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
453 from: from.to_points(scale),
454 ctrl: ctrl.to_points(scale),
455 to: to.to_points(scale),
456 },
457 Self::Cubic {
458 from,
459 ctrl1,
460 ctrl2,
461 to,
462 } => PathEvent::Cubic {
463 from: from.to_points(scale),
464 ctrl1: ctrl1.to_points(scale),
465 ctrl2: ctrl2.to_points(scale),
466 to: to.to_points(scale),
467 },
468 Self::End { last, first, close } => PathEvent::End {
469 last: last.to_points(scale),
470 first: first.to_points(scale),
471 close: *close,
472 },
473 }
474 }
475
476 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
477 match self {
478 Self::Begin { at } => PathEvent::Begin {
479 at: at.to_scaled(scale),
480 },
481 Self::Line { from, to } => PathEvent::Line {
482 from: from.to_scaled(scale),
483 to: to.to_scaled(scale),
484 },
485 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
486 from: from.to_scaled(scale),
487 ctrl: ctrl.to_scaled(scale),
488 to: to.to_scaled(scale),
489 },
490 Self::Cubic {
491 from,
492 ctrl1,
493 ctrl2,
494 to,
495 } => PathEvent::Cubic {
496 from: from.to_scaled(scale),
497 ctrl1: ctrl1.to_scaled(scale),
498 ctrl2: ctrl2.to_scaled(scale),
499 to: to.to_scaled(scale),
500 },
501 Self::End { last, first, close } => PathEvent::End {
502 last: last.to_scaled(scale),
503 first: first.to_scaled(scale),
504 close: *close,
505 },
506 }
507 }
508}
509
510impl Displayable<f32> for PathEvent<Points> {
511 type Pixels = PathEvent<Pixels>;
512 type Points = Self;
513 type Scaled = PathEvent<Scaled>;
514
515 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
516 match self {
517 Self::Begin { at } => PathEvent::Begin {
518 at: at.to_pixels(scale),
519 },
520 Self::Line { from, to } => PathEvent::Line {
521 from: from.to_pixels(scale),
522 to: to.to_pixels(scale),
523 },
524 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
525 from: from.to_pixels(scale),
526 ctrl: ctrl.to_pixels(scale),
527 to: to.to_pixels(scale),
528 },
529 Self::Cubic {
530 from,
531 ctrl1,
532 ctrl2,
533 to,
534 } => PathEvent::Cubic {
535 from: from.to_pixels(scale),
536 ctrl1: ctrl1.to_pixels(scale),
537 ctrl2: ctrl2.to_pixels(scale),
538 to: to.to_pixels(scale),
539 },
540 Self::End { last, first, close } => PathEvent::End {
541 last: last.to_pixels(scale),
542 first: first.to_pixels(scale),
543 close: *close,
544 },
545 }
546 }
547
548 fn to_points(&self, _scale: &figures::DisplayScale<f32>) -> Self::Points {
549 *self
550 }
551
552 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
553 match self {
554 Self::Begin { at } => PathEvent::Begin {
555 at: at.to_scaled(scale),
556 },
557 Self::Line { from, to } => PathEvent::Line {
558 from: from.to_scaled(scale),
559 to: to.to_scaled(scale),
560 },
561 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
562 from: from.to_scaled(scale),
563 ctrl: ctrl.to_scaled(scale),
564 to: to.to_scaled(scale),
565 },
566 Self::Cubic {
567 from,
568 ctrl1,
569 ctrl2,
570 to,
571 } => PathEvent::Cubic {
572 from: from.to_scaled(scale),
573 ctrl1: ctrl1.to_scaled(scale),
574 ctrl2: ctrl2.to_scaled(scale),
575 to: to.to_scaled(scale),
576 },
577 Self::End { last, first, close } => PathEvent::End {
578 last: last.to_scaled(scale),
579 first: first.to_scaled(scale),
580 close: *close,
581 },
582 }
583 }
584}
585
586impl Displayable<f32> for PathEvent<Scaled> {
587 type Pixels = PathEvent<Pixels>;
588 type Points = PathEvent<Points>;
589 type Scaled = Self;
590
591 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
592 match self {
593 Self::Begin { at } => PathEvent::Begin {
594 at: at.to_pixels(scale),
595 },
596 Self::Line { from, to } => PathEvent::Line {
597 from: from.to_pixels(scale),
598 to: to.to_pixels(scale),
599 },
600 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
601 from: from.to_pixels(scale),
602 ctrl: ctrl.to_pixels(scale),
603 to: to.to_pixels(scale),
604 },
605 Self::Cubic {
606 from,
607 ctrl1,
608 ctrl2,
609 to,
610 } => PathEvent::Cubic {
611 from: from.to_pixels(scale),
612 ctrl1: ctrl1.to_pixels(scale),
613 ctrl2: ctrl2.to_pixels(scale),
614 to: to.to_pixels(scale),
615 },
616 Self::End { last, first, close } => PathEvent::End {
617 last: last.to_pixels(scale),
618 first: first.to_pixels(scale),
619 close: *close,
620 },
621 }
622 }
623
624 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
625 match self {
626 Self::Begin { at } => PathEvent::Begin {
627 at: at.to_points(scale),
628 },
629 Self::Line { from, to } => PathEvent::Line {
630 from: from.to_points(scale),
631 to: to.to_points(scale),
632 },
633 Self::Quadratic { from, ctrl, to } => PathEvent::Quadratic {
634 from: from.to_points(scale),
635 ctrl: ctrl.to_points(scale),
636 to: to.to_points(scale),
637 },
638 Self::Cubic {
639 from,
640 ctrl1,
641 ctrl2,
642 to,
643 } => PathEvent::Cubic {
644 from: from.to_points(scale),
645 ctrl1: ctrl1.to_points(scale),
646 ctrl2: ctrl2.to_points(scale),
647 to: to.to_points(scale),
648 },
649 Self::End { last, first, close } => PathEvent::End {
650 last: last.to_points(scale),
651 first: first.to_points(scale),
652 close: *close,
653 },
654 }
655 }
656
657 fn to_scaled(&self, _scale: &figures::DisplayScale<f32>) -> Self::Scaled {
658 *self
659 }
660}