scene.rs

  1// todo("windows"): remove
  2#![cfg_attr(windows, allow(dead_code))]
  3
  4use schemars::JsonSchema;
  5use serde::{Deserialize, Serialize};
  6
  7use crate::{
  8    AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
  9    Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
 10};
 11use std::{fmt::Debug, iter::Peekable, ops::Range, slice};
 12
 13#[allow(non_camel_case_types, unused)]
 14pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 15
 16pub(crate) type DrawOrder = u32;
 17
 18#[derive(Default)]
 19pub(crate) struct Scene {
 20    pub(crate) paint_operations: Vec<PaintOperation>,
 21    primitive_bounds: BoundsTree<ScaledPixels>,
 22    layer_stack: Vec<DrawOrder>,
 23    pub(crate) shadows: Vec<Shadow>,
 24    pub(crate) quads: Vec<Quad>,
 25    pub(crate) paths: Vec<Path<ScaledPixels>>,
 26    pub(crate) underlines: Vec<Underline>,
 27    pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
 28    pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
 29    pub(crate) surfaces: Vec<PaintSurface>,
 30}
 31
 32impl Scene {
 33    pub fn clear(&mut self) {
 34        self.paint_operations.clear();
 35        self.primitive_bounds.clear();
 36        self.layer_stack.clear();
 37        self.paths.clear();
 38        self.shadows.clear();
 39        self.quads.clear();
 40        self.underlines.clear();
 41        self.monochrome_sprites.clear();
 42        self.polychrome_sprites.clear();
 43        self.surfaces.clear();
 44    }
 45
 46    #[cfg_attr(
 47        all(
 48            any(target_os = "linux", target_os = "freebsd"),
 49            not(any(feature = "x11", feature = "wayland"))
 50        ),
 51        allow(dead_code)
 52    )]
 53    pub fn paths(&self) -> &[Path<ScaledPixels>] {
 54        &self.paths
 55    }
 56
 57    pub fn len(&self) -> usize {
 58        self.paint_operations.len()
 59    }
 60
 61    pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
 62        let order = self.primitive_bounds.insert(bounds);
 63        self.layer_stack.push(order);
 64        self.paint_operations
 65            .push(PaintOperation::StartLayer(bounds));
 66    }
 67
 68    pub fn pop_layer(&mut self) {
 69        self.layer_stack.pop();
 70        self.paint_operations.push(PaintOperation::EndLayer);
 71    }
 72
 73    pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
 74        let mut primitive = primitive.into();
 75        let clipped_bounds = primitive
 76            .bounds()
 77            .intersect(&primitive.content_mask().bounds);
 78
 79        if clipped_bounds.is_empty() {
 80            return;
 81        }
 82
 83        let order = self
 84            .layer_stack
 85            .last()
 86            .copied()
 87            .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
 88        match &mut primitive {
 89            Primitive::Shadow(shadow) => {
 90                shadow.order = order;
 91                self.shadows.push(shadow.clone());
 92            }
 93            Primitive::Quad(quad) => {
 94                quad.order = order;
 95                self.quads.push(quad.clone());
 96            }
 97            Primitive::Path(path) => {
 98                path.order = order;
 99                path.id = PathId(self.paths.len());
100                self.paths.push(path.clone());
101            }
102            Primitive::Underline(underline) => {
103                underline.order = order;
104                self.underlines.push(underline.clone());
105            }
106            Primitive::MonochromeSprite(sprite) => {
107                sprite.order = order;
108                self.monochrome_sprites.push(sprite.clone());
109            }
110            Primitive::PolychromeSprite(sprite) => {
111                sprite.order = order;
112                self.polychrome_sprites.push(sprite.clone());
113            }
114            Primitive::Surface(surface) => {
115                surface.order = order;
116                self.surfaces.push(surface.clone());
117            }
118        }
119        self.paint_operations
120            .push(PaintOperation::Primitive(primitive));
121    }
122
123    pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
124        for operation in &prev_scene.paint_operations[range] {
125            match operation {
126                PaintOperation::Primitive(primitive) => self.insert_primitive(primitive.clone()),
127                PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
128                PaintOperation::EndLayer => self.pop_layer(),
129            }
130        }
131    }
132
133    pub fn finish(&mut self) {
134        self.shadows.sort_by_key(|shadow| shadow.order);
135        self.quads.sort_by_key(|quad| quad.order);
136        self.paths.sort_by_key(|path| path.order);
137        self.underlines.sort_by_key(|underline| underline.order);
138        self.monochrome_sprites
139            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
140        self.polychrome_sprites
141            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
142        self.surfaces.sort_by_key(|surface| surface.order);
143    }
144
145    #[cfg_attr(
146        all(
147            any(target_os = "linux", target_os = "freebsd"),
148            not(any(feature = "x11", feature = "wayland"))
149        ),
150        allow(dead_code)
151    )]
152    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
153        BatchIterator {
154            shadows: &self.shadows,
155            shadows_start: 0,
156            shadows_iter: self.shadows.iter().peekable(),
157            quads: &self.quads,
158            quads_start: 0,
159            quads_iter: self.quads.iter().peekable(),
160            paths: &self.paths,
161            paths_start: 0,
162            paths_iter: self.paths.iter().peekable(),
163            underlines: &self.underlines,
164            underlines_start: 0,
165            underlines_iter: self.underlines.iter().peekable(),
166            monochrome_sprites: &self.monochrome_sprites,
167            monochrome_sprites_start: 0,
168            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
169            polychrome_sprites: &self.polychrome_sprites,
170            polychrome_sprites_start: 0,
171            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
172            surfaces: &self.surfaces,
173            surfaces_start: 0,
174            surfaces_iter: self.surfaces.iter().peekable(),
175        }
176    }
177}
178
179#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
180#[cfg_attr(
181    all(
182        any(target_os = "linux", target_os = "freebsd"),
183        not(any(feature = "x11", feature = "wayland"))
184    ),
185    allow(dead_code)
186)]
187pub(crate) enum PrimitiveKind {
188    Shadow,
189    #[default]
190    Quad,
191    Path,
192    Underline,
193    MonochromeSprite,
194    PolychromeSprite,
195    Surface,
196}
197
198pub(crate) enum PaintOperation {
199    Primitive(Primitive),
200    StartLayer(Bounds<ScaledPixels>),
201    EndLayer,
202}
203
204#[derive(Clone)]
205pub(crate) enum Primitive {
206    Shadow(Shadow),
207    Quad(Quad),
208    Path(Path<ScaledPixels>),
209    Underline(Underline),
210    MonochromeSprite(MonochromeSprite),
211    PolychromeSprite(PolychromeSprite),
212    Surface(PaintSurface),
213}
214
215impl Primitive {
216    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
217        match self {
218            Primitive::Shadow(shadow) => &shadow.bounds,
219            Primitive::Quad(quad) => &quad.bounds,
220            Primitive::Path(path) => &path.bounds,
221            Primitive::Underline(underline) => &underline.bounds,
222            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
223            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
224            Primitive::Surface(surface) => &surface.bounds,
225        }
226    }
227
228    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
229        match self {
230            Primitive::Shadow(shadow) => &shadow.content_mask,
231            Primitive::Quad(quad) => &quad.content_mask,
232            Primitive::Path(path) => &path.content_mask,
233            Primitive::Underline(underline) => &underline.content_mask,
234            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
235            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
236            Primitive::Surface(surface) => &surface.content_mask,
237        }
238    }
239}
240
241#[cfg_attr(
242    all(
243        any(target_os = "linux", target_os = "freebsd"),
244        not(any(feature = "x11", feature = "wayland"))
245    ),
246    allow(dead_code)
247)]
248struct BatchIterator<'a> {
249    shadows: &'a [Shadow],
250    shadows_start: usize,
251    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
252    quads: &'a [Quad],
253    quads_start: usize,
254    quads_iter: Peekable<slice::Iter<'a, Quad>>,
255    paths: &'a [Path<ScaledPixels>],
256    paths_start: usize,
257    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
258    underlines: &'a [Underline],
259    underlines_start: usize,
260    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
261    monochrome_sprites: &'a [MonochromeSprite],
262    monochrome_sprites_start: usize,
263    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
264    polychrome_sprites: &'a [PolychromeSprite],
265    polychrome_sprites_start: usize,
266    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
267    surfaces: &'a [PaintSurface],
268    surfaces_start: usize,
269    surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
270}
271
272impl<'a> Iterator for BatchIterator<'a> {
273    type Item = PrimitiveBatch<'a>;
274
275    fn next(&mut self) -> Option<Self::Item> {
276        let mut orders_and_kinds = [
277            (
278                self.shadows_iter.peek().map(|s| s.order),
279                PrimitiveKind::Shadow,
280            ),
281            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
282            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
283            (
284                self.underlines_iter.peek().map(|u| u.order),
285                PrimitiveKind::Underline,
286            ),
287            (
288                self.monochrome_sprites_iter.peek().map(|s| s.order),
289                PrimitiveKind::MonochromeSprite,
290            ),
291            (
292                self.polychrome_sprites_iter.peek().map(|s| s.order),
293                PrimitiveKind::PolychromeSprite,
294            ),
295            (
296                self.surfaces_iter.peek().map(|s| s.order),
297                PrimitiveKind::Surface,
298            ),
299        ];
300        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
301
302        let first = orders_and_kinds[0];
303        let second = orders_and_kinds[1];
304        let (batch_kind, max_order_and_kind) = if first.0.is_some() {
305            (first.1, (second.0.unwrap_or(u32::MAX), second.1))
306        } else {
307            return None;
308        };
309
310        match batch_kind {
311            PrimitiveKind::Shadow => {
312                let shadows_start = self.shadows_start;
313                let mut shadows_end = shadows_start + 1;
314                self.shadows_iter.next();
315                while self
316                    .shadows_iter
317                    .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
318                    .is_some()
319                {
320                    shadows_end += 1;
321                }
322                self.shadows_start = shadows_end;
323                Some(PrimitiveBatch::Shadows(
324                    &self.shadows[shadows_start..shadows_end],
325                ))
326            }
327            PrimitiveKind::Quad => {
328                let quads_start = self.quads_start;
329                let mut quads_end = quads_start + 1;
330                self.quads_iter.next();
331                while self
332                    .quads_iter
333                    .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
334                    .is_some()
335                {
336                    quads_end += 1;
337                }
338                self.quads_start = quads_end;
339                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
340            }
341            PrimitiveKind::Path => {
342                let paths_start = self.paths_start;
343                let mut paths_end = paths_start + 1;
344                self.paths_iter.next();
345                while self
346                    .paths_iter
347                    .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
348                    .is_some()
349                {
350                    paths_end += 1;
351                }
352                self.paths_start = paths_end;
353                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
354            }
355            PrimitiveKind::Underline => {
356                let underlines_start = self.underlines_start;
357                let mut underlines_end = underlines_start + 1;
358                self.underlines_iter.next();
359                while self
360                    .underlines_iter
361                    .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
362                    .is_some()
363                {
364                    underlines_end += 1;
365                }
366                self.underlines_start = underlines_end;
367                Some(PrimitiveBatch::Underlines(
368                    &self.underlines[underlines_start..underlines_end],
369                ))
370            }
371            PrimitiveKind::MonochromeSprite => {
372                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
373                let sprites_start = self.monochrome_sprites_start;
374                let mut sprites_end = sprites_start + 1;
375                self.monochrome_sprites_iter.next();
376                while self
377                    .monochrome_sprites_iter
378                    .next_if(|sprite| {
379                        (sprite.order, batch_kind) < max_order_and_kind
380                            && sprite.tile.texture_id == texture_id
381                    })
382                    .is_some()
383                {
384                    sprites_end += 1;
385                }
386                self.monochrome_sprites_start = sprites_end;
387                Some(PrimitiveBatch::MonochromeSprites {
388                    texture_id,
389                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
390                })
391            }
392            PrimitiveKind::PolychromeSprite => {
393                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
394                let sprites_start = self.polychrome_sprites_start;
395                let mut sprites_end = self.polychrome_sprites_start + 1;
396                self.polychrome_sprites_iter.next();
397                while self
398                    .polychrome_sprites_iter
399                    .next_if(|sprite| {
400                        (sprite.order, batch_kind) < max_order_and_kind
401                            && sprite.tile.texture_id == texture_id
402                    })
403                    .is_some()
404                {
405                    sprites_end += 1;
406                }
407                self.polychrome_sprites_start = sprites_end;
408                Some(PrimitiveBatch::PolychromeSprites {
409                    texture_id,
410                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
411                })
412            }
413            PrimitiveKind::Surface => {
414                let surfaces_start = self.surfaces_start;
415                let mut surfaces_end = surfaces_start + 1;
416                self.surfaces_iter.next();
417                while self
418                    .surfaces_iter
419                    .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
420                    .is_some()
421                {
422                    surfaces_end += 1;
423                }
424                self.surfaces_start = surfaces_end;
425                Some(PrimitiveBatch::Surfaces(
426                    &self.surfaces[surfaces_start..surfaces_end],
427                ))
428            }
429        }
430    }
431}
432
433#[derive(Debug)]
434#[cfg_attr(
435    all(
436        any(target_os = "linux", target_os = "freebsd"),
437        not(any(feature = "x11", feature = "wayland"))
438    ),
439    allow(dead_code)
440)]
441pub(crate) enum PrimitiveBatch<'a> {
442    Shadows(&'a [Shadow]),
443    Quads(&'a [Quad]),
444    Paths(&'a [Path<ScaledPixels>]),
445    Underlines(&'a [Underline]),
446    MonochromeSprites {
447        texture_id: AtlasTextureId,
448        sprites: &'a [MonochromeSprite],
449    },
450    PolychromeSprites {
451        texture_id: AtlasTextureId,
452        sprites: &'a [PolychromeSprite],
453    },
454    Surfaces(&'a [PaintSurface]),
455}
456
457#[derive(Default, Debug, Clone)]
458#[repr(C)]
459pub(crate) struct Quad {
460    pub order: DrawOrder,
461    pub border_style: BorderStyle,
462    pub bounds: Bounds<ScaledPixels>,
463    pub content_mask: ContentMask<ScaledPixels>,
464    pub background: Background,
465    pub border_color: Hsla,
466    pub corner_radii: Corners<ScaledPixels>,
467    pub border_widths: Edges<ScaledPixels>,
468}
469
470impl From<Quad> for Primitive {
471    fn from(quad: Quad) -> Self {
472        Primitive::Quad(quad)
473    }
474}
475
476#[derive(Debug, Clone)]
477#[repr(C)]
478pub(crate) struct Underline {
479    pub order: DrawOrder,
480    pub pad: u32, // align to 8 bytes
481    pub bounds: Bounds<ScaledPixels>,
482    pub content_mask: ContentMask<ScaledPixels>,
483    pub color: Hsla,
484    pub thickness: ScaledPixels,
485    pub wavy: bool,
486}
487
488impl From<Underline> for Primitive {
489    fn from(underline: Underline) -> Self {
490        Primitive::Underline(underline)
491    }
492}
493
494#[derive(Debug, Clone)]
495#[repr(C)]
496pub(crate) struct Shadow {
497    pub order: DrawOrder,
498    pub blur_radius: ScaledPixels,
499    pub bounds: Bounds<ScaledPixels>,
500    pub corner_radii: Corners<ScaledPixels>,
501    pub content_mask: ContentMask<ScaledPixels>,
502    pub color: Hsla,
503}
504
505impl From<Shadow> for Primitive {
506    fn from(shadow: Shadow) -> Self {
507        Primitive::Shadow(shadow)
508    }
509}
510
511/// The style of a border.
512#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
513#[repr(C)]
514pub enum BorderStyle {
515    /// A solid border.
516    #[default]
517    Solid = 0,
518    /// A dashed border.
519    Dashed = 1,
520}
521
522/// A data type representing a 2 dimensional transformation that can be applied to an element.
523#[derive(Debug, Clone, Copy, PartialEq)]
524#[repr(C)]
525pub struct TransformationMatrix {
526    /// 2x2 matrix containing rotation and scale,
527    /// stored row-major
528    pub rotation_scale: [[f32; 2]; 2],
529    /// translation vector
530    pub translation: [f32; 2],
531}
532
533impl Eq for TransformationMatrix {}
534
535impl TransformationMatrix {
536    /// The unit matrix, has no effect.
537    pub fn unit() -> Self {
538        Self {
539            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
540            translation: [0.0, 0.0],
541        }
542    }
543
544    /// Move the origin by a given point
545    pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
546        self.compose(Self {
547            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
548            translation: [point.x.0, point.y.0],
549        })
550    }
551
552    /// Clockwise rotation in radians around the origin
553    pub fn rotate(self, angle: Radians) -> Self {
554        self.compose(Self {
555            rotation_scale: [
556                [angle.0.cos(), -angle.0.sin()],
557                [angle.0.sin(), angle.0.cos()],
558            ],
559            translation: [0.0, 0.0],
560        })
561    }
562
563    /// Scale around the origin
564    pub fn scale(self, size: Size<f32>) -> Self {
565        self.compose(Self {
566            rotation_scale: [[size.width, 0.0], [0.0, size.height]],
567            translation: [0.0, 0.0],
568        })
569    }
570
571    /// Perform matrix multiplication with another transformation
572    /// to produce a new transformation that is the result of
573    /// applying both transformations: first, `other`, then `self`.
574    #[inline]
575    pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
576        if other == Self::unit() {
577            return self;
578        }
579        // Perform matrix multiplication
580        TransformationMatrix {
581            rotation_scale: [
582                [
583                    self.rotation_scale[0][0] * other.rotation_scale[0][0]
584                        + self.rotation_scale[0][1] * other.rotation_scale[1][0],
585                    self.rotation_scale[0][0] * other.rotation_scale[0][1]
586                        + self.rotation_scale[0][1] * other.rotation_scale[1][1],
587                ],
588                [
589                    self.rotation_scale[1][0] * other.rotation_scale[0][0]
590                        + self.rotation_scale[1][1] * other.rotation_scale[1][0],
591                    self.rotation_scale[1][0] * other.rotation_scale[0][1]
592                        + self.rotation_scale[1][1] * other.rotation_scale[1][1],
593                ],
594            ],
595            translation: [
596                self.translation[0]
597                    + self.rotation_scale[0][0] * other.translation[0]
598                    + self.rotation_scale[0][1] * other.translation[1],
599                self.translation[1]
600                    + self.rotation_scale[1][0] * other.translation[0]
601                    + self.rotation_scale[1][1] * other.translation[1],
602            ],
603        }
604    }
605
606    /// Apply transformation to a point, mainly useful for debugging
607    pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
608        let input = [point.x.0, point.y.0];
609        let mut output = self.translation;
610        for (i, output_cell) in output.iter_mut().enumerate() {
611            for (k, input_cell) in input.iter().enumerate() {
612                *output_cell += self.rotation_scale[i][k] * *input_cell;
613            }
614        }
615        Point::new(output[0].into(), output[1].into())
616    }
617}
618
619impl Default for TransformationMatrix {
620    fn default() -> Self {
621        Self::unit()
622    }
623}
624
625#[derive(Clone, Debug)]
626#[repr(C)]
627pub(crate) struct MonochromeSprite {
628    pub order: DrawOrder,
629    pub pad: u32, // align to 8 bytes
630    pub bounds: Bounds<ScaledPixels>,
631    pub content_mask: ContentMask<ScaledPixels>,
632    pub color: Hsla,
633    pub tile: AtlasTile,
634    pub transformation: TransformationMatrix,
635}
636
637impl From<MonochromeSprite> for Primitive {
638    fn from(sprite: MonochromeSprite) -> Self {
639        Primitive::MonochromeSprite(sprite)
640    }
641}
642
643#[derive(Clone, Debug)]
644#[repr(C)]
645pub(crate) struct PolychromeSprite {
646    pub order: DrawOrder,
647    pub pad: u32, // align to 8 bytes
648    pub grayscale: bool,
649    pub opacity: f32,
650    pub bounds: Bounds<ScaledPixels>,
651    pub content_mask: ContentMask<ScaledPixels>,
652    pub corner_radii: Corners<ScaledPixels>,
653    pub tile: AtlasTile,
654}
655
656impl From<PolychromeSprite> for Primitive {
657    fn from(sprite: PolychromeSprite) -> Self {
658        Primitive::PolychromeSprite(sprite)
659    }
660}
661
662#[derive(Clone, Debug)]
663pub(crate) struct PaintSurface {
664    pub order: DrawOrder,
665    pub bounds: Bounds<ScaledPixels>,
666    pub content_mask: ContentMask<ScaledPixels>,
667    #[cfg(target_os = "macos")]
668    pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
669}
670
671impl From<PaintSurface> for Primitive {
672    fn from(surface: PaintSurface) -> Self {
673        Primitive::Surface(surface)
674    }
675}
676
677#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
678pub(crate) struct PathId(pub(crate) usize);
679
680/// A line made up of a series of vertices and control points.
681#[derive(Clone, Debug)]
682pub struct Path<P: Clone + Debug + Default + PartialEq> {
683    pub(crate) id: PathId,
684    order: DrawOrder,
685    pub(crate) bounds: Bounds<P>,
686    pub(crate) content_mask: ContentMask<P>,
687    pub(crate) vertices: Vec<PathVertex<P>>,
688    pub(crate) color: Background,
689    start: Point<P>,
690    current: Point<P>,
691    contour_count: usize,
692}
693
694impl Path<Pixels> {
695    /// Create a new path with the given starting point.
696    pub fn new(start: Point<Pixels>) -> Self {
697        Self {
698            id: PathId(0),
699            order: DrawOrder::default(),
700            vertices: Vec::new(),
701            start,
702            current: start,
703            bounds: Bounds {
704                origin: start,
705                size: Default::default(),
706            },
707            content_mask: Default::default(),
708            color: Default::default(),
709            contour_count: 0,
710        }
711    }
712
713    /// Scale this path by the given factor.
714    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
715        Path {
716            id: self.id,
717            order: self.order,
718            bounds: self.bounds.scale(factor),
719            content_mask: self.content_mask.scale(factor),
720            vertices: self
721                .vertices
722                .iter()
723                .map(|vertex| vertex.scale(factor))
724                .collect(),
725            start: self.start.map(|start| start.scale(factor)),
726            current: self.current.scale(factor),
727            contour_count: self.contour_count,
728            color: self.color,
729        }
730    }
731
732    /// Move the start, current point to the given point.
733    pub fn move_to(&mut self, to: Point<Pixels>) {
734        self.contour_count += 1;
735        self.start = to;
736        self.current = to;
737    }
738
739    /// Draw a straight line from the current point to the given point.
740    pub fn line_to(&mut self, to: Point<Pixels>) {
741        self.contour_count += 1;
742        if self.contour_count > 1 {
743            self.push_triangle(
744                (self.start, self.current, to),
745                (point(0., 1.), point(0., 1.), point(0., 1.)),
746            );
747        }
748        self.current = to;
749    }
750
751    /// Draw a curve from the current point to the given point, using the given control point.
752    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
753        self.contour_count += 1;
754        if self.contour_count > 1 {
755            self.push_triangle(
756                (self.start, self.current, to),
757                (point(0., 1.), point(0., 1.), point(0., 1.)),
758            );
759        }
760
761        self.push_triangle(
762            (self.current, ctrl, to),
763            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
764        );
765        self.current = to;
766    }
767
768    /// Push a triangle to the Path.
769    pub fn push_triangle(
770        &mut self,
771        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
772        st: (Point<f32>, Point<f32>, Point<f32>),
773    ) {
774        self.bounds = self
775            .bounds
776            .union(&Bounds {
777                origin: xy.0,
778                size: Default::default(),
779            })
780            .union(&Bounds {
781                origin: xy.1,
782                size: Default::default(),
783            })
784            .union(&Bounds {
785                origin: xy.2,
786                size: Default::default(),
787            });
788
789        self.vertices.push(PathVertex {
790            xy_position: xy.0,
791            st_position: st.0,
792            content_mask: Default::default(),
793        });
794        self.vertices.push(PathVertex {
795            xy_position: xy.1,
796            st_position: st.1,
797            content_mask: Default::default(),
798        });
799        self.vertices.push(PathVertex {
800            xy_position: xy.2,
801            st_position: st.2,
802            content_mask: Default::default(),
803        });
804    }
805}
806
807impl From<Path<ScaledPixels>> for Primitive {
808    fn from(path: Path<ScaledPixels>) -> Self {
809        Primitive::Path(path)
810    }
811}
812
813#[derive(Clone, Debug)]
814#[repr(C)]
815pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
816    pub(crate) xy_position: Point<P>,
817    pub(crate) st_position: Point<f32>,
818    pub(crate) content_mask: ContentMask<P>,
819}
820
821impl PathVertex<Pixels> {
822    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
823        PathVertex {
824            xy_position: self.xy_position.scale(factor),
825            st_position: self.st_position,
826            content_mask: self.content_mask.scale(factor),
827        }
828    }
829}