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 type PointF = Point<f32>;
 12
 13pub type LayerId = u32;
 14
 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    #[allow(dead_code)]
184    pub fn paths(&self) -> &[Path<ScaledPixels>] {
185        &self.paths
186    }
187
188    pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
189        BatchIterator {
190            shadows: &self.shadows,
191            shadows_start: 0,
192            shadows_iter: self.shadows.iter().peekable(),
193            quads: &self.quads,
194            quads_start: 0,
195            quads_iter: self.quads.iter().peekable(),
196            paths: &self.paths,
197            paths_start: 0,
198            paths_iter: self.paths.iter().peekable(),
199            underlines: &self.underlines,
200            underlines_start: 0,
201            underlines_iter: self.underlines.iter().peekable(),
202            monochrome_sprites: &self.monochrome_sprites,
203            monochrome_sprites_start: 0,
204            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
205            polychrome_sprites: &self.polychrome_sprites,
206            polychrome_sprites_start: 0,
207            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
208        }
209    }
210}
211
212struct BatchIterator<'a> {
213    shadows: &'a [Shadow],
214    shadows_start: usize,
215    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
216    quads: &'a [Quad],
217    quads_start: usize,
218    quads_iter: Peekable<slice::Iter<'a, Quad>>,
219    paths: &'a [Path<ScaledPixels>],
220    paths_start: usize,
221    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
222    underlines: &'a [Underline],
223    underlines_start: usize,
224    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
225    monochrome_sprites: &'a [MonochromeSprite],
226    monochrome_sprites_start: usize,
227    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
228    polychrome_sprites: &'a [PolychromeSprite],
229    polychrome_sprites_start: usize,
230    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
231}
232
233impl<'a> Iterator for BatchIterator<'a> {
234    type Item = PrimitiveBatch<'a>;
235
236    fn next(&mut self) -> Option<Self::Item> {
237        let mut orders_and_kinds = [
238            (
239                self.shadows_iter.peek().map(|s| s.order),
240                PrimitiveKind::Shadow,
241            ),
242            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
243            (
244                self.underlines_iter.peek().map(|u| u.order),
245                PrimitiveKind::Underline,
246            ),
247            (
248                self.monochrome_sprites_iter.peek().map(|s| s.order),
249                PrimitiveKind::MonochromeSprite,
250            ),
251            (
252                self.polychrome_sprites_iter.peek().map(|s| s.order),
253                PrimitiveKind::PolychromeSprite,
254            ),
255        ];
256        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
257
258        let first = orders_and_kinds[0];
259        let second = orders_and_kinds[1];
260        let (batch_kind, max_order) = if first.0.is_some() {
261            (first.1, second.0.unwrap_or(u32::MAX))
262        } else {
263            return None;
264        };
265
266        match batch_kind {
267            PrimitiveKind::Shadow => {
268                let shadows_start = self.shadows_start;
269                let mut shadows_end = shadows_start;
270                while self
271                    .shadows_iter
272                    .next_if(|shadow| shadow.order <= max_order)
273                    .is_some()
274                {
275                    shadows_end += 1;
276                }
277                self.shadows_start = shadows_end;
278                Some(PrimitiveBatch::Shadows(
279                    &self.shadows[shadows_start..shadows_end],
280                ))
281            }
282            PrimitiveKind::Quad => {
283                let quads_start = self.quads_start;
284                let mut quads_end = quads_start;
285                while self
286                    .quads_iter
287                    .next_if(|quad| quad.order <= max_order)
288                    .is_some()
289                {
290                    quads_end += 1;
291                }
292                self.quads_start = quads_end;
293                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
294            }
295            PrimitiveKind::Path => {
296                let paths_start = self.paths_start;
297                let mut paths_end = paths_start;
298                while self
299                    .paths_iter
300                    .next_if(|path| path.order <= max_order)
301                    .is_some()
302                {
303                    paths_end += 1;
304                }
305                self.paths_start = paths_end;
306                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
307            }
308            PrimitiveKind::Underline => {
309                let underlines_start = self.underlines_start;
310                let mut underlines_end = underlines_start;
311                while self
312                    .underlines_iter
313                    .next_if(|underline| underline.order <= max_order)
314                    .is_some()
315                {
316                    underlines_end += 1;
317                }
318                self.underlines_start = underlines_end;
319                Some(PrimitiveBatch::Underlines(
320                    &self.underlines[underlines_start..underlines_end],
321                ))
322            }
323            PrimitiveKind::MonochromeSprite => {
324                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
325                let sprites_start = self.monochrome_sprites_start;
326                let mut sprites_end = sprites_start;
327                while self
328                    .monochrome_sprites_iter
329                    .next_if(|sprite| {
330                        sprite.order <= max_order && sprite.tile.texture_id == texture_id
331                    })
332                    .is_some()
333                {
334                    sprites_end += 1;
335                }
336                self.monochrome_sprites_start = sprites_end;
337                Some(PrimitiveBatch::MonochromeSprites {
338                    texture_id,
339                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
340                })
341            }
342            PrimitiveKind::PolychromeSprite => {
343                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
344                let sprites_start = self.polychrome_sprites_start;
345                let mut sprites_end = self.polychrome_sprites_start;
346                while self
347                    .polychrome_sprites_iter
348                    .next_if(|sprite| {
349                        sprite.order <= max_order && sprite.tile.texture_id == texture_id
350                    })
351                    .is_some()
352                {
353                    sprites_end += 1;
354                }
355                self.polychrome_sprites_start = sprites_end;
356                Some(PrimitiveBatch::PolychromeSprites {
357                    texture_id,
358                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
359                })
360            }
361        }
362    }
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
366pub enum PrimitiveKind {
367    Shadow,
368    #[default]
369    Quad,
370    Path,
371    Underline,
372    MonochromeSprite,
373    PolychromeSprite,
374}
375
376pub enum Primitive {
377    Shadow(Shadow),
378    Quad(Quad),
379    Path(Path<ScaledPixels>),
380    Underline(Underline),
381    MonochromeSprite(MonochromeSprite),
382    PolychromeSprite(PolychromeSprite),
383}
384
385#[derive(Debug)]
386pub(crate) enum PrimitiveBatch<'a> {
387    Shadows(&'a [Shadow]),
388    Quads(&'a [Quad]),
389    Paths(&'a [Path<ScaledPixels>]),
390    Underlines(&'a [Underline]),
391    MonochromeSprites {
392        texture_id: AtlasTextureId,
393        sprites: &'a [MonochromeSprite],
394    },
395    PolychromeSprites {
396        texture_id: AtlasTextureId,
397        sprites: &'a [PolychromeSprite],
398    },
399}
400
401#[derive(Default, Debug, Clone, Eq, PartialEq)]
402#[repr(C)]
403pub struct Quad {
404    pub order: u32, // Initially a LayerId, then a DrawOrder.
405    pub bounds: Bounds<ScaledPixels>,
406    pub content_mask: ContentMask<ScaledPixels>,
407    pub background: Hsla,
408    pub border_color: Hsla,
409    pub corner_radii: Corners<ScaledPixels>,
410    pub border_widths: Edges<ScaledPixels>,
411}
412
413impl Ord for Quad {
414    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
415        self.order.cmp(&other.order)
416    }
417}
418
419impl PartialOrd for Quad {
420    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
421        Some(self.cmp(other))
422    }
423}
424
425impl From<Quad> for Primitive {
426    fn from(quad: Quad) -> Self {
427        Primitive::Quad(quad)
428    }
429}
430
431#[derive(Debug, Clone, Eq, PartialEq)]
432#[repr(C)]
433pub struct Underline {
434    pub order: u32,
435    pub bounds: Bounds<ScaledPixels>,
436    pub content_mask: ContentMask<ScaledPixels>,
437    pub thickness: ScaledPixels,
438    pub color: Hsla,
439    pub wavy: bool,
440}
441
442impl Ord for Underline {
443    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
444        self.order.cmp(&other.order)
445    }
446}
447
448impl PartialOrd for Underline {
449    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
450        Some(self.cmp(other))
451    }
452}
453
454impl From<Underline> for Primitive {
455    fn from(underline: Underline) -> Self {
456        Primitive::Underline(underline)
457    }
458}
459
460#[derive(Debug, Clone, Eq, PartialEq)]
461#[repr(C)]
462pub struct Shadow {
463    pub order: u32,
464    pub bounds: Bounds<ScaledPixels>,
465    pub corner_radii: Corners<ScaledPixels>,
466    pub content_mask: ContentMask<ScaledPixels>,
467    pub color: Hsla,
468    pub blur_radius: ScaledPixels,
469}
470
471impl Ord for Shadow {
472    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
473        self.order.cmp(&other.order)
474    }
475}
476
477impl PartialOrd for Shadow {
478    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
479        Some(self.cmp(other))
480    }
481}
482
483impl From<Shadow> for Primitive {
484    fn from(shadow: Shadow) -> Self {
485        Primitive::Shadow(shadow)
486    }
487}
488
489#[derive(Clone, Debug, Eq, PartialEq)]
490#[repr(C)]
491pub struct MonochromeSprite {
492    pub order: u32,
493    pub bounds: Bounds<ScaledPixels>,
494    pub content_mask: ContentMask<ScaledPixels>,
495    pub color: Hsla,
496    pub tile: AtlasTile,
497}
498
499impl Ord for MonochromeSprite {
500    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
501        match self.order.cmp(&other.order) {
502            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
503            order => order,
504        }
505    }
506}
507
508impl PartialOrd for MonochromeSprite {
509    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
510        Some(self.cmp(other))
511    }
512}
513
514impl From<MonochromeSprite> for Primitive {
515    fn from(sprite: MonochromeSprite) -> Self {
516        Primitive::MonochromeSprite(sprite)
517    }
518}
519
520#[derive(Clone, Debug, Eq, PartialEq)]
521#[repr(C)]
522pub struct PolychromeSprite {
523    pub order: u32,
524    pub bounds: Bounds<ScaledPixels>,
525    pub content_mask: ContentMask<ScaledPixels>,
526    pub corner_radii: Corners<ScaledPixels>,
527    pub tile: AtlasTile,
528    pub grayscale: bool,
529}
530
531impl Ord for PolychromeSprite {
532    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
533        match self.order.cmp(&other.order) {
534            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
535            order => order,
536        }
537    }
538}
539
540impl PartialOrd for PolychromeSprite {
541    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
542        Some(self.cmp(other))
543    }
544}
545
546impl From<PolychromeSprite> for Primitive {
547    fn from(sprite: PolychromeSprite) -> Self {
548        Primitive::PolychromeSprite(sprite)
549    }
550}
551
552#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
553pub(crate) struct PathId(pub(crate) usize);
554
555#[derive(Debug)]
556pub struct Path<P: Clone + Debug> {
557    pub(crate) id: PathId,
558    order: u32,
559    pub(crate) bounds: Bounds<P>,
560    pub(crate) vertices: Vec<PathVertex<P>>,
561    start: Option<Point<P>>,
562    current: Point<P>,
563}
564
565impl Path<Pixels> {
566    pub fn new() -> Self {
567        Self {
568            id: PathId(0),
569            order: 0,
570            vertices: Vec::new(),
571            start: Default::default(),
572            current: Default::default(),
573            bounds: Default::default(),
574        }
575    }
576
577    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
578        Path {
579            id: self.id,
580            order: self.order,
581            bounds: self.bounds.scale(factor),
582            vertices: self
583                .vertices
584                .iter()
585                .map(|vertex| vertex.scale(factor))
586                .collect(),
587            start: self.start.map(|start| start.scale(factor)),
588            current: self.current.scale(factor),
589        }
590    }
591
592    pub fn line_to(&mut self, to: Point<Pixels>) {
593        if let Some(start) = self.start {
594            self.push_triangle(
595                (start, self.current, to),
596                (point(0., 1.), point(0., 1.), point(0., 1.)),
597            );
598        } else {
599            self.start = Some(to);
600        }
601        self.current = to;
602    }
603
604    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
605        self.line_to(to);
606        self.push_triangle(
607            (self.current, ctrl, to),
608            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
609        );
610    }
611
612    fn push_triangle(
613        &mut self,
614        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
615        st: (Point<f32>, Point<f32>, Point<f32>),
616    ) {
617        if self.vertices.is_empty() {
618            self.bounds = Bounds {
619                origin: xy.0,
620                size: Default::default(),
621            };
622        }
623        self.bounds = self
624            .bounds
625            .union(&Bounds {
626                origin: xy.0,
627                size: Default::default(),
628            })
629            .union(&Bounds {
630                origin: xy.1,
631                size: Default::default(),
632            })
633            .union(&Bounds {
634                origin: xy.2,
635                size: Default::default(),
636            });
637
638        self.vertices.push(PathVertex {
639            xy_position: xy.0,
640            st_position: st.0,
641            content_mask: Default::default(),
642        });
643        self.vertices.push(PathVertex {
644            xy_position: xy.1,
645            st_position: st.1,
646            content_mask: Default::default(),
647        });
648        self.vertices.push(PathVertex {
649            xy_position: xy.2,
650            st_position: st.2,
651            content_mask: Default::default(),
652        });
653    }
654}
655
656impl Eq for Path<ScaledPixels> {}
657
658impl PartialEq for Path<ScaledPixels> {
659    fn eq(&self, other: &Self) -> bool {
660        self.order == other.order
661    }
662}
663
664impl Ord for Path<ScaledPixels> {
665    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
666        self.order.cmp(&other.order)
667    }
668}
669
670impl PartialOrd for Path<ScaledPixels> {
671    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
672        Some(self.cmp(other))
673    }
674}
675
676impl From<Path<ScaledPixels>> for Primitive {
677    fn from(path: Path<ScaledPixels>) -> Self {
678        Primitive::Path(path)
679    }
680}
681
682#[derive(Clone, Debug)]
683#[repr(C)]
684pub struct PathVertex<P: Clone + Debug> {
685    pub(crate) xy_position: Point<P>,
686    pub(crate) st_position: Point<f32>,
687    pub(crate) content_mask: ContentMask<P>,
688}
689
690impl PathVertex<Pixels> {
691    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
692        PathVertex {
693            xy_position: self.xy_position.scale(factor),
694            st_position: self.st_position,
695            content_mask: self.content_mask.scale(factor),
696        }
697    }
698}
699
700#[derive(Copy, Clone, Debug)]
701pub struct AtlasId(pub(crate) usize);
702
703impl Bounds<ScaledPixels> {
704    fn to_bsp_polygon<A: Copy>(&self, z: f32, anchor: A) -> BspPolygon<A> {
705        let upper_left = self.origin;
706        let upper_right = self.upper_right();
707        let lower_right = self.lower_right();
708        let lower_left = self.lower_left();
709
710        BspPolygon::from_points(
711            [
712                Point3D::new(upper_left.x.into(), upper_left.y.into(), z as f64),
713                Point3D::new(upper_right.x.into(), upper_right.y.into(), z as f64),
714                Point3D::new(lower_right.x.into(), lower_right.y.into(), z as f64),
715                Point3D::new(lower_left.x.into(), lower_left.y.into(), z as f64),
716            ],
717            anchor,
718        )
719        .expect("Polygon should not be empty")
720    }
721}
722
723#[cfg(test)]
724mod tests {
725    use crate::{point, size};
726
727    use super::*;
728    use smallvec::smallvec;
729
730    #[test]
731    fn test_scene() {
732        let mut scene = SceneBuilder::new();
733        assert_eq!(scene.layers_by_order.len(), 0);
734
735        scene.insert(&smallvec![1].into(), quad());
736        scene.insert(&smallvec![2].into(), shadow());
737        scene.insert(&smallvec![3].into(), quad());
738
739        let mut batches_count = 0;
740        for _ in scene.build().batches() {
741            batches_count += 1;
742        }
743        assert_eq!(batches_count, 3);
744    }
745
746    fn quad() -> Quad {
747        Quad {
748            order: 0,
749            bounds: Bounds {
750                origin: point(ScaledPixels(0.), ScaledPixels(0.)),
751                size: size(ScaledPixels(100.), ScaledPixels(100.)),
752            },
753            content_mask: Default::default(),
754            background: Default::default(),
755            border_color: Default::default(),
756            corner_radii: Default::default(),
757            border_widths: Default::default(),
758        }
759    }
760
761    fn shadow() -> Shadow {
762        Shadow {
763            order: Default::default(),
764            bounds: Bounds {
765                origin: point(ScaledPixels(0.), ScaledPixels(0.)),
766                size: size(ScaledPixels(100.), ScaledPixels(100.)),
767            },
768            corner_radii: Default::default(),
769            content_mask: Default::default(),
770            color: Default::default(),
771            blur_radius: Default::default(),
772        }
773    }
774}