1use super::facets::{
6 FacetEntry, FacetId, FacetMap, FacetPtr, RectangleFacet, SpacingFacet, TextFacet,
7 TextFacetOptions,
8};
9use super::group::{GroupId, GroupMap, GroupMember, GroupMemberData};
10use super::layout::{ArrangerPtr, Axis, Flex, FlexBuilder, StackBuilder};
11use super::{raster_for_corner_knockouts, BlendMode, FillRule, LayerGroup, Rendering};
12use crate::color::Color;
13use crate::drawing::{path_for_cursor, FontFace};
14use crate::render::generic::{GenericOrder, OrderError};
15use crate::render::{
16 Composition, Context as RenderContext, Fill, Layer, Order, PreClear, Raster, RenderExt, Style,
17};
18use crate::{Coord, IntPoint, Point, Rect, Size, ViewAssistantContext};
19use anyhow::{bail, Error};
20use euclid::{size2, vec2};
21use fuchsia_trace::duration;
22use std::any::Any;
23use std::collections::{BTreeMap, HashMap};
24use std::fmt::{self, Debug};
25use zx::{AsHandleRef, Event, Signals};
26
27pub const MAX_ORDER: usize = (Order::MAX.as_u32() - 3) as usize;
30
31pub type SceneOrder = GenericOrder<MAX_ORDER>;
33
34impl TryFrom<SceneOrder> for Order {
35 type Error = OrderError;
36
37 fn try_from(order: SceneOrder) -> Result<Self, OrderError> {
38 Self::try_from(order.as_u32())
39 }
40}
41
42struct DirectLayerGroup<'a>(&'a mut Composition);
43
44impl LayerGroup for DirectLayerGroup<'_> {
45 fn clear(&mut self) {
46 self.0.clear();
47 }
48 fn insert(&mut self, order: SceneOrder, layer: Layer) {
49 self.0.insert(Order::try_from(order).unwrap_or_else(|e| panic!("{}", e)), layer);
50 }
51 fn remove(&mut self, order: SceneOrder) {
52 self.0.remove(Order::try_from(order).unwrap_or_else(|e| panic!("{}", e)));
53 }
54}
55
56struct SimpleLayerGroup<'a>(&'a mut BTreeMap<SceneOrder, Layer>);
57
58impl LayerGroup for SimpleLayerGroup<'_> {
59 fn clear(&mut self) {
60 self.0.clear();
61 }
62 fn insert(&mut self, order: SceneOrder, layer: Layer) {
63 self.0.insert(order, layer);
64 }
65 fn remove(&mut self, order: SceneOrder) {
66 self.0.remove(&order);
67 }
68}
69
70fn create_mouse_cursor_raster(render_context: &mut RenderContext) -> Raster {
71 let path = path_for_cursor(Point::zero(), 20.0, render_context);
72 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
73 raster_builder.add(&path, None);
74 raster_builder.build()
75}
76
77fn cursor_layer(cursor_raster: &Raster, position: IntPoint, color: &Color) -> Layer {
78 Layer {
79 raster: cursor_raster.clone().translate(position.to_vector()),
80 clip: None,
81 style: Style {
82 fill_rule: FillRule::NonZero,
83 fill: Fill::Solid(*color),
84 blend_mode: BlendMode::Over,
85 },
86 }
87}
88
89fn cursor_layer_pair(cursor_raster: &Raster, position: IntPoint) -> (Layer, Layer) {
90 let black_pos = position + vec2(-1, -1);
91 (
92 cursor_layer(cursor_raster, position, &Color::fuchsia()),
93 cursor_layer(cursor_raster, black_pos, &Color::new()),
94 )
95}
96
97type LayerMap = BTreeMap<FacetId, BTreeMap<SceneOrder, Layer>>;
98
99pub struct SceneOptions {
101 pub background_color: Color,
103 pub round_scene_corners: bool,
107 pub enable_mouse_cursor: bool,
110 pub root_arranger: Option<ArrangerPtr>,
112 pub mutable: bool,
116 pub animated: bool,
119}
120
121impl SceneOptions {
122 pub fn with_background_color(background_color: Color) -> Self {
124 Self { background_color, ..Self::default() }
125 }
126}
127
128impl Default for SceneOptions {
129 fn default() -> Self {
130 Self {
131 background_color: Color::new(),
132 round_scene_corners: false,
133 enable_mouse_cursor: true,
134 root_arranger: None,
135 mutable: true,
136 animated: false,
137 }
138 }
139}
140
141pub struct Scene {
144 renderings: HashMap<u64, Rendering>,
145 mouse_cursor_raster: Option<Raster>,
146 corner_knockouts_raster: Option<Raster>,
147 facets: FacetMap,
148 facet_order: Vec<FacetId>,
149 groups: GroupMap,
150 layers: LayerMap,
151 composition: Composition,
152 options: SceneOptions,
153}
154
155impl Scene {
156 fn new_from_builder(options: SceneOptions, facets: FacetMap, groups: GroupMap) -> Self {
157 let facet_order: Vec<FacetId> = facets.iter().map(|(facet_id, _)| *facet_id).collect();
158 Self {
159 renderings: HashMap::new(),
160 mouse_cursor_raster: None,
161 corner_knockouts_raster: None,
162 facets,
163 facet_order,
164 groups,
165 layers: LayerMap::new(),
166 composition: Composition::new(options.background_color),
167 options,
168 }
169 }
170
171 pub fn round_scene_corners(&mut self, round_scene_corners: bool) {
173 self.options.round_scene_corners = round_scene_corners;
174 if !round_scene_corners {
175 self.corner_knockouts_raster = None;
176 }
177 }
178
179 pub fn add_facet(&mut self, mut facet: FacetPtr) -> FacetId {
181 assert_eq!(self.options.mutable, true);
182 let facet_id = FacetId::new();
183 facet.associate_facet_id(facet_id);
184 self.facets
185 .insert(facet_id, FacetEntry { facet, location: Point::zero(), size: Size::zero() });
186 self.facet_order.push(facet_id);
187 facet_id
188 }
189
190 pub fn remove_facet(&mut self, facet_id: FacetId) -> Result<(), Error> {
192 assert_eq!(self.options.mutable, true);
193 if let Some(_) = self.facets.remove(&facet_id).as_mut() {
194 self.layers.remove(&facet_id);
195 self.facet_order.retain(|fid| facet_id != *fid);
196 Ok(())
197 } else {
198 bail!("Tried to remove non-existant facet")
199 }
200 }
201
202 pub fn move_facet_forward(&mut self, facet_id: FacetId) -> Result<(), Error> {
204 assert_eq!(self.options.mutable, true);
205 if let Some(index) = self.facet_order.iter().position(|fid| *fid == facet_id) {
206 if index > 0 {
207 let new_index = index - 1;
208 self.facet_order.swap(new_index, index)
209 }
210 Ok(())
211 } else {
212 bail!("Tried to move_facet_forward non-existant facet")
213 }
214 }
215
216 pub fn move_facet_backward(&mut self, facet_id: FacetId) -> Result<(), Error> {
218 assert_eq!(self.options.mutable, true);
219 if let Some(index) = self.facet_order.iter().position(|fid| *fid == facet_id) {
220 if index < self.facet_order.len() - 1 {
221 let new_index = index + 1;
222 self.facet_order.swap(new_index, index)
223 }
224 Ok(())
225 } else {
226 bail!("Tried to move_facet_backward non-existant facet")
227 }
228 }
229
230 pub fn new_group(&mut self) -> GroupId {
232 GroupId::new()
233 }
234
235 pub fn add_facet_to_group(
237 &mut self,
238 facet_id: FacetId,
239 group_id: GroupId,
240 member_data: Option<GroupMemberData>,
241 ) {
242 self.groups.add_facet_to_group(facet_id, group_id, member_data);
243 }
244
245 pub fn remove_facet_from_group(&mut self, facet_id: FacetId, group_id: GroupId) {
247 self.groups.remove_facet_from_group(facet_id, group_id);
248 }
249
250 pub fn set_group_arranger(&mut self, group_id: GroupId, arranger: ArrangerPtr) {
253 self.groups.set_group_arranger(group_id, arranger);
254 }
255
256 pub(crate) fn update_scene_layers(
257 &mut self,
258 render_context: &mut RenderContext,
259 view_context: &ViewAssistantContext,
260 ) {
261 duration!(c"gfx", c"Scene::update_scene_layers");
262
263 for (facet_id, facet_entry) in &mut self.facets {
264 let facet_layers = self.layers.entry(*facet_id).or_insert_with(|| BTreeMap::new());
265 let mut layer_group = SimpleLayerGroup(facet_layers);
266 facet_entry
267 .facet
268 .update_layers(facet_entry.size, &mut layer_group, render_context, view_context)
269 .expect("update_layers");
270 }
271 }
272
273 fn create_or_update_rendering(
274 renderings: &mut HashMap<u64, Rendering>,
275 background_color: Color,
276 context: &ViewAssistantContext,
277 ) -> Option<PreClear> {
278 let image_id = context.image_id;
279 let size_rendering = renderings.entry(image_id).or_insert_with(|| Rendering::new());
280 let size = context.size;
281 if size != size_rendering.size {
282 size_rendering.size = context.size;
283 Some(PreClear { color: background_color })
284 } else {
285 None
286 }
287 }
288
289 fn update_composition(
290 layers: impl IntoIterator<Item = (SceneOrder, Layer)>,
291 mouse_position: &Option<IntPoint>,
292 mouse_cursor_raster: &Option<Raster>,
293 corner_knockouts: &Option<Raster>,
294 composition: &mut Composition,
295 ) {
296 duration!(c"gfx", c"Scene::update_composition");
297
298 for (order, layer) in layers.into_iter() {
299 composition.insert(Order::try_from(order).unwrap_or_else(|e| panic!("{}", e)), layer);
300 }
301
302 const CORNER_KOCKOUTS_ORDER: u32 = Order::MAX.as_u32();
303 const MOUSE_CURSOR_LAYER_0_ORDER: u32 = Order::MAX.as_u32() - 1;
304 const MOUSE_CURSOR_LAYER_1_ORDER: u32 = Order::MAX.as_u32() - 2;
305
306 if let Some(position) = mouse_position {
307 if let Some(raster) = mouse_cursor_raster {
308 let (layer0, layer1) = cursor_layer_pair(raster, *position);
309 composition.insert(
310 Order::try_from(MOUSE_CURSOR_LAYER_0_ORDER).unwrap_or_else(|e| panic!("{}", e)),
311 layer0,
312 );
313 composition.insert(
314 Order::try_from(MOUSE_CURSOR_LAYER_1_ORDER).unwrap_or_else(|e| panic!("{}", e)),
315 layer1,
316 );
317 }
318 }
319
320 if let Some(raster) = corner_knockouts {
321 composition.insert(
322 Order::try_from(CORNER_KOCKOUTS_ORDER).unwrap_or_else(|e| panic!("{}", e)),
323 Layer {
324 raster: raster.clone(),
325 clip: None,
326 style: Style {
327 fill_rule: FillRule::NonZero,
328 fill: Fill::Solid(Color::new()),
329 blend_mode: BlendMode::Over,
330 },
331 },
332 );
333 }
334 }
335
336 fn is_immutable_single_facet_scene_at_origin(&self) -> bool {
337 !self.options.mutable
338 && self.facets.len() == 1
339 && self.facets.iter().next().expect("first facet").1.location == Point::zero()
340 }
341
342 pub fn render(
344 &mut self,
345 render_context: &mut RenderContext,
346 ready_event: Event,
347 context: &ViewAssistantContext,
348 ) -> Result<(), Error> {
349 let image = render_context.get_current_image(context);
350 let background_color = self.options.background_color;
351 let pre_clear =
352 Self::create_or_update_rendering(&mut self.renderings, background_color, context);
353 let size = context.size;
354
355 let ext = RenderExt { pre_clear, ..Default::default() };
356
357 const CORNER_KNOCKOUTS_SIZE: f32 = 10.0;
358
359 if self.options.round_scene_corners && self.corner_knockouts_raster.is_none() {
360 self.corner_knockouts_raster = Some(raster_for_corner_knockouts(
361 &Rect::from_size(size),
362 CORNER_KNOCKOUTS_SIZE,
363 render_context,
364 ));
365 }
366
367 let mouse_cursor_position = if self.options.enable_mouse_cursor {
368 if context.mouse_cursor_position.is_some() && self.mouse_cursor_raster.is_none() {
369 self.mouse_cursor_raster = Some(create_mouse_cursor_raster(render_context));
370 }
371 &context.mouse_cursor_position
372 } else {
373 &None
374 };
375
376 if self.is_immutable_single_facet_scene_at_origin() {
379 let (_, facet_entry) = self.facets.iter_mut().next().expect("first facet");
380 let composition = &mut self.composition;
381 let mut layer_group = DirectLayerGroup(composition);
382 facet_entry
383 .facet
384 .update_layers(size, &mut layer_group, render_context, context)
385 .expect("update_layers");
386
387 Self::update_composition(
388 std::iter::empty(),
389 mouse_cursor_position,
390 &self.mouse_cursor_raster,
391 &self.corner_knockouts_raster,
392 composition,
393 );
394 } else {
395 self.update_scene_layers(render_context, context);
396
397 let composition = &mut self.composition;
398 let facet_order = &self.facet_order;
399 let facets = &self.facets;
400 let layers = &self.layers;
401
402 composition.clear();
404
405 let mut next_origin: u32 = 0;
406 let layers = facet_order.iter().rev().flat_map(|facet_id| {
407 let facet_entry = facets.get(facet_id).expect("facets");
408 let facet_translation = facet_entry.location.to_vector().to_i32();
409 let facet_layers = layers.get(facet_id).expect("layers");
410 let facet_origin = next_origin;
411
412 next_origin += facet_layers.keys().next_back().map(|o| o.as_u32() + 1).unwrap_or(0);
414
415 facet_layers.iter().map(move |(order, layer)| {
416 (
417 SceneOrder::try_from(facet_origin + order.as_u32())
418 .unwrap_or_else(|e| panic!("{}", e)),
419 Layer {
420 raster: layer.raster.clone().translate(facet_translation),
421 clip: layer.clip.clone().map(|c| c.translate(facet_translation)),
422 style: layer.style.clone(),
423 },
424 )
425 })
426 });
427
428 Self::update_composition(
429 layers,
430 mouse_cursor_position,
431 &self.mouse_cursor_raster,
432 &self.corner_knockouts_raster,
433 composition,
434 );
435 }
436
437 render_context.render(&mut self.composition, None, image, &ext);
438 ready_event.as_handle_ref().signal(Signals::NONE, Signals::EVENT_SIGNALED)?;
439
440 Ok(())
441 }
442
443 pub fn send_message(&mut self, target: &FacetId, msg: Box<dyn Any>) {
445 if let Some(facet_entry) = self.facets.get_mut(target) {
446 facet_entry.facet.handle_message(msg);
447 }
448 }
449
450 pub fn set_facet_location(&mut self, target: &FacetId, location: Point) {
452 assert_eq!(self.options.mutable, true);
453 if let Some(facet_entry) = self.facets.get_mut(target) {
454 facet_entry.location = location;
455 }
456 }
457
458 pub fn set_facet_size(&mut self, target: &FacetId, size: Size) {
460 assert_eq!(self.options.mutable, true);
461 if let Some(facet_entry) = self.facets.get_mut(target) {
462 facet_entry.size = size;
463 }
464 }
465
466 pub fn get_facet_location(&self, target: &FacetId) -> Point {
468 self.facets
469 .get(target)
470 .and_then(|facet_entry| Some(facet_entry.location))
471 .unwrap_or_else(Point::zero)
472 }
473
474 pub fn get_facet_size(&self, target: &FacetId) -> Size {
476 self.facets
477 .get(target)
478 .and_then(|facet_entry| Some(facet_entry.size))
479 .expect("get_facet_size")
480 }
481
482 pub(crate) fn calculate_facet_size(&self, target: &FacetId, available: Size) -> Size {
483 self.facets
484 .get(target)
485 .and_then(|facet_entry| Some(facet_entry.facet.calculate_size(available)))
486 .expect("calculate_facet_size")
487 }
488
489 pub fn get_facet_bounds(&self, target: &FacetId) -> Rect {
491 Rect::new(self.get_facet_location(target), self.get_facet_size(target))
492 }
493
494 fn position_group(
495 &mut self,
496 origin: Point,
497 group_id: GroupId,
498 size_map: &HashMap<GroupMember, Size>,
499 ) {
500 let members = self.groups.group_members(group_id);
501 let group_size = size_map.get(&GroupMember::Group(group_id)).expect("group size");
502 let sizes: Vec<_> =
503 members.iter().map(|member| *size_map.get(member).expect("members size")).collect();
504 if let Some(arranger) = self.groups.group_arranger(group_id) {
505 let member_data = self.groups.group_member_data(group_id);
506 let positions = arranger.arrange(*group_size, &sizes, &member_data);
507 let member_ids = self.groups.group_members(group_id);
508 for (pos, member_id) in positions.iter().zip(member_ids.iter()) {
509 match member_id {
510 GroupMember::Facet(facet_id) => {
511 let size =
512 size_map.get(member_id).map_or_else(|| size2(10.0, 10.0), |size| *size);
513 self.set_facet_size(facet_id, size);
514 self.set_facet_location(facet_id, *pos + origin.to_vector())
515 }
516 GroupMember::Group(member_group_id) => {
517 self.position_group(*pos + origin.to_vector(), *member_group_id, size_map)
518 }
519 }
520 }
521 } else {
522 println!("no arranger");
523 }
524 }
525
526 pub fn layout(&mut self, target_size: Size) {
528 let size_map = self.groups.calculate_size_map(target_size, self);
529 self.position_group(Point::zero(), self.groups.get_root_group_id(), &size_map);
530 }
531
532 pub fn all_facet_bounds(&self) -> Vec<Rect> {
534 self.facet_order
535 .iter()
536 .filter_map(|facet_id| {
537 self.facets
538 .get(facet_id)
539 .and_then(|facet_entry| Some(Rect::new(facet_entry.location, facet_entry.size)))
540 })
541 .collect()
542 }
543
544 pub(crate) fn is_animated(&self) -> bool {
545 self.options.animated
546 }
547}
548
549impl Debug for Scene {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 f.debug_struct("Scene").field("groups", &self.groups).finish()
552 }
553}
554
555pub struct GroupBuilder<'a> {
557 pub(crate) builder: &'a mut SceneBuilder,
558 label: String,
559 pub(crate) arranger: Option<ArrangerPtr>,
560}
561
562impl<'a> GroupBuilder<'a> {
563 pub(crate) fn new(builder: &'a mut SceneBuilder) -> Self {
564 Self { builder, label: String::from(""), arranger: None }
565 }
566
567 pub fn label(mut self, label: &str) -> Self {
569 self.label = String::from(label);
570 self
571 }
572
573 pub fn row_with_member_data(self, member_data: Option<GroupMemberData>) -> FlexBuilder<'a> {
575 FlexBuilder::new(self, Axis::Horizontal, member_data)
576 }
577
578 pub fn row(self) -> FlexBuilder<'a> {
580 self.row_with_member_data(None)
581 }
582
583 pub fn column_with_member_data(self, member_data: Option<GroupMemberData>) -> FlexBuilder<'a> {
585 FlexBuilder::new(self, Axis::Vertical, member_data)
586 }
587
588 pub fn column(self) -> FlexBuilder<'a> {
590 self.column_with_member_data(None)
591 }
592
593 pub fn stack(self) -> StackBuilder<'a> {
595 StackBuilder::new(self)
596 }
597
598 pub fn contents<F>(self, mut f: F) -> GroupId
601 where
602 F: FnMut(&mut SceneBuilder),
603 {
604 self.builder.start_group(&self.label, self.arranger.unwrap_or_else(Flex::new_ptr));
605 f(self.builder);
606 self.builder.end_group()
607 }
608
609 pub fn contents_with_member_data<F>(
612 self,
613 member_data: Option<GroupMemberData>,
614 mut f: F,
615 ) -> GroupId
616 where
617 F: FnMut(&mut SceneBuilder),
618 {
619 self.builder.start_group_with_member_data(
620 &self.label,
621 self.arranger.unwrap_or_else(Flex::new_ptr),
622 member_data,
623 );
624 f(self.builder);
625 self.builder.end_group()
626 }
627}
628
629pub struct SceneBuilder {
631 options: SceneOptions,
632 facets: FacetMap,
633 groups: GroupMap,
634 group_stack: Vec<GroupId>,
635}
636
637impl SceneBuilder {
638 pub fn new() -> Self {
640 Self {
641 options: SceneOptions::default(),
642 facets: FacetMap::new(),
643 groups: GroupMap::new(),
644 group_stack: vec![],
645 }
646 }
647
648 pub fn round_scene_corners(mut self, round: bool) -> Self {
652 self.options.round_scene_corners = round;
653 self
654 }
655
656 pub fn enable_mouse_cursor(mut self, enable: bool) -> Self {
659 self.options.enable_mouse_cursor = enable;
660 self
661 }
662
663 pub fn background_color(mut self, background_color: Color) -> Self {
665 self.options.background_color = background_color;
666 self
667 }
668
669 pub fn mutable(mut self, mutable: bool) -> Self {
671 self.options.mutable = mutable;
672 self
673 }
674
675 pub fn animated(mut self, animated: bool) -> Self {
677 self.options.animated = animated;
678 self
679 }
680
681 fn allocate_facet_id(&mut self) -> FacetId {
682 FacetId::new()
683 }
684
685 fn push_facet(
686 &mut self,
687 mut facet: FacetPtr,
688 location: Point,
689 member_data: Option<GroupMemberData>,
690 ) -> FacetId {
691 let facet_id = self.allocate_facet_id();
692 facet.associate_facet_id(facet_id);
693 self.facets.insert(facet_id.clone(), FacetEntry { facet, location, size: Size::zero() });
694 if let Some(group_id) = self.group_stack.last() {
695 self.groups.add_facet_to_group(facet_id, *group_id, member_data);
696 }
697 facet_id
698 }
699
700 pub fn rectangle(&mut self, size: Size, color: Color) -> FacetId {
702 self.push_facet(RectangleFacet::new(size, color), Point::zero(), None)
703 }
704
705 pub fn rounded_rectangle(&mut self, size: Size, corner: Coord, color: Color) -> FacetId {
707 self.push_facet(RectangleFacet::new_rounded(size, corner, color), Point::zero(), None)
708 }
709
710 pub fn space(&mut self, size: Size) -> FacetId {
712 self.push_facet(Box::new(SpacingFacet::new(size)), Point::zero(), None)
713 }
714
715 pub fn h_line(
717 &mut self,
718 width: Coord,
719 thickness: Coord,
720 color: Color,
721 location: Option<Point>,
722 ) -> FacetId {
723 self.h_line_with_data(width, thickness, color, location, None)
724 }
725
726 pub fn h_line_with_data(
728 &mut self,
729 width: Coord,
730 thickness: Coord,
731 color: Color,
732 location: Option<Point>,
733 member_data: Option<GroupMemberData>,
734 ) -> FacetId {
735 self.push_facet(
736 RectangleFacet::h_line(width, thickness, color),
737 location.unwrap_or_else(Point::zero),
738 member_data,
739 )
740 }
741
742 pub fn v_line(
744 &mut self,
745 height: Coord,
746 thickness: Coord,
747 color: Color,
748 location: Option<Point>,
749 ) -> FacetId {
750 self.v_line_with_data(height, thickness, color, location, None)
751 }
752
753 pub fn v_line_with_data(
755 &mut self,
756 height: Coord,
757 thickness: Coord,
758 color: Color,
759 location: Option<Point>,
760 member_data: Option<GroupMemberData>,
761 ) -> FacetId {
762 self.push_facet(
763 RectangleFacet::v_line(height, thickness, color),
764 location.unwrap_or_else(Point::zero),
765 member_data,
766 )
767 }
768
769 pub fn text(
771 &mut self,
772 face: FontFace,
773 text: &str,
774 size: f32,
775 location: Point,
776 options: TextFacetOptions,
777 ) -> FacetId {
778 self.text_with_data(face, text, size, location, options, None)
779 }
780
781 pub fn text_with_data(
784 &mut self,
785 face: FontFace,
786 text: &str,
787 size: f32,
788 location: Point,
789 options: TextFacetOptions,
790 member_data: Option<GroupMemberData>,
791 ) -> FacetId {
792 self.push_facet(TextFacet::with_options(face, text, size, options), location, member_data)
793 }
794
795 pub fn facet_with_data(
798 &mut self,
799 facet: FacetPtr,
800 member_data: Option<GroupMemberData>,
801 ) -> FacetId {
802 self.push_facet(facet, Point::zero(), member_data)
803 }
804
805 pub fn facet(&mut self, facet: FacetPtr) -> FacetId {
807 self.push_facet(facet, Point::zero(), None)
808 }
809
810 pub fn facet_at_location(&mut self, facet: FacetPtr, location: Point) -> FacetId {
813 self.push_facet(facet, location, None)
814 }
815
816 pub fn start_group_with_member_data(
819 &mut self,
820 label: &str,
821 arranger: ArrangerPtr,
822 member_data: Option<GroupMemberData>,
823 ) {
824 let group_id = GroupId::new();
825 self.groups.start_group(group_id, label, arranger, self.group_stack.last(), member_data);
826 self.group_stack.push(group_id);
827 }
828
829 pub fn start_group(&mut self, label: &str, arranger: ArrangerPtr) {
832 self.start_group_with_member_data(label, arranger, None);
833 }
834
835 pub fn end_group(&mut self) -> GroupId {
837 self.group_stack.pop().expect("group stack to always have a group ID")
838 }
839
840 pub fn group(&mut self) -> GroupBuilder<'_> {
842 GroupBuilder::new(self)
843 }
844
845 pub fn build(mut self) -> Scene {
847 while self.group_stack.len() > 0 {
848 self.end_group();
849 }
850 Scene::new_from_builder(self.options, self.facets, self.groups)
851 }
852}