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
64 self.placed_items = layout_snapshot.placed_items.clone();
65 self.cde.restore(&layout_snapshot.cde_snapshot);
66
67 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
68 debug_assert!(assertions::snapshot_matches_layout(self, layout_snapshot))
69 }
70
71 pub fn place_item(&mut self, item: &Item, d_transformation: DTransformation) -> PItemKey {
74 let pk = self
75 .placed_items
76 .insert(PlacedItem::new(item, d_transformation));
77 let pi = &self.placed_items[pk];
78 let hazard = Hazard::new((pk, pi).into(), pi.shape.clone(), true);
79
80 self.cde.register_hazard(hazard);
81
82 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
83
84 pk
85 }
86
87 pub fn remove_item(&mut self, pk: PItemKey) -> PlacedItem {
89 let pi = self
90 .placed_items
91 .remove(pk)
92 .expect("key is not valid anymore");
93
94 self.cde.deregister_hazard_by_entity((pk, &pi).into());
96
97 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
98
99 pi
100 }
101
102 pub fn is_empty(&self) -> bool {
104 self.placed_items.is_empty()
105 }
106
107 pub fn density(&self, instance: &impl Instance) -> f32 {
110 self.placed_item_area(instance) / self.container.area()
111 }
112
113 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
115 self.placed_items
116 .iter()
117 .map(|(_, pi)| instance.item(pi.item_id))
118 .map(|item| item.area())
119 .sum::<f32>()
120 }
121
122 pub fn cde(&self) -> &CDEngine {
124 &self.cde
125 }
126
127 pub fn is_feasible(&self) -> bool {
129 self.placed_items.iter().all(|(pk, pi)| {
130 let hkey = self
131 .cde
132 .haz_key_from_pi_key(pk)
133 .expect("all placed items should be registered in the CDE");
134 !self.cde.detect_poly_collision(&pi.shape, &hkey)
135 })
136 }
137}
138
139#[derive(Clone, Debug)]
142pub struct LayoutSnapshot {
143 pub container: Container,
145 pub placed_items: SlotMap<PItemKey, PlacedItem>,
147 pub cde_snapshot: CDESnapshot,
149}
150
151impl LayoutSnapshot {
152 pub fn density(&self, instance: &impl Instance) -> f32 {
154 self.placed_item_area(instance) / self.container.area()
155 }
156
157 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
159 self.placed_items
160 .iter()
161 .map(|(_, pi)| instance.item(pi.item_id))
162 .map(|item| item.area())
163 .sum::<f32>()
164 }
165}