1
use easygpu_lyon::lyon_tessellation::path::PathEvent as LyonPathEvent;
2
use easygpu_lyon::lyon_tessellation::{self};
3
use figures::{Displayable, Points};
4

            
5
use super::lyon_point;
6
use crate::math::{Pixels, Point, Scale, Scaled};
7
use crate::scene::Target;
8
use crate::shape::{Fill, Stroke};
9
use crate::Error;
10

            
11
/// A point on a [`Path`].
12
pub type Endpoint<S> = Point<f32, S>;
13
/// A control point used to create curves.
14
pub type ControlPoint<S> = Point<f32, S>;
15

            
16
/// An entry in a [`Path`].
17
#[derive(Debug, Clone, Copy)]
18
pub enum PathEvent<S> {
19
    /// Begins a path. Must be at the start.
20
    Begin {
21
        /// The location to begin at.
22
        at: Endpoint<S>,
23
    },
24
    /// A straight line segment.
25
    Line {
26
        /// The origin of the line.
27
        from: Endpoint<S>,
28
        /// The end location of the line.
29
        to: Endpoint<S>,
30
    },
31
    /// A quadratic curve (one control point).
32
    Quadratic {
33
        /// The origin of the curve.
34
        from: Endpoint<S>,
35
        /// The control point for the curve.
36
        ctrl: ControlPoint<S>,
37
        /// The end location of the curve.
38
        to: Endpoint<S>,
39
    },
40
    /// A cubic curve (two control points).
41
    Cubic {
42
        /// The origin of the curve.
43
        from: Endpoint<S>,
44
        /// The first control point for the curve.
45
        ctrl1: ControlPoint<S>,
46
        /// The second control point for the curve.
47
        ctrl2: ControlPoint<S>,
48
        /// The end location of the curve.
49
        to: Endpoint<S>,
50
    },
51
    /// Ends the path. Must be the last entry.
52
    End {
53
        /// The end location of the path.
54
        last: Endpoint<S>,
55
        /// The start location of the path.
56
        first: Endpoint<S>,
57
        /// Whether the path should be closed.
58
        close: bool,
59
    },
60
}
61

            
62
impl 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

            
95
impl<U> PathEvent<U> {
96
    /// Returns the path event with the new unit. Does not alter the underlying
97
    /// coordinate data.
98
    #[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
/// A geometric shape defined by a path.
132
#[derive(Default, Debug, Clone)]
133
pub struct Path<S> {
134
    events: Vec<PathEvent<S>>,
135
}
136

            
137
impl<U> Path<U> {
138
    /// Returns the path with the new unit. Does not alter the underlying
139
    /// coordinate data.
140
    #[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

            
148
impl 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
            // There's a bug with async-local variables and this analysis. There is no
159
            // cross-dependency on any of these parameters.
160
            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

            
194
impl 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

            
228
impl<S, T> From<T> for Path<S>
229
where
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

            
239
/// Builds a [`Path`].
240
pub struct PathBuilder<S> {
241
    path: Path<S>,
242
    start_at: Endpoint<S>,
243
    current_location: Endpoint<S>,
244
    close: bool,
245
}
246

            
247
impl<S> PathBuilder<S> {
248
    /// Creates a new path with the initial position `start_at`.
249
    #[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
    /// Returns the built path.
261
    #[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
    /// Create a straight line from the current location to `end_at`.
272
    #[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
    /// Create a quadratic curve from the current location to `end_at` using
283
    /// `control` as the curve's control point.
284
    #[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
    /// Create a cubic curve from the current location to `end_at` using
296
    /// `control1` and `control2` as the curve's control points.
297
    #[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
    /// Closes the path, connecting the current location to the shape's starting
315
    /// location.
316
    #[must_use]
317
    pub const fn close(mut self) -> Self {
318
        self.close = true;
319
        self
320
    }
321
}
322

            
323
impl<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

            
333
impl<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

            
368
impl 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

            
390
impl 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

            
412
impl 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

            
434
impl 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

            
510
impl 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

            
586
impl 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
}