scene.rs

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