use crate::collision_detection::hazards::Hazard;
use crate::collision_detection::{CDESnapshot, CDEngine};
use crate::entities::Item;
use crate::entities::{Container, Instance};
use crate::entities::{PItemKey, PlacedItem};
use crate::geometry::DTransformation;
use crate::util::assertions;
use slotmap::SlotMap;
#[derive(Clone)]
pub struct Layout {
pub container: Container,
pub placed_items: SlotMap<PItemKey, PlacedItem>,
cde: CDEngine,
}
impl Layout {
pub fn new(container: Container) -> Self {
let cde = container.base_cde.as_ref().clone();
Layout {
container,
placed_items: SlotMap::with_key(),
cde,
}
}
pub fn from_snapshot(ls: &LayoutSnapshot) -> Self {
let mut layout = Layout::new(ls.container.clone());
layout.restore(ls);
layout
}
pub fn swap_container(&mut self, container: Container) {
let cde_snapshot = self.cde.save();
self.container = container;
self.cde = self.container.base_cde.as_ref().clone();
for hazard in cde_snapshot.dynamic_hazards {
self.cde.register_hazard(hazard);
}
}
pub fn save(&self) -> LayoutSnapshot {
LayoutSnapshot {
container: self.container.clone(),
placed_items: self.placed_items.clone(),
cde_snapshot: self.cde.save(),
}
}
pub fn restore(&mut self, layout_snapshot: &LayoutSnapshot) {
assert_eq!(self.container.id, layout_snapshot.container.id);
self.placed_items = layout_snapshot.placed_items.clone();
self.cde.restore(&layout_snapshot.cde_snapshot);
debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
debug_assert!(assertions::snapshot_matches_layout(self, layout_snapshot))
}
pub fn place_item(&mut self, item: &Item, d_transformation: DTransformation) -> PItemKey {
let pk = self
.placed_items
.insert(PlacedItem::new(item, d_transformation));
let pi = &self.placed_items[pk];
let hazard = Hazard::new((pk, pi).into(), pi.shape.clone(), true);
self.cde.register_hazard(hazard);
debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
pk
}
pub fn remove_item(&mut self, pk: PItemKey) -> PlacedItem {
let pi = self
.placed_items
.remove(pk)
.expect("key is not valid anymore");
self.cde.deregister_hazard_by_entity((pk, &pi).into());
debug_assert!(assertions::layout_qt_matches_fresh_qt(self));
pi
}
pub fn is_empty(&self) -> bool {
self.placed_items.is_empty()
}
pub fn density(&self, instance: &impl Instance) -> f32 {
self.placed_item_area(instance) / self.container.area()
}
pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
self.placed_items
.iter()
.map(|(_, pi)| instance.item(pi.item_id))
.map(|item| item.area())
.sum::<f32>()
}
pub fn cde(&self) -> &CDEngine {
&self.cde
}
pub fn is_feasible(&self) -> bool {
self.placed_items.iter().all(|(pk, pi)| {
let hkey = self
.cde
.haz_key_from_pi_key(pk)
.expect("all placed items should be registered in the CDE");
!self.cde.detect_poly_collision(&pi.shape, &hkey)
})
}
}
#[derive(Clone, Debug)]
pub struct LayoutSnapshot {
pub container: Container,
pub placed_items: SlotMap<PItemKey, PlacedItem>,
pub cde_snapshot: CDESnapshot,
}
impl LayoutSnapshot {
pub fn density(&self, instance: &impl Instance) -> f32 {
self.placed_item_area(instance) / self.container.area()
}
pub fn placed_item_area(&self, instance: &impl Instance) -> f32 {
self.placed_items
.iter()
.map(|(_, pi)| instance.item(pi.item_id))
.map(|item| item.area())
.sum::<f32>()
}
}