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 pub fn new(container: Container) -> Self {
25 let cde = container.base_cde.as_ref().clone();
26 Layout {
27 container,
28 placed_items: SlotMap::with_key(),
29 cde,
30 }
31 }
32
33 pub fn from_snapshot(ls: &LayoutSnapshot) -> Self {
34 let mut layout = Layout::new(ls.container.clone());
35 layout.restore(ls);
36 layout
37 }
38
39 pub fn swap_container(&mut self, container: Container) {
41 let cde_snapshot = self.cde.save();
42 self.container = container;
44 self.cde = self.container.base_cde.as_ref().clone();
45 for hazard in cde_snapshot.dynamic_hazards {
46 self.cde.register_hazard(hazard);
48 }
49 }
50
51 pub fn save(&self) -> LayoutSnapshot {
53 LayoutSnapshot {
54 container: self.container.clone(),
55 placed_items: self.placed_items.clone(),
56 cde_snapshot: self.cde.save(),
57 }
58 }
59
60 pub fn restore(&mut self, layout_snapshot: &LayoutSnapshot) {
62 assert_eq!(self.container.id, layout_snapshot.container.id);
63 self.placed_items = layout_snapshot.placed_items.clone();
64 self.cde.restore(&layout_snapshot.cde_snapshot);
65
66 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
67 debug_assert!(assertions::layouts_match(self, layout_snapshot))
68 }
69
70 pub fn place_item(&mut self, item: &Item, d_transformation: DTransformation) -> PItemKey {
73 let pk = self
74 .placed_items
75 .insert(PlacedItem::new(item, d_transformation));
76 let pi = &self.placed_items[pk];
77 let hazard = Hazard::new((pk, pi).into(), pi.shape.clone(), true);
78
79 self.cde.register_hazard(hazard);
80
81 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
82
83 pk
84 }
85
86 pub fn remove_item(&mut self, pk: PItemKey) -> PlacedItem {
91 let pi = self
92 .placed_items
93 .remove(pk)
94 .expect("key is not valid anymore");
95
96 self.cde.deregister_hazard_by_entity((pk, &pi).into());
98
99 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
100
101 pi
102 }
103
104 pub fn is_empty(&self) -> bool {
106 self.placed_items.is_empty()
107 }
108
109 pub fn density(&self, instance: &impl Instance) -> f32 {
112 self.placed_item_area(instance) / self.container.area()
113 }
114
115 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
117 self.placed_items
118 .iter()
119 .map(|(_, pi)| instance.item(pi.item_id))
120 .map(|item| item.area())
121 .sum::<f32>()
122 }
123
124 pub fn cde(&self) -> &CDEngine {
126 &self.cde
127 }
128
129 pub fn is_feasible(&self) -> bool {
131 self.placed_items.iter().all(|(pk, pi)| {
132 let hkey = self
133 .cde
134 .haz_key_from_pi_key(pk)
135 .expect("all placed items should be registered in the CDE");
136 !self.cde.detect_poly_collision(&pi.shape, &hkey)
137 })
138 }
139}
140
141#[derive(Clone, Debug)]
144pub struct LayoutSnapshot {
145 pub container: Container,
147 pub placed_items: SlotMap<PItemKey, PlacedItem>,
149 pub cde_snapshot: CDESnapshot,
151}
152
153impl LayoutSnapshot {
154 pub fn density(&self, instance: &impl Instance) -> f32 {
156 self.placed_item_area(instance) / self.container.area()
157 }
158
159 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
161 self.placed_items
162 .iter()
163 .map(|(_, pi)| instance.item(pi.item_id))
164 .map(|item| item.area())
165 .sum::<f32>()
166 }
167}