scene.rs

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