jagua_rs/entities/
layout.rs1use crate::collision_detection::hazards::Hazard;
2use crate::collision_detection::{CDESnapshot, CDEngine};
3use crate::entities::Item;
4use crate::entities::{Container, Instance};
5use crate::entities::{PItemKey, PlacedItem};
6use crate::geometry::DTransformation;
7use crate::util::assertions;
8use slotmap::SlotMap;
9
10#[derive(Clone)]
14pub struct Layout {
15 pub container: Container,
17 pub placed_items: SlotMap<PItemKey, PlacedItem>,
19 cde: CDEngine,
21}
22
23impl Layout {
24 #[must_use]
25 pub fn new(container: Container) -> Self {
26 let cde = container.base_cde.as_ref().clone();
27 Layout {
28 container,
29 placed_items: SlotMap::with_key(),
30 cde,
31 }
32 }
33
34 #[must_use]
35 pub fn from_snapshot(ls: &LayoutSnapshot) -> Self {
36 let mut layout = Layout::new(ls.container.clone());
37 layout.restore(ls);
38 layout
39 }
40
41 pub fn swap_container(&mut self, container: Container) {
43 let cde_snapshot = self.cde.save();
44 self.container = container;
46 self.cde = self.container.base_cde.as_ref().clone();
47 for hazard in cde_snapshot.dynamic_hazards {
48 self.cde.register_hazard(hazard);
50 }
51 }
52
53 #[must_use]
55 pub fn save(&self) -> LayoutSnapshot {
56 LayoutSnapshot {
57 container: self.container.clone(),
58 placed_items: self.placed_items.clone(),
59 cde_snapshot: self.cde.save(),
60 }
61 }
62
63 pub fn restore(&mut self, layout_snapshot: &LayoutSnapshot) {
65 assert_eq!(self.container.id, layout_snapshot.container.id);
66
67 self.placed_items.clone_from(&layout_snapshot.placed_items);
68 self.cde.restore(&layout_snapshot.cde_snapshot);
69
70 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
71 debug_assert!(assertions::snapshot_matches_layout(self, layout_snapshot));
72 }
73
74 pub fn place_item(&mut self, item: &Item, d_transformation: DTransformation) -> PItemKey {
77 let pk = self
78 .placed_items
79 .insert(PlacedItem::new(item, d_transformation));
80 let pi = &self.placed_items[pk];
81 let hazard = Hazard::new((pk, pi).into(), pi.shape.clone(), true);
82
83 self.cde.register_hazard(hazard);
84
85 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
86
87 pk
88 }
89
90 pub fn remove_item(&mut self, pk: PItemKey) -> PlacedItem {
92 let pi = self
93 .placed_items
94 .remove(pk)
95 .expect("key is not valid anymore");
96
97 self.cde.deregister_hazard_by_entity((pk, &pi).into());
99
100 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
101
102 pi
103 }
104
105 #[must_use]
107 pub fn is_empty(&self) -> bool {
108 self.placed_items.is_empty()
109 }
110
111 pub fn density(&self, instance: &impl Instance) -> f32 {
114 self.placed_item_area(instance) / self.container.area()
115 }
116
117 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
119 self.placed_items
120 .iter()
121 .map(|(_, pi)| instance.item(pi.item_id))
122 .map(Item::area)
123 .sum::<f32>()
124 }
125
126 #[must_use]
128 pub fn cde(&self) -> &CDEngine {
129 &self.cde
130 }
131
132 #[must_use]
134 pub fn is_feasible(&self) -> bool {
135 self.placed_items.iter().all(|(pk, pi)| {
136 let hkey = self
137 .cde
138 .haz_key_from_pi_key(pk)
139 .expect("all placed items should be registered in the CDE");
140 !self.cde.detect_poly_collision(&pi.shape, &hkey)
141 })
142 }
143}
144
145#[derive(Clone, Debug)]
148pub struct LayoutSnapshot {
149 pub container: Container,
151 pub placed_items: SlotMap<PItemKey, PlacedItem>,
153 pub cde_snapshot: CDESnapshot,
155}
156
157impl LayoutSnapshot {
158 pub fn density(&self, instance: &impl Instance) -> f32 {
160 self.placed_item_area(instance) / self.container.area()
161 }
162
163 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
165 self.placed_items
166 .iter()
167 .map(|(_, pi)| instance.item(pi.item_id))
168 .map(Item::area)
169 .sum::<f32>()
170 }
171}