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}