jagua_rs/entities/
layout.rs1use crate::collision_detection::hazards::{Hazard, HazardEntity};
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 self.container = container;
42 self.cde = self.container.base_cde.as_ref().clone();
44 for (pk, pi) in self.placed_items.iter() {
45 let hazard = Hazard::new((pk, pi).into(), pi.shape.clone());
46 self.cde.register_hazard(hazard);
47 }
48 }
49
50 pub fn save(&mut self) -> LayoutSnapshot {
52 LayoutSnapshot {
53 container: self.container.clone(),
54 placed_items: self.placed_items.clone(),
55 cde_snapshot: self.cde.create_snapshot(),
56 }
57 }
58
59 pub fn restore(&mut self, layout_snapshot: &LayoutSnapshot) {
61 assert_eq!(self.container.id, layout_snapshot.container.id);
62 self.placed_items = layout_snapshot.placed_items.clone();
63 self.cde.restore(&layout_snapshot.cde_snapshot);
64
65 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
66 debug_assert!(assertions::layouts_match(self, layout_snapshot))
67 }
68
69 pub fn place_item(&mut self, item: &Item, d_transformation: DTransformation) -> PItemKey {
72 let pk = self
73 .placed_items
74 .insert(PlacedItem::new(item, d_transformation));
75 let pi = &self.placed_items[pk];
76 let hazard = Hazard::new((pk, pi).into(), pi.shape.clone());
77
78 self.cde.register_hazard(hazard);
79
80 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
81
82 pk
83 }
84
85 pub fn remove_item(&mut self, pk: PItemKey, commit_instant: bool) -> PlacedItem {
90 let pi = self
91 .placed_items
92 .remove(pk)
93 .expect("key is not valid anymore");
94
95 self.cde.deregister_hazard((pk, &pi).into(), commit_instant);
97
98 debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
99
100 pi
101 }
102
103 pub fn is_empty(&self) -> bool {
105 self.placed_items.is_empty()
106 }
107
108 pub fn density(&self, instance: &impl Instance) -> f32 {
111 self.placed_item_area(instance) / self.container.area()
112 }
113
114 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
116 self.placed_items
117 .iter()
118 .map(|(_, pi)| instance.item(pi.item_id))
119 .map(|item| item.area())
120 .sum::<f32>()
121 }
122
123 pub fn cde(&self) -> &CDEngine {
125 &self.cde
126 }
127
128 pub fn is_feasible(&self) -> bool {
130 self.placed_items.iter().all(|(pk, pi)| {
131 let filter = HazardEntity::from((pk, pi));
132 !self.cde.detect_poly_collision(&pi.shape, &filter)
133 })
134 }
135}
136
137#[derive(Clone, Debug)]
140pub struct LayoutSnapshot {
141 pub container: Container,
143 pub placed_items: SlotMap<PItemKey, PlacedItem>,
145 pub cde_snapshot: CDESnapshot,
147}
148
149impl LayoutSnapshot {
150 pub fn density(&self, instance: &impl Instance) -> f32 {
152 self.placed_item_area(instance) / self.container.area()
153 }
154
155 pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
157 self.placed_items
158 .iter()
159 .map(|(_, pi)| instance.item(pi.item_id))
160 .map(|item| item.area())
161 .sum::<f32>()
162 }
163}