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}