diff --git a/assets/config_lbf.json b/assets/config_lbf.json index a8e7ca9..05a894f 100644 --- a/assets/config_lbf.json +++ b/assets/config_lbf.json @@ -1,13 +1,7 @@ { "cde_config": { - "quadtree": { - "FixedDepth": 5 - }, - "haz_prox": { - "Enabled": { - "n_cells": 10000 - } - }, + "quadtree_depth": 5, + "hpg_n_cells": 2000, "item_surrogate_config": { "pole_coverage_goal": 0.9, "max_poles": 10, @@ -16,16 +10,36 @@ } }, "poly_simpl_config": { - "Enabled": { + "mode": "enabled", + "params": { "tolerance": 0.001 } }, - "deterministic_mode": true, + "deterministic_mode": false, "n_samples_per_item": 5000, - "ls_samples_fraction": 0.5, + "ls_samples_fraction": 0.2, "svg_draw_options": { + "theme": { + "stroke_width_multiplier": 2.0, + "bin_fill": "#CC824A", + "item_fill": "#FFC879", + "hole_fill": "#2D2D2D", + "qz_fill": [ + "#000000", + "#FF0000", + "#FF5E00", + "#FFA500", + "#FFAD00", + "#FFFF00", + "#CBFF00", + "#CBFF00", + "#CBFF00", + "#CBFF00" + ], + "qz_stroke_opac": 0.5 + }, "quadtree": false, "haz_prox_grid": false, "surrogate": false } -} \ No newline at end of file +} diff --git a/jaguars/src/collision_detection/cd_engine.rs b/jaguars/src/collision_detection/cd_engine.rs index e564b3c..a53c7b7 100644 --- a/jaguars/src/collision_detection/cd_engine.rs +++ b/jaguars/src/collision_detection/cd_engine.rs @@ -2,11 +2,11 @@ use indexmap::IndexSet; use itertools::Itertools; use tribool::Tribool; +use crate::collision_detection::hazard::Hazard; +use crate::collision_detection::hazard::HazardEntity; use crate::collision_detection::hpg::grid::Grid; use crate::collision_detection::hpg::hazard_proximity_grid::{HazardProximityGrid, PendingChangesErr}; use crate::collision_detection::hpg::hpg_cell::HPGCell; -use crate::collision_detection::hazard::Hazard; -use crate::collision_detection::hazard::HazardEntity; use crate::collision_detection::quadtree::qt_node::QTNode; use crate::geometry::fail_fast::sp_surrogate::SPSurrogate; use crate::geometry::geo_enums::{GeoPosition, GeoRelation}; @@ -18,8 +18,7 @@ use crate::geometry::primitives::point::Point; use crate::geometry::primitives::simple_polygon::SimplePolygon; use crate::geometry::transformation::Transformation; use crate::util::assertions; -use crate::util::config::{CDEConfig, HazProxConfig, QuadTreeConfig}; - +use crate::util::config::CDEConfig; /// The Collision Detection Engine (CDE) is responsible for validating potential placements of items in a `Layout`. /// It can be queried to check if a given shape or surrogate collides with any `Hazard`s in the `Layout`. @@ -46,19 +45,12 @@ pub struct CDESnapshot { impl CDEngine { pub fn new(bbox: AARectangle, static_hazards: Vec, config: CDEConfig) -> CDEngine { - let qt_depth = match config.quadtree { - QuadTreeConfig::FixedDepth(depth) => depth, - QuadTreeConfig::Auto => panic!("not implemented, quadtree depth must be specified"), - }; - - let haz_prox_grid = match config.haz_prox { - HazProxConfig::Disabled => None, - HazProxConfig::Enabled { n_cells } => { - Some(HazardProximityGrid::new(bbox.clone(), &static_hazards, n_cells)) - } + let haz_prox_grid = match config.hpg_n_cells { + 0 => None, + hpg_n_cells => Some(HazardProximityGrid::new(bbox.clone(), &static_hazards, hpg_n_cells)) }; - let mut qt_root = QTNode::new(qt_depth, bbox.clone(), None); + let mut qt_root = QTNode::new(config.quadtree_depth, bbox.clone(), None); for haz in static_hazards.iter() { qt_root.register_hazard(haz.into()); @@ -120,7 +112,7 @@ impl CDEngine { assert!(!self.haz_prox_grid.as_ref().map_or(false, |hpg| hpg.has_pending_deregisters())); CDESnapshot { dynamic_hazards: self.dynamic_hazards.clone(), - grid: self.haz_prox_grid.as_ref().map(|hpg| hpg.grid().clone()) + grid: self.haz_prox_grid.as_ref().map(|hpg| hpg.grid.clone()) } } @@ -190,8 +182,8 @@ impl CDEngine { } pub fn smallest_qt_node_dimension(&self) -> f64 { - let bbox = self.quadtree.bbox(); - let level = self.quadtree.level(); + let bbox = &self.quadtree.bbox; + let level = self.quadtree.level; //every level, the dimension is halved bbox.width() / 2.0_f64.powi(level as i32) } @@ -346,12 +338,12 @@ impl CDEngine { if std::ptr::eq(haz_shape, s_omega) { //s_omega is registered in the quadtree. //maybe the quadtree can help us. - match self.quadtree.point_definitely_collides_with(&s_mu.poi().center, &haz.entity).try_into() { + match self.quadtree.point_definitely_collides_with(&s_mu.poi.center, &haz.entity).try_into() { Ok(collides) => return collides, Err(_) => (), //no definitive answer } } - let inclusion = s_omega.collides_with(&s_mu.poi().center); + let inclusion = s_omega.collides_with(&s_mu.poi.center); match haz.entity.position() { GeoPosition::Interior => inclusion, diff --git a/jaguars/src/collision_detection/hazard_filter.rs b/jaguars/src/collision_detection/hazard_filter.rs index 5618644..9ac58c5 100644 --- a/jaguars/src/collision_detection/hazard_filter.rs +++ b/jaguars/src/collision_detection/hazard_filter.rs @@ -3,7 +3,6 @@ use itertools::Itertools; use crate::collision_detection::hazard::Hazard; use crate::collision_detection::hazard::HazardEntity; - /// Trait that allows for filtering out irrelevant hazards depending on the context. /// Enables ignoring certain hazards when querying the CDE. pub trait HazardFilter { diff --git a/jaguars/src/collision_detection/hpg/boundary_fill.rs b/jaguars/src/collision_detection/hpg/boundary_fill.rs index eaab022..e67a678 100644 --- a/jaguars/src/collision_detection/hpg/boundary_fill.rs +++ b/jaguars/src/collision_detection/hpg/boundary_fill.rs @@ -22,7 +22,7 @@ impl BoundaryFillGrid { /// Creates a new BoundaryFillGrid, add all cells inside the seed_bbox to the queue pub fn new(grid: &Grid, seed_bbox: AARectangle) -> Self { - let state = vec![CellState::new(); grid.n_rows() * grid.n_cols()]; + let state = vec![CellState::new(); grid.n_rows * grid.n_cols]; //Find the range of rows and columns which reside inside the seed_bbox let row_range = grid.rows_in_range(seed_bbox.y_min..=seed_bbox.y_max); @@ -50,7 +50,7 @@ impl BoundaryFillGrid { false => { match self.seed_iterator.next() { Some((row, col)) => { - let cell = grid.get_index(row, col); + let cell = grid.to_index(row, col); if let Some(cell) = cell { self.state[cell].visit(); self.n_visited += 1; diff --git a/jaguars/src/collision_detection/hpg/grid.rs b/jaguars/src/collision_detection/hpg/grid.rs index 0d0e68b..5a27cb8 100644 --- a/jaguars/src/collision_detection/hpg/grid.rs +++ b/jaguars/src/collision_detection/hpg/grid.rs @@ -3,17 +3,18 @@ use std::ops::RangeInclusive; use itertools::Itertools; use ordered_float::NotNan; + use crate::geometry::primitives::point::Point; /// Representation of a grid of optional elements of type T /// Divided into rows and columns, where each row and column has a unique coordinate #[derive(Clone, Debug)] pub struct Grid { - cells: Vec>, - rows: Vec>, - cols: Vec>, - n_rows: usize, - n_cols: usize, + pub cells: Vec>, + pub rows: Vec>, + pub cols: Vec>, + pub n_rows: usize, + pub n_cols: usize, } impl Grid { @@ -113,7 +114,7 @@ impl Grid { neighbors } - pub fn get_index(&self, row: usize, col: usize) -> Option { + pub fn to_index(&self, row: usize, col: usize) -> Option { Self::calculate_index(row, col, self.n_rows, self.n_cols) } @@ -124,7 +125,7 @@ impl Grid { } } - pub fn get_row_col(&self, index: usize) -> Option<(usize, usize)> { + pub fn to_row_col(&self, index: usize) -> Option<(usize, usize)> { match index.cmp(&(self.n_rows * self.n_cols)) { Ordering::Less => { let row = index / self.n_cols; @@ -134,20 +135,4 @@ impl Grid { _ => None //out of bounds } } - - pub fn n_rows(&self) -> usize { - self.n_rows - } - - pub fn n_cols(&self) -> usize { - self.n_cols - } - - pub fn elements(&self) -> &Vec> { - &self.cells - } - - pub fn elements_mut(&mut self) -> &mut Vec> { - &mut self.cells - } } \ No newline at end of file diff --git a/jaguars/src/collision_detection/hpg/grid_generator.rs b/jaguars/src/collision_detection/hpg/grid_generator.rs index 33bcf94..4641622 100644 --- a/jaguars/src/collision_detection/hpg/grid_generator.rs +++ b/jaguars/src/collision_detection/hpg/grid_generator.rs @@ -1,5 +1,6 @@ use std::cmp::Ordering; -use log::{debug}; + +use log::debug; use crate::collision_detection::hazard::Hazard; use crate::geometry::geo_traits::{DistanceFrom, Shape}; diff --git a/jaguars/src/collision_detection/hpg/hazard_proximity_grid.rs b/jaguars/src/collision_detection/hpg/hazard_proximity_grid.rs index 097e5d1..417d480 100644 --- a/jaguars/src/collision_detection/hpg/hazard_proximity_grid.rs +++ b/jaguars/src/collision_detection/hpg/hazard_proximity_grid.rs @@ -2,12 +2,12 @@ use std::iter; use itertools::Itertools; +use crate::collision_detection::hazard::Hazard; +use crate::collision_detection::hazard::HazardEntity; use crate::collision_detection::hpg::boundary_fill::BoundaryFillGrid; use crate::collision_detection::hpg::grid::Grid; use crate::collision_detection::hpg::grid_generator; -use crate::collision_detection::hpg::hpg_cell::{HPGCellUpdate, HPGCell}; -use crate::collision_detection::hazard::Hazard; -use crate::collision_detection::hazard::HazardEntity; +use crate::collision_detection::hpg::hpg_cell::{HPGCell, HPGCellUpdate}; use crate::geometry::geo_traits::Shape; use crate::geometry::primitives::aa_rectangle::AARectangle; @@ -15,9 +15,9 @@ use crate::geometry::primitives::aa_rectangle::AARectangle; /// The grid is a part of the `CDEngine` and is thus automatically updated when hazards are registered or deregistered. #[derive(Debug, Clone)] pub struct HazardProximityGrid { - grid: Grid, + pub grid: Grid, pending_deregisters: Vec, - cell_radius : f64, + pub cell_radius : f64, } impl HazardProximityGrid { @@ -45,7 +45,7 @@ impl HazardProximityGrid { } pub fn restore(&mut self, grid: Grid) { - assert_eq!(self.grid.elements().len(), grid.elements().len()); + assert_eq!(self.grid.cells.len(), grid.cells.len()); self.grid = grid; self.pending_deregisters.clear(); } @@ -64,7 +64,7 @@ impl HazardProximityGrid { let mut b_fill = BoundaryFillGrid::new(&self.grid, seed_bbox); while let Some(next_dot_index) = b_fill.pop(&self.grid) { - let cell = self.grid.elements_mut()[next_dot_index].as_mut(); + let cell = self.grid.cells[next_dot_index].as_mut(); if let Some(cell) = cell { match cell.register_hazard(to_register) { HPGCellUpdate::Affected => { @@ -81,23 +81,23 @@ impl HazardProximityGrid { //TODO: move this to an assertion check debug_assert!( { - let old_cells = self.grid.elements().clone(); + let old_cells = self.grid.cells.clone(); //ensure no changes remain - let undetected = self.grid.elements_mut().iter_mut().enumerate() + let undetected = self.grid.cells.iter_mut().enumerate() .flat_map(|(i, cell)| cell.as_mut().map(|cell| (i, cell))) .map(|(i, cell)| (i, cell.register_hazard(to_register))) .filter(|(_i, res)| res == &HPGCellUpdate::Affected) .map(|(i, _res)| i) .collect_vec(); - let undetected_row_cols = undetected.iter().map(|i| self.grid.get_row_col(*i).unwrap()).collect_vec(); + let undetected_row_cols = undetected.iter().map(|i| self.grid.to_row_col(*i).unwrap()).collect_vec(); if undetected.len() != 0 { println!("{:?} undetected affected cells", undetected_row_cols); for i in undetected { println!("old {:?}", &old_cells[i]); - println!("new {:?}", &self.grid.elements()[i]); + println!("new {:?}", &self.grid.cells[i]); } false } else { @@ -112,7 +112,7 @@ impl HazardProximityGrid { { match process_now { true => { - for cell in self.grid.elements_mut().iter_mut().flatten() { + for cell in self.grid.cells.iter_mut().flatten() { let result = cell.deregister_hazards(iter::once(to_deregister), remaining.clone()); match result { HPGCellUpdate::Affected => (), @@ -133,7 +133,7 @@ impl HazardProximityGrid { if self.has_pending_deregisters() { let to_deregister = self.pending_deregisters.iter(); - for cell in self.grid.elements_mut().iter_mut().flatten() { + for cell in self.grid.cells.iter_mut().flatten() { cell.deregister_hazards(to_deregister.clone(), remaining.clone()); } @@ -145,18 +145,6 @@ impl HazardProximityGrid { !self.pending_deregisters.is_empty() } - pub fn cells(&self) -> &Vec> { - &self.grid.elements() - } - - pub fn cell_radius(&self) -> f64 { - self.cell_radius - } - - pub fn grid(&self) -> &Grid { - &self.grid - } - } diff --git a/jaguars/src/collision_detection/hpg/hpg_cell.rs b/jaguars/src/collision_detection/hpg/hpg_cell.rs index 476fa8e..8884e42 100644 --- a/jaguars/src/collision_detection/hpg/hpg_cell.rs +++ b/jaguars/src/collision_detection/hpg/hpg_cell.rs @@ -2,16 +2,16 @@ use std::cmp::Ordering; use itertools::Itertools; -use crate::collision_detection::hpg::proximity::Proximity; use crate::collision_detection::hazard::Hazard; use crate::collision_detection::hazard::HazardEntity; +use crate::collision_detection::hpg::proximity::Proximity; use crate::entities::item::Item; +use crate::entities::quality_zone::N_QUALITIES; use crate::geometry::geo_enums::GeoPosition; use crate::geometry::geo_traits::{DistanceFrom, Shape}; use crate::geometry::primitives::aa_rectangle::AARectangle; use crate::geometry::primitives::circle::Circle; use crate::geometry::primitives::point::Point; -use crate::entities::quality_zone::N_QUALITIES; /// Represents a cell in the Hazard Proximity Grid #[derive(Clone, Debug)] @@ -165,8 +165,8 @@ impl HPGCell { } pub fn could_accommodate_item(&self, item: &Item) -> bool { - let haz_prox : f64 = (&self.hazard_proximity(item.base_quality())).into(); - let item_poi_radius = item.shape().poi().radius; + let haz_prox : f64 = (&self.hazard_proximity(item.base_quality)).into(); + let item_poi_radius = item.shape.poi.radius; item_poi_radius < haz_prox + self.radius } diff --git a/jaguars/src/collision_detection/quadtree/qt_hazard.rs b/jaguars/src/collision_detection/quadtree/qt_hazard.rs index 4dfe8dd..bbc90ae 100644 --- a/jaguars/src/collision_detection/quadtree/qt_hazard.rs +++ b/jaguars/src/collision_detection/quadtree/qt_hazard.rs @@ -1,4 +1,5 @@ use std::borrow::Borrow; + use crate::collision_detection::hazard::Hazard; use crate::collision_detection::hazard::HazardEntity; use crate::collision_detection::quadtree::qt_partial_hazard::{EdgeIndices, QTPartialHazard}; @@ -46,7 +47,7 @@ impl QTHazard { QTHazPresence::Entire => [Some(self.clone()), Some(self.clone()), Some(self.clone()), Some(self.clone())], QTHazPresence::Partial(partial_haz) => { //check the bbox of the hazard with the bboxes of the quadrants - let haz_bbox = partial_haz.shape().bbox(); + let haz_bbox = partial_haz.shape_arc().bbox(); let haz_quad_relations = [ haz_bbox.relation_to(&quadrants[0]), haz_bbox.relation_to(&quadrants[1]), @@ -64,7 +65,7 @@ impl QTHazard { constricted_presence[quad_index] = Some(self.presence.clone()); } else { //the hazard is partially active in multiple quadrants, find them - let shape = partial_haz.shape(); + let shape = partial_haz.shape_arc(); let mut check_collisions_with_quadrants = |edge_index: usize| { let edge = shape.get_edge(edge_index); for quad_index in 0..4 { @@ -73,7 +74,7 @@ impl QTHazard { let constricted_haz_presence = constricted_presence[quad_index].get_or_insert( QTHazPresence::Partial( QTPartialHazard::new( - partial_haz.shape(), + partial_haz.shape_arc(), EdgeIndices::Some(vec![]) ) ) @@ -88,7 +89,7 @@ impl QTHazard { } }; - match partial_haz.edge_indices() { + match &partial_haz.edge_indices { EdgeIndices::All => { for edge_index in 0..shape.number_of_points() { check_collisions_with_quadrants(edge_index); diff --git a/jaguars/src/collision_detection/quadtree/qt_node.rs b/jaguars/src/collision_detection/quadtree/qt_node.rs index 911e0a7..cd936b9 100644 --- a/jaguars/src/collision_detection/quadtree/qt_node.rs +++ b/jaguars/src/collision_detection/quadtree/qt_node.rs @@ -13,11 +13,11 @@ use crate::geometry::primitives::point::Point; #[derive(Clone, Debug)] pub struct QTNode { /// The level of the node in the tree, 0 being the bottom-most level - level: u8, - bbox: AARectangle, - children: Option>, + pub level: u8, + pub bbox: AARectangle, + pub children: Option>, /// The hazards present in the node - hazards: QTHazardVec, + pub hazards: QTHazardVec, } impl QTNode { @@ -35,10 +35,10 @@ impl QTNode { fn register_to_children(children: &mut Option>, hazard: &QTHazard) { if let Some(children) = children.as_mut() { let child_bboxes = [ - children[0].bbox(), - children[1].bbox(), - children[2].bbox(), - children[3].bbox(), + &children[0].bbox, + &children[1].bbox, + &children[2].bbox, + &children[3].bbox, ]; let constricted_hazards = hazard.constrict(child_bboxes); @@ -121,22 +121,6 @@ impl QTNode { self.children.is_some() } - pub fn level(&self) -> u8 { - self.level - } - - pub fn bbox(&self) -> &AARectangle { - &self.bbox - } - - pub fn children(&self) -> &Option> { - &self.children - } - - pub fn hazards(&self) -> &QTHazardVec { - &self.hazards - } - /// Returns None if no collision between the entity and any hazard is detected, /// otherwise returns the first encountered hazard that collides with the entity /// In practice T is usually an `Edge` or `Circle` @@ -145,12 +129,12 @@ impl QTNode { { match self.hazards.strongest(irrelevant_hazards) { None => None, - Some(strongest_hazard) => match entity.collides_with(self.bbox()) { + Some(strongest_hazard) => match entity.collides_with(&self.bbox) { false => None, true => match strongest_hazard.presence { QTHazPresence::None => None, QTHazPresence::Entire => Some(&strongest_hazard.entity), - QTHazPresence::Partial(_) => match self.children() { + QTHazPresence::Partial(_) => match &self.children { Some(children) => { //Check if any of the children intersect with the entity children.iter() @@ -182,10 +166,10 @@ impl QTNode { { match self.hazards.strongest(irrelevant_hazards) { None => Tribool::False, - Some(hazard) => match (entity.collides_with(self.bbox()), &hazard.presence) { + Some(hazard) => match (entity.collides_with(&self.bbox), &hazard.presence) { (false, _) | (_, QTHazPresence::None) => Tribool::False, (true, QTHazPresence::Entire) => Tribool::True, - (true, QTHazPresence::Partial(_)) => match self.children() { + (true, QTHazPresence::Partial(_)) => match &self.children { Some(children) => { //There is a partial hazard and the node has children, check all children let mut result = Tribool::False; //Assume no collision diff --git a/jaguars/src/collision_detection/quadtree/qt_partial_hazard.rs b/jaguars/src/collision_detection/quadtree/qt_partial_hazard.rs index dad4240..3d0e6fc 100644 --- a/jaguars/src/collision_detection/quadtree/qt_partial_hazard.rs +++ b/jaguars/src/collision_detection/quadtree/qt_partial_hazard.rs @@ -1,5 +1,5 @@ use std::borrow::Borrow; -use std::hash::{Hash}; +use std::hash::Hash; use std::sync::{Arc, Weak}; use crate::collision_detection::hazard::Hazard; @@ -8,12 +8,11 @@ use crate::geometry::primitives::circle::Circle; use crate::geometry::primitives::edge::Edge; use crate::geometry::primitives::simple_polygon::SimplePolygon; - /// QTPartialHazards define a set of edges from a hazard that is partially active in the QTNode. #[derive(Clone, Debug)] pub struct QTPartialHazard { - shape: Weak, - edge_indices: EdgeIndices, + pub shape: Weak, + pub edge_indices: EdgeIndices, } #[derive(Clone, Debug, PartialEq, Hash, Eq)] @@ -40,18 +39,10 @@ impl QTPartialHazard { } } - pub fn shape_weak(&self) -> &Weak { - &self.shape - } - - pub fn shape(&self) -> Arc { + pub fn shape_arc(&self) -> Arc { self.shape.upgrade().expect("polygon reference is not alive") } - pub fn edge_indices(&self) -> &EdgeIndices{ - &self.edge_indices - } - pub fn encompasses_all_edges(&self) -> bool { self.edge_indices == EdgeIndices::All } @@ -67,11 +58,12 @@ impl QTPartialHazard { } -pub const BBOX_CHECK_THRESHOLD: usize = 4; +pub const BBOX_CHECK_THRESHOLD: usize = 4; //check bbox if number of edges is greater than this +pub const BBOX_CHECK_THRESHOLD_PLUS_1: usize = BBOX_CHECK_THRESHOLD + 1; impl CollidesWith for QTPartialHazard { fn collides_with(&self, edge: &Edge) -> bool { - let shape = self.shape.upgrade().expect("polygon reference should be alive"); - match self.edge_indices() { + let shape = self.shape_arc(); + match &self.edge_indices { EdgeIndices::All => { match shape.bbox().collides_with(edge) { false => false, @@ -84,7 +76,7 @@ impl CollidesWith for QTPartialHazard { match indices.len(){ 0 => unreachable!("edge indices should not be empty"), 1..=BBOX_CHECK_THRESHOLD => indices.iter().any(|&i| edge.collides_with(&shape.get_edge(i))), - BBOX_CHECK_THRESHOLD.. => { + BBOX_CHECK_THRESHOLD_PLUS_1.. => { match shape.bbox().collides_with(edge) { false => false, true => indices.iter().any(|&i| edge.collides_with(&shape.get_edge(i))) @@ -98,8 +90,8 @@ impl CollidesWith for QTPartialHazard { impl CollidesWith for QTPartialHazard { fn collides_with(&self, circle: &Circle) -> bool { - let shape = self.shape.upgrade().expect("polygon reference is not alive"); - match self.edge_indices() { + let shape = self.shape_arc(); + match &self.edge_indices { EdgeIndices::All => { match circle.collides_with(&shape.bbox()) { false => false, @@ -112,7 +104,7 @@ impl CollidesWith for QTPartialHazard { match indices.len(){ 0 => unreachable!("edge indices should not be empty"), 1..=BBOX_CHECK_THRESHOLD => indices.iter().any(|&i| circle.collides_with(&shape.get_edge(i))), - BBOX_CHECK_THRESHOLD.. => { + BBOX_CHECK_THRESHOLD_PLUS_1.. => { match circle.collides_with(&shape.bbox()) { false => false, true => indices.iter().any(|&i| circle.collides_with(&shape.get_edge(i))) diff --git a/jaguars/src/entities/bin.rs b/jaguars/src/entities/bin.rs index 14b5021..4a2a586 100644 --- a/jaguars/src/entities/bin.rs +++ b/jaguars/src/entities/bin.rs @@ -5,26 +5,26 @@ use itertools::Itertools; use crate::collision_detection::cd_engine::CDEngine; use crate::collision_detection::hazard::Hazard; use crate::collision_detection::hazard::HazardEntity; +use crate::entities::quality_zone::N_QUALITIES; use crate::entities::quality_zone::QualityZone; use crate::geometry::geo_traits::Shape; use crate::geometry::primitives::aa_rectangle::AARectangle; use crate::geometry::primitives::simple_polygon::SimplePolygon; use crate::geometry::transformation::Transformation; -use crate::entities::quality_zone::N_QUALITIES; use crate::util::config::CDEConfig; /// A `Bin` is a container in which items can be placed. /// Its interior is defined by an outer polygon and zero or more holes. #[derive(Clone, Debug)] pub struct Bin { - id: usize, - outer: Arc, - value: u64, - centering_transform: Transformation, - holes: Vec>, - quality_zones: [Option; N_QUALITIES], - base_cde: Arc, - area: f64, + pub id: usize, + pub outer: Arc, + pub value: u64, + pub centering_transform: Transformation, + pub holes: Vec>, + pub quality_zones: [Option; N_QUALITIES], + pub base_cde: Arc, + pub area: f64, } impl Bin { @@ -97,39 +97,7 @@ impl Bin { hazards } - pub fn id(&self) -> usize { - self.id - } - - pub fn outer(&self) -> &Arc { - &self.outer - } - - pub fn holes(&self) -> &Vec> { - &self.holes - } - pub fn bbox(&self) -> AARectangle { self.outer.bbox() } - - pub fn value(&self) -> u64 { - self.value - } - - pub fn centering_transform(&self) -> &Transformation { - &self.centering_transform - } - - pub fn quality_zones(&self) -> &[Option; N_QUALITIES] { - &self.quality_zones - } - - pub fn base_cde(&self) -> &CDEngine { - &self.base_cde - } - - pub fn area(&self) -> f64 { - self.area - } } \ No newline at end of file diff --git a/jaguars/src/entities/instance.rs b/jaguars/src/entities/instance.rs index fcc9409..a45f7e0 100644 --- a/jaguars/src/entities/instance.rs +++ b/jaguars/src/entities/instance.rs @@ -1,4 +1,5 @@ use enum_dispatch::enum_dispatch; + use crate::entities::bin::Bin; use crate::entities::item::Item; use crate::geometry::geo_traits::Shape; @@ -58,7 +59,7 @@ impl BPInstance { pub fn new(items: Vec<(Item, usize)>, bins: Vec<(Bin, usize)>) -> Self { assert!(assertions::instance_item_bin_ids_correct(&items, &bins)); - let item_area = items.iter().map(|(item, qty)| item.shape().area() * *qty as f64).sum(); + let item_area = items.iter().map(|(item, qty)| item.shape.area() * *qty as f64).sum(); Self { items, item_area, bins } } @@ -66,7 +67,7 @@ impl BPInstance { impl SPInstance { pub fn new(items: Vec<(Item, usize)>, strip_height: f64) -> Self { - let item_area = items.iter().map(|(item, qty)| item.shape().area() * *qty as f64).sum(); + let item_area = items.iter().map(|(item, qty)| item.shape.area() * *qty as f64).sum(); Self { items, item_area, strip_height } } diff --git a/jaguars/src/entities/item.rs b/jaguars/src/entities/item.rs index 40d0b25..84c6569 100644 --- a/jaguars/src/entities/item.rs +++ b/jaguars/src/entities/item.rs @@ -9,13 +9,13 @@ use crate::util::config::SPSurrogateConfig; /// An `Item` can be placed in a `Bin`. #[derive(Clone, Debug)] pub struct Item { - id: usize, - shape: Arc, - allowed_rotation: AllowedRotation, - base_quality: Option, - value: u64, - centering_transform: Transformation, - hazard_filter: Option, + pub id: usize, + pub shape: Arc, + pub allowed_rotation: AllowedRotation, + pub base_quality: Option, + pub value: u64, + pub centering_transform: Transformation, + pub hazard_filter: Option, } impl Item { @@ -33,32 +33,4 @@ impl Item { ..self.clone() } } - - pub fn shape(&self) -> &SimplePolygon { - &self.shape - } - - pub fn id(&self) -> usize { - self.id - } - - pub fn value(&self) -> u64 { - self.value - } - - pub fn centering_transform(&self) -> &Transformation { - &self.centering_transform - } - - pub fn base_quality(&self) -> Option { - self.base_quality - } - - pub fn hazard_filter(&self) -> Option<&QZHazardFilter> { - self.hazard_filter.as_ref() - } - - pub fn allowed_rotation(&self) -> &AllowedRotation { - &self.allowed_rotation - } } \ No newline at end of file diff --git a/jaguars/src/entities/layout.rs b/jaguars/src/entities/layout.rs index c3fd5ad..7e17e10 100644 --- a/jaguars/src/entities/layout.rs +++ b/jaguars/src/entities/layout.rs @@ -1,6 +1,5 @@ use crate::collision_detection::cd_engine::{CDEngine, CDESnapshot}; use crate::entities::bin::Bin; - use crate::entities::item::Item; use crate::entities::placed_item::PlacedItem; use crate::entities::placed_item::PlacedItemUID; @@ -20,7 +19,7 @@ pub struct Layout { impl Layout { pub fn new(id: usize, bin: Bin) -> Self { - let cde = bin.base_cde().clone(); + let cde = bin.base_cde.as_ref().clone(); Layout { id, bin, @@ -48,7 +47,7 @@ impl Layout { } pub fn restore(&mut self, layout_snapshot: &LayoutSnapshot) { - assert_eq!(self.bin.id(), layout_snapshot.bin.id()); + assert_eq!(self.bin.id, layout_snapshot.bin.id); self.placed_items = layout_snapshot.placed_items.clone(); self.cde.restore(&layout_snapshot.cde_snapshot); @@ -75,9 +74,9 @@ impl Layout { pub fn remove_item(&mut self, pi_uid: &PlacedItemUID, commit_instantly: bool) { - let pos = self.placed_items.iter().position(|pi| pi.uid() == pi_uid).expect("item not found"); + let pos = self.placed_items.iter().position(|pi| &pi.uid == pi_uid).expect("item not found"); let placed_item = self.placed_items.swap_remove(pos); - self.cde.deregister_hazard(&placed_item.uid().clone().into(), commit_instantly); + self.cde.deregister_hazard(&placed_item.uid.clone().into(), commit_instantly); debug_assert!(assertions::layout_qt_matches_fresh_qt(self)); } @@ -95,8 +94,8 @@ impl Layout { } pub fn usage(&self) -> f64 { - let bin_area = self.bin().area(); - let item_area = self.placed_items.iter().map(|p_i| p_i.shape().area()).sum::(); + let bin_area = self.bin().area; + let item_area = self.placed_items.iter().map(|p_i| p_i.shape.area()).sum::(); item_area / bin_area } diff --git a/jaguars/src/entities/placed_item.rs b/jaguars/src/entities/placed_item.rs index aecbc05..de77d0e 100644 --- a/jaguars/src/entities/placed_item.rs +++ b/jaguars/src/entities/placed_item.rs @@ -12,10 +12,10 @@ use crate::geometry::primitives::simple_polygon::SimplePolygon; #[derive(Clone, Debug)] pub struct PlacedItem { /// Unique identifier for the placed item - pi_uid: PlacedItemUID, - qz_haz_filter: Option, + pub uid: PlacedItemUID, + pub qz_haz_filter: Option, /// The shape of the `Item` after it has been transformed and placed in a `Layout` - shape: Arc, + pub shape: Arc, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -28,35 +28,22 @@ pub struct PlacedItemUID { impl PlacedItem { pub fn new(item: &Item, d_transf: DTransformation) -> Self { let transf = d_transf.compose(); - let shape = Arc::new(item.shape().transform_clone(&transf)); - let qz_haz_filter = item.hazard_filter().cloned(); - let pi_uid = PlacedItemUID { item_id: item.id(), d_transf }; + let shape = Arc::new(item.shape.transform_clone(&transf)); + let qz_haz_filter = item.hazard_filter.clone(); + let pi_uid = PlacedItemUID { item_id: item.id, d_transf }; PlacedItem { - pi_uid, + uid: pi_uid, shape, qz_haz_filter, } } pub fn item_id(&self) -> usize { - self.pi_uid.item_id + self.uid.item_id } pub fn d_transformation(&self) -> &DTransformation { - &self.pi_uid.d_transf - } - - pub fn shape(&self) -> &Arc { - &self.shape - } - - pub fn uid(&self) -> &PlacedItemUID { - &self.pi_uid - } - - - pub fn haz_filter(&self) -> &Option { - &self.qz_haz_filter + &self.uid.d_transf } } @@ -68,6 +55,6 @@ impl Into for &PlacedItem { impl Into for &PlacedItem { fn into(self) -> HazardEntity { - HazardEntity::PlacedItem(self.pi_uid.clone()) + HazardEntity::PlacedItem(self.uid.clone()) } } diff --git a/jaguars/src/entities/problems/bin_packing.rs b/jaguars/src/entities/problems/bin_packing.rs index 0ca2067..f1cb20d 100644 --- a/jaguars/src/entities/problems/bin_packing.rs +++ b/jaguars/src/entities/problems/bin_packing.rs @@ -1,9 +1,8 @@ use std::collections::HashSet; - use itertools::Itertools; -use crate::entities::instance::{BPInstance}; +use crate::entities::instance::BPInstance; use crate::entities::instance::InstanceGeneric; use crate::entities::layout::Layout; use crate::entities::placed_item::PlacedItemUID; @@ -16,7 +15,7 @@ use crate::util::assertions; /// Bin Packing Problem #[derive(Clone)] pub struct BPProblem { - instance: BPInstance, + pub instance: BPInstance, layouts: Vec, empty_layouts: Vec, missing_item_qtys: Vec, @@ -60,7 +59,7 @@ impl BPProblem { } pub fn register_layout(&mut self, layout: Layout) { - self.register_bin(layout.bin().id()); + self.register_bin(layout.bin().id); layout.placed_items().iter().for_each( |p_i| { self.register_included_item(p_i.item_id()) @@ -74,7 +73,7 @@ impl BPProblem { LayoutIndex::Existing(i) => { let layout = self.layouts.remove(i); self.layout_has_changed(layout.id()); - self.unregister_bin(layout.bin().id()); + self.unregister_bin(layout.bin().id); layout.placed_items().iter().for_each( |v| { self.unregister_included_item(v.item_id()) }); self.uncommitted_removed_layouts.push(layout); @@ -108,10 +107,6 @@ impl BPProblem { self.unchanged_layouts.remove(index); } } - - fn instance(&self) -> &BPInstance { - &self.instance - } } impl ProblemGeneric for BPProblem { @@ -172,7 +167,7 @@ impl ProblemGeneric for BPProblem { } }; - let target_item_qtys = self.instance().items().iter().map(|(_, qty)| *qty).collect_vec(); + let target_item_qtys = self.instance.items().iter().map(|(_, qty)| *qty).collect_vec(); let solution = Solution::new(id, layout_snapshots, self.usage(), included_item_qtys, target_item_qtys, bin_qtys); diff --git a/jaguars/src/entities/problems/problem.rs b/jaguars/src/entities/problems/problem.rs index 9f32cb4..dbfceda 100644 --- a/jaguars/src/entities/problems/problem.rs +++ b/jaguars/src/entities/problems/problem.rs @@ -1,14 +1,10 @@ use std::borrow::Borrow; - use enum_dispatch::enum_dispatch; - -use crate::entities::placing_option::PlacingOption; - - use crate::entities::layout::Layout; use crate::entities::placed_item::PlacedItemUID; +use crate::entities::placing_option::PlacingOption; use crate::entities::problems::bin_packing::BPProblem; use crate::entities::problems::problem::private::ProblemGenericPrivate; use crate::entities::problems::strip_packing::SPProblem; @@ -64,7 +60,7 @@ pub trait ProblemGeneric: ProblemGenericPrivate { fn usage(&mut self) -> f64 { let (total_bin_area, total_used_area) = self.layouts_mut().iter_mut().fold((0.0, 0.0), |acc, l| { - let bin_area = l.bin().area(); + let bin_area = l.bin().area; let used_area = bin_area * l.usage(); (acc.0 + bin_area, acc.1 + used_area) }); @@ -72,13 +68,13 @@ pub trait ProblemGeneric: ProblemGenericPrivate { } fn used_bin_value(&self) -> u64 { - self.layouts().iter().map(|l| l.bin().value()).sum() + self.layouts().iter().map(|l| l.bin().value).sum() } fn included_item_qtys(&self) -> Vec; fn empty_layout_has_stock(&self, index: usize) -> bool { - let bin_id = self.empty_layouts()[index].bin().id(); + let bin_id = self.empty_layouts()[index].bin().id; self.bin_qtys()[bin_id] > 0 } diff --git a/jaguars/src/entities/problems/strip_packing.rs b/jaguars/src/entities/problems/strip_packing.rs index 4d4931d..cea524d 100644 --- a/jaguars/src/entities/problems/strip_packing.rs +++ b/jaguars/src/entities/problems/strip_packing.rs @@ -1,16 +1,15 @@ use std::slice; - use itertools::Itertools; use ordered_float::NotNan; use crate::collision_detection::hazard_filter; use crate::entities::bin::Bin; -use crate::entities::placing_option::PlacingOption; -use crate::entities::instance::{SPInstance}; use crate::entities::instance::InstanceGeneric; +use crate::entities::instance::SPInstance; use crate::entities::layout::Layout; use crate::entities::placed_item::PlacedItemUID; +use crate::entities::placing_option::PlacingOption; use crate::entities::problems::problem::{LayoutIndex, ProblemGeneric}; use crate::entities::problems::problem::private::ProblemGenericPrivate; use crate::entities::solution::Solution; @@ -21,7 +20,7 @@ use crate::util::config::CDEConfig; /// Strip Packing Problem #[derive(Clone)] pub struct SPProblem { - instance: SPInstance, + pub instance: SPInstance, layout: Layout, strip_height: f64, strip_width: f64, @@ -48,16 +47,16 @@ impl SPProblem { } pub fn modify_strip_width(&mut self, new_width: f64) { - let old_p_uids = self.layout.placed_items().iter().map(|p_i| p_i.uid().clone()).collect_vec(); + let old_p_uids = self.layout.placed_items().iter().map(|p_i| p_i.uid.clone()).collect_vec(); self.missing_item_qtys.iter_mut().enumerate().for_each(|(i, qty)| *qty = self.instance.item_qty(i) as isize); let next_id = self.layout.id() + 1; - self.layout = Layout::new(next_id, Bin::from_strip(next_id, new_width, self.strip_height, self.layout.bin().base_cde().config().clone())); + self.layout = Layout::new(next_id, Bin::from_strip(next_id, new_width, self.strip_height, self.layout.bin().base_cde.config().clone())); self.strip_width = new_width; for p_uid in old_p_uids { let item = self.instance.item(p_uid.item_id); - let entities_to_ignore = item.hazard_filter().map_or(vec![], |f| hazard_filter::get_irrelevant_hazard_entities(f, self.layout.cde().all_hazards())); - let shape = item.shape(); + let entities_to_ignore = item.hazard_filter.as_ref().map_or(vec![], |f| hazard_filter::get_irrelevant_hazard_entities(f, self.layout.cde().all_hazards())); + let shape = &item.shape; let transf = p_uid.d_transf.compose(); if !self.layout.cde().surrogate_collides(shape.surrogate(), &transf, entities_to_ignore.as_slice()) { let transformed_shape = shape.transform_clone(&transf); @@ -76,7 +75,7 @@ impl SPProblem { pub fn fit_strip_width(&mut self) { let max_x = self.layout.placed_items().iter() - .map(|pi| pi.shape().bbox().x_max) + .map(|pi| pi.shape.bbox().x_max) .map(|x| NotNan::new(x).unwrap()) .max().map_or(0.0, |x| x.into_inner()); @@ -95,10 +94,6 @@ impl SPProblem { pub fn strip_width(&self) -> f64 { self.strip_width } - - fn instance(&self) -> &SPInstance { - &self.instance - } } impl ProblemGeneric for SPProblem { diff --git a/jaguars/src/entities/solution.rs b/jaguars/src/entities/solution.rs index 18b397b..f1d1e29 100644 --- a/jaguars/src/entities/solution.rs +++ b/jaguars/src/entities/solution.rs @@ -48,7 +48,7 @@ impl Solution { //ratio of included item area vs total instance item area let total_item_area = instance.item_area(); let included_item_area = self.placed_item_qtys.iter().enumerate() - .map(|(i, qty)| instance.item(i).shape().area() * *qty as f64) + .map(|(i, qty)| instance.item(i).shape.area() * *qty as f64) .sum::(); let completeness = included_item_area / total_item_area; completeness @@ -70,8 +70,8 @@ impl Solution { 0 => panic!("No stored layouts in solution"), 1 => { let bins = &bp_instance.bins; - let cheapest_bin = &bins.iter().min_by(|(b1, _), (b2, _)| b1.value().cmp(&b2.value())).unwrap().0; - self.layout_snapshots[0].bin.id() == cheapest_bin.id() + let cheapest_bin = &bins.iter().min_by(|(b1, _), (b2, _)| b1.value.cmp(&b2.value)).unwrap().0; + self.layout_snapshots[0].bin.id == cheapest_bin.id } _ => false } diff --git a/jaguars/src/geometry/convex_hull.rs b/jaguars/src/geometry/convex_hull.rs index e9e4286..41ab80e 100644 --- a/jaguars/src/geometry/convex_hull.rs +++ b/jaguars/src/geometry/convex_hull.rs @@ -1,13 +1,14 @@ use crate::geometry::primitives::point::Point; use crate::geometry::primitives::simple_polygon::SimplePolygon; + /// /// Returns the indices of the points in the SimplePolygon that form the convex hull pub fn convex_hull_indices(shape: &SimplePolygon) -> Vec { - let c_hull = convex_hull_from_points(shape.points().clone()); + let c_hull = convex_hull_from_points(shape.points.clone()); let mut indices = vec![]; for p in c_hull.iter() { - indices.push(shape.points().iter().position(|x| x == p).unwrap()); + indices.push(shape.points.iter().position(|x| x == p).unwrap()); } indices } diff --git a/jaguars/src/geometry/d_transformation.rs b/jaguars/src/geometry/d_transformation.rs index 26df860..cecede2 100644 --- a/jaguars/src/geometry/d_transformation.rs +++ b/jaguars/src/geometry/d_transformation.rs @@ -1,5 +1,6 @@ use std::borrow::Borrow; use std::fmt::Display; + use ordered_float::NotNan; use crate::geometry::transformation::Transformation; diff --git a/jaguars/src/geometry/fail_fast/poi.rs b/jaguars/src/geometry/fail_fast/poi.rs index d9801d6..3fa085d 100644 --- a/jaguars/src/geometry/fail_fast/poi.rs +++ b/jaguars/src/geometry/fail_fast/poi.rs @@ -7,7 +7,6 @@ use crate::geometry::primitives::aa_rectangle::AARectangle; use crate::geometry::primitives::circle::Circle; use crate::geometry::primitives::simple_polygon::SimplePolygon; - /// Generates the Pole of Inaccessibility (PoI) /// The interior is defined as the interior of the shape minus the interior of the poles /// Based on Mapbox's "Polylabel" algorithm: @@ -36,9 +35,9 @@ pub fn generate_next_pole(shape: &SimplePolygon, poles: &[Circle]) -> Circle { ///Generates additional poles for a shape alongside the PoI pub fn generate_additional_surrogate_poles(shape: &SimplePolygon, max_poles: usize, coverage_goal: f64) -> Vec { - let mut all_poles = vec![shape.poi().clone()]; + let mut all_poles = vec![shape.poi.clone()]; let pole_area_goal = shape.area() * coverage_goal; - let mut total_pole_area = shape.poi().area(); + let mut total_pole_area = shape.poi.area(); //Generate the poles for _ in 0..max_poles { @@ -63,7 +62,7 @@ pub fn generate_additional_surrogate_poles(shape: &SimplePolygon, max_poles: usi let next_pole = surrogate_poles.iter().enumerate().max_by_key(|(_i, p)| { //or to centroid if no other poles present let min_distance_to_existing_poles = sorted_poles.iter() - .chain([shape.poi()]) + .chain([&shape.poi]) .map(|p2| { let d = p.distance_from_border(&p2.centroid()).1; NotNan::new(d).unwrap() diff --git a/jaguars/src/geometry/fail_fast/sp_surrogate.rs b/jaguars/src/geometry/fail_fast/sp_surrogate.rs index ec2a8dd..2e65ed0 100644 --- a/jaguars/src/geometry/fail_fast/sp_surrogate.rs +++ b/jaguars/src/geometry/fail_fast/sp_surrogate.rs @@ -1,4 +1,3 @@ -use std::ops::Range; use crate::geometry::convex_hull; use crate::geometry::fail_fast::{piers, poi}; use crate::geometry::geo_traits::{Transformable, TransformableFrom}; @@ -20,7 +19,7 @@ pub struct SPSurrogate { impl SPSurrogate { pub fn new(simple_poly: &SimplePolygon, config: SPSurrogateConfig) -> Self { let convex_hull_indices = convex_hull::convex_hull_indices(simple_poly); - let mut poles = vec![simple_poly.poi().clone()]; + let mut poles = vec![simple_poly.poi.clone()]; poles.extend(poi::generate_additional_surrogate_poles(simple_poly, config.max_poles.saturating_sub(1), config.pole_coverage_goal)); let poles_bounding_circle = Circle::bounding_circle(&poles); diff --git a/jaguars/src/geometry/primitives/circle.rs b/jaguars/src/geometry/primitives/circle.rs index 46aafec..eaae381 100644 --- a/jaguars/src/geometry/primitives/circle.rs +++ b/jaguars/src/geometry/primitives/circle.rs @@ -7,6 +7,7 @@ use crate::geometry::primitives::aa_rectangle::AARectangle; use crate::geometry::primitives::edge::Edge; use crate::geometry::primitives::point::Point; use crate::geometry::transformation::Transformation; + /// Geometric primitive representing a circle #[derive(Clone, Debug, PartialEq)] pub struct Circle { diff --git a/jaguars/src/geometry/primitives/simple_polygon.rs b/jaguars/src/geometry/primitives/simple_polygon.rs index d60732e..f0cf76f 100644 --- a/jaguars/src/geometry/primitives/simple_polygon.rs +++ b/jaguars/src/geometry/primitives/simple_polygon.rs @@ -21,12 +21,12 @@ use crate::util::f64a::F64A; /// Geometric primitive representing a simple polygon: #[derive(Clone, Debug)] pub struct SimplePolygon { - points: Vec, - bbox: AARectangle, - area: f64, - diameter: f64, - poi: Circle, - surrogate: Option, + pub points: Vec, + pub bbox: AARectangle, + pub area: f64, + pub diameter: f64, + pub poi: Circle, + pub surrogate: Option, } impl SimplePolygon { @@ -88,18 +88,10 @@ impl SimplePolygon { (0..self.number_of_points()).map(move |i| self.get_edge(i)) } - pub fn points(&self) -> &Vec { - &self.points - } - pub fn number_of_points(&self) -> usize { self.points.len() } - pub fn poi(&self) -> &Circle { - &self.poi - } - pub fn surrogate(&self) -> &SPSurrogate { self.surrogate.as_ref().expect("surrogate not generated") } @@ -248,7 +240,7 @@ impl TransformableFrom for SimplePolygon { p.transform_from(ref_p, t); } - poi.transform_from(reference.poi(), t); + poi.transform_from(&reference.poi, t); //transform the surrogate if let Some(surrogate) = surrogate.as_mut() { diff --git a/jaguars/src/geometry/transformation.rs b/jaguars/src/geometry/transformation.rs index 1a70c6a..dfd784d 100644 --- a/jaguars/src/geometry/transformation.rs +++ b/jaguars/src/geometry/transformation.rs @@ -5,7 +5,6 @@ use ordered_float::NotNan; use crate::geometry::d_transformation::DTransformation; - #[derive(Clone, Debug)] ///Proper rigid transformation in matrix form pub struct Transformation { diff --git a/jaguars/src/io/parser.rs b/jaguars/src/io/parser.rs index 4b282ee..aa0237f 100644 --- a/jaguars/src/io/parser.rs +++ b/jaguars/src/io/parser.rs @@ -11,6 +11,7 @@ use crate::entities::placing_option::PlacingOption; use crate::entities::problems::bin_packing::BPProblem; use crate::entities::problems::problem::{LayoutIndex, Problem, ProblemGeneric}; use crate::entities::problems::strip_packing::SPProblem; +use crate::entities::quality_zone::N_QUALITIES; use crate::entities::quality_zone::QualityZone; use crate::entities::solution::Solution; use crate::geometry::d_transformation::DTransformation; @@ -21,7 +22,6 @@ use crate::geometry::primitives::simple_polygon::SimplePolygon; use crate::geometry::transformation::Transformation; use crate::io::json_instance::{JsonInstance, JsonPoly, JsonSimplePoly}; use crate::io::json_solution::{JsonLayout, JsonLayoutStats, JsonObjectType, JsonPlacedItem, JsonSolution, JsonTransformation}; -use crate::entities::quality_zone::N_QUALITIES; use crate::util::config::CDEConfig; use crate::util::polygon_simplification; use crate::util::polygon_simplification::{PolySimplConfig, PolySimplMode}; @@ -196,16 +196,16 @@ fn build_solution_from_json(json_layouts: &[JsonLayout], instance: Arc //Find the empty layout matching the bin id in the JSON solution, 0 if strip packing instance. let (empty_layout_index, _) = problem.empty_layouts().iter().enumerate() - .find(|(_, layout)| layout.bin().id() == bin.map_or(0, |b| b.id())).unwrap(); + .find(|(_, layout)| layout.bin().id == bin.map_or(0, |b| b.id)).unwrap(); - let bin_centering = bin.map_or(DTransformation::empty(), |b| DTransformation::from(b.centering_transform())).translation(); + let bin_centering = bin.map_or(DTransformation::empty(), |b| DTransformation::from(&b.centering_transform)).translation(); let json_first_item = json_layout.placed_items.get(0).unwrap(); let first_item = instance.item(json_first_item.item_index); //all items have a centering transformation applied during parsing. //However, the transformation described in the JSON solution is relative to the item's original position, not the one after the centering transformation - let first_item_centering_correction = first_item.centering_transform().clone().inverse().decompose().translation(); + let first_item_centering_correction = first_item.centering_transform.clone().inverse().decompose().translation(); let transf = Transformation::empty() .translate(first_item_centering_correction) //undo the item centering transformation @@ -217,7 +217,7 @@ fn build_solution_from_json(json_layouts: &[JsonLayout], instance: Arc let initial_insert_opt = PlacingOption { layout_index: LayoutIndex::Empty(empty_layout_index), - item_id: first_item.id(), + item_id: first_item.id, transf, d_transf, }; @@ -230,7 +230,7 @@ fn build_solution_from_json(json_layouts: &[JsonLayout], instance: Arc //Insert the rest of the items for json_item in json_layout.placed_items.iter().skip(1) { let item = instance.item(json_item.item_index); - let item_centering_correction = item.centering_transform().clone().inverse().decompose().translation(); + let item_centering_correction = item.centering_transform.clone().inverse().decompose().translation(); let transf = Transformation::empty() .translate(item_centering_correction) .rotate(json_item.transformation.rotation) @@ -241,7 +241,7 @@ fn build_solution_from_json(json_layouts: &[JsonLayout], instance: Arc let insert_opt = PlacingOption { layout_index: LayoutIndex::Existing(layout_index), - item_id: item.id(), + item_id: item.id, transf, d_transf, }; @@ -257,14 +257,14 @@ pub fn compose_json_solution(solution: &Solution, instance: &Instance, epoch: In let layouts = solution.layout_snapshots.iter() .map(|sl| { let object_type = match &instance { - Instance::BP(_bpi)=> JsonObjectType::Object { id: sl.bin.id() }, + Instance::BP(_bpi)=> JsonObjectType::Object { id: sl.bin.id }, Instance::SP(spi) => JsonObjectType::Strip { width: sl.bin.bbox().width(), height: spi.strip_height }, }; //JSON solution should have their bins back in their original position, so we need to correct for the centering transformation let bin_centering_correction = match &instance { Instance::BP(bpi) => { - let bin = &bpi.bins[sl.bin.id()].0; - bin.centering_transform().clone().inverse().decompose().translation() + let bin = &bpi.bins[sl.bin.id].0; + bin.centering_transform.clone().inverse().decompose().translation() }, Instance::SP(_) => (0.0, 0.0), //no bin, no correction }; @@ -273,7 +273,7 @@ pub fn compose_json_solution(solution: &Solution, instance: &Instance, epoch: In .map(|pl| { let item_index = pl.item_id(); let item = instance.item(item_index); - let item_centering = item.centering_transform().decompose().translation(); + let item_centering = item.centering_transform.decompose().translation(); let pl_decomp_transf = pl.d_transformation(); diff --git a/jaguars/src/util/assertions.rs b/jaguars/src/util/assertions.rs index db5cd61..e33fe53 100644 --- a/jaguars/src/util/assertions.rs +++ b/jaguars/src/util/assertions.rs @@ -4,21 +4,20 @@ use itertools::Itertools; use log::error; use crate::collision_detection::cd_engine::CDEngine; -use crate::collision_detection::hazard_filter::CombinedHazardFilter; -use crate::collision_detection::hazard_filter::EntityHazardFilter; use crate::collision_detection::hazard::Hazard; use crate::collision_detection::hazard::HazardEntity; use crate::collision_detection::hazard_filter; +use crate::collision_detection::hazard_filter::CombinedHazardFilter; +use crate::collision_detection::hazard_filter::EntityHazardFilter; use crate::collision_detection::quadtree::qt_hazard::QTHazard; use crate::collision_detection::quadtree::qt_hazard::QTHazPresence; use crate::collision_detection::quadtree::qt_node::QTNode; use crate::entities::bin::Bin; - use crate::entities::item::Item; use crate::entities::layout::Layout; +use crate::entities::layout::LayoutSnapshot; use crate::entities::problems::problem::ProblemGeneric; use crate::entities::solution::Solution; -use crate::entities::layout::LayoutSnapshot; use crate::geometry::geo_traits::{Shape, Transformable}; use crate::geometry::transformation::Transformation; use crate::util; @@ -28,14 +27,14 @@ use crate::util; pub fn instance_item_bin_ids_correct(items: &Vec<(Item, usize)>, bins: &Vec<(Bin, usize)>) -> bool { let mut id = 0; for (parttype, _qty) in items { - if parttype.id() != id { + if parttype.id != id { return false; } id += 1; } let mut id = 0; for (bin, _qty) in bins { - if bin.id() != id { + if bin.id != id { return false; } id += 1; @@ -55,11 +54,11 @@ pub fn problem_matches_solution(problem: &P, solution: &Solut } pub fn layouts_match(layout: &Layout, layout_snapshot: &LayoutSnapshot) -> bool { - if layout.bin().id() != layout_snapshot.bin.id() { + if layout.bin().id != layout_snapshot.bin.id { return false; } for sp_item in layout_snapshot.placed_items.iter() { - if layout.placed_items().iter().find(|sp| sp.uid() == sp_item.uid()).is_none() { + if layout.placed_items().iter().find(|sp| sp.uid == sp_item.uid).is_none() { return false; } } @@ -89,18 +88,18 @@ pub fn collision_hazards_sorted_correctly(hazards: &Vec) -> bool { } pub fn all_bins_and_items_centered(items: &Vec<(Item, usize)>, bins: &Vec<(Bin, usize)>) -> bool { - items.iter().map(|(i, _)| i.shape().centroid()) - .chain(bins.iter().map(|(b, _)| b.outer().centroid())) + items.iter().map(|(i, _)| i.shape.centroid()) + .chain(bins.iter().map(|(b, _)| b.outer.centroid())) .all(|c| almost::zero(c.0) && almost::zero(c.1)) } pub fn item_to_place_does_not_collide(item: &Item, transformation: &Transformation, layout: &Layout) -> bool { - let haz_filter = item.hazard_filter(); + let haz_filter = &item.hazard_filter; - let shape = item.shape(); + let shape = item.shape.as_ref(); let t_shape = shape.transform_clone(transformation); - let entities_to_ignore = haz_filter + let entities_to_ignore = haz_filter.as_ref() .map_or(vec![], |f| hazard_filter::get_irrelevant_hazard_entities(f, layout.cde().all_hazards())); if layout.cde().surrogate_collides(shape.surrogate(), transformation, &entities_to_ignore) || @@ -113,7 +112,7 @@ pub fn item_to_place_does_not_collide(item: &Item, transformation: &Transformati pub fn layout_is_collision_free(layout: &Layout) -> bool { for pi in layout.placed_items() { let ehf = EntityHazardFilter { entities: vec![pi.into()] }; - let combo_filter = match pi.haz_filter() { + let combo_filter = match &pi.qz_haz_filter { None => CombinedHazardFilter { filters: vec![Box::new(&ehf)] }, @@ -123,8 +122,8 @@ pub fn layout_is_collision_free(layout: &Layout) -> bool { }; let entities_to_ignore = hazard_filter::get_irrelevant_hazard_entities(&combo_filter, layout.cde().all_hazards()); - if layout.cde().shape_collides(pi.shape(), &entities_to_ignore) { - println!("Collision detected for item {:.?}", pi.uid()); + if layout.cde().shape_collides(&pi.shape, &entities_to_ignore) { + println!("Collision detected for item {:.?}", pi.uid); util::print_layout(layout); return false; } @@ -134,14 +133,14 @@ pub fn layout_is_collision_free(layout: &Layout) -> bool { pub fn qt_node_contains_no_deactivated_hazards<'a>(node: &'a QTNode, mut stacktrace: Vec<&'a QTNode>) -> (bool, Vec<&'a QTNode>) { stacktrace.push(node); - let deactivated_hazard = node.hazards().all_hazards().iter().find(|h| !h.active); + let deactivated_hazard = node.hazards.all_hazards().iter().find(|h| !h.active); if deactivated_hazard.is_some() { println!("Deactivated hazard found"); dbg!(&stacktrace); return (false, stacktrace); } - match node.children() { + match &node.children { Some(children) => { for child in children.as_ref() { let result = qt_node_contains_no_deactivated_hazards(child, stacktrace); @@ -160,7 +159,7 @@ pub fn qt_node_contains_no_deactivated_hazards<'a>(node: &'a QTNode, mut stacktr } pub fn qt_contains_no_dangling_hazards(cde: &CDEngine) -> bool { - if let Some(children) = cde.quadtree().children() { + if let Some(children) = &cde.quadtree().children { for child in children.as_ref() { if !qt_node_contains_no_dangling_hazards(child, cde.quadtree()) { return false; @@ -171,15 +170,15 @@ pub fn qt_contains_no_dangling_hazards(cde: &CDEngine) -> bool { } fn qt_node_contains_no_dangling_hazards(node: &QTNode, parent: &QTNode) -> bool { - let parent_h_entities = parent.hazards().all_hazards().iter().map(|h| &h.entity).unique().collect_vec(); + let parent_h_entities = parent.hazards.all_hazards().iter().map(|h| &h.entity).unique().collect_vec(); - let dangling_hazards = node.hazards().all_hazards().iter().any(|h| !parent_h_entities.contains(&&h.entity)); + let dangling_hazards = node.hazards.all_hazards().iter().any(|h| !parent_h_entities.contains(&&h.entity)); if dangling_hazards { println!("Node contains dangling hazard"); return false; } - match node.children() { + match &node.children { Some(children) => { for child in children.as_ref() { if !qt_node_contains_no_dangling_hazards(child, node) { @@ -194,7 +193,7 @@ fn qt_node_contains_no_dangling_hazards(node: &QTNode, parent: &QTNode) -> bool } pub fn qt_hz_entity_activation_consistent(cde: &CDEngine) -> bool { - for (active, hz_entity) in cde.quadtree().hazards().all_hazards().iter().map(|h| (h.active, &h.entity)).unique() { + for (active, hz_entity) in cde.quadtree().hazards.all_hazards().iter().map(|h| (h.active, &h.entity)).unique() { if !hz_entity_same_everywhere(cde.quadtree(), &hz_entity, active) { return false; } @@ -203,7 +202,7 @@ pub fn qt_hz_entity_activation_consistent(cde: &CDEngine) -> bool { } pub fn hz_entity_same_everywhere(qt_node: &QTNode, hz_entity: &HazardEntity, active: bool) -> bool { - match qt_node.hazards().all_hazards().iter().find(|h| &h.entity == hz_entity) { + match qt_node.hazards.all_hazards().iter().find(|h| &h.entity == hz_entity) { Some(h) => { if h.active != active { println!("Hazard entity activation inconsistent"); @@ -212,7 +211,7 @@ pub fn hz_entity_same_everywhere(qt_node: &QTNode, hz_entity: &HazardEntity, act } None => {} } - if let Some(children) = qt_node.children() { + if let Some(children) = &qt_node.children { for child in children.as_ref() { if !hz_entity_same_everywhere(child, hz_entity, active) { return false; @@ -228,7 +227,7 @@ pub fn layout_qt_matches_fresh_qt(layout: &Layout) -> bool { //rebuild the quadtree let bin = layout.bin(); - let mut fresh_cde = bin.base_cde().clone(); + let mut fresh_cde = bin.base_cde.as_ref().clone(); for pi in layout.placed_items() { fresh_cde.register_hazard(pi.into()); } @@ -241,8 +240,8 @@ fn qt_nodes_match(qn1: Option<&QTNode>, qn2: Option<&QTNode>) -> bool { match (qn1, qn2) { (Some(qn1), Some(qn2)) => { //if both nodes exist - let hv1 = qn1.hazards(); - let hv2 = qn2.hazards(); + let hv1 = &qn1.hazards; + let hv2 = &qn2.hazards; //collect active hazards to hashsets let active_haz_1 = hv1.active_hazards().iter() @@ -265,13 +264,13 @@ fn qt_nodes_match(qn1: Option<&QTNode>, qn2: Option<&QTNode>) -> bool { } } (Some(qn1), None) => { - if qn1.hazards().active_hazards().iter().next().is_some() { + if qn1.hazards.active_hazards().iter().next().is_some() { error!("qn1 contains active hazards while other qn2 does not exist"); return false; } } (None, Some(qn2)) => { - if qn2.hazards().active_hazards().iter().next().is_some() { + if qn2.hazards.active_hazards().iter().next().is_some() { error!("qn2 contains active hazards while other qn1 does not exist"); return false; } @@ -280,14 +279,14 @@ fn qt_nodes_match(qn1: Option<&QTNode>, qn2: Option<&QTNode>) -> bool { } //Check children - match (qn1.map_or(&None, |qn| qn.children()), qn2.map_or(&None, |qn| qn.children())) { + match (qn1.map_or(&None, |qn| &qn.children), qn2.map_or(&None, |qn| &qn.children)) { (None, None) => true, (Some(c1), None) => { let qn1_has_partial_hazards = qn1.map_or( false, |qn| { - qn.hazards().active_hazards().iter() + qn.hazards.active_hazards().iter() .any(|h| matches!(h.presence, QTHazPresence::Partial(_))) }, ); @@ -304,7 +303,7 @@ fn qt_nodes_match(qn1: Option<&QTNode>, qn2: Option<&QTNode>) -> bool { let qn2_has_partial_hazards = qn2.map_or( false, - |qn| qn.hazards().active_hazards().iter() + |qn| qn.hazards.active_hazards().iter() .any(|h| matches!(h.presence, QTHazPresence::Partial(_))), ); if qn2_has_partial_hazards { diff --git a/jaguars/src/util/config.rs b/jaguars/src/util/config.rs index 97302f1..505cd68 100644 --- a/jaguars/src/util/config.rs +++ b/jaguars/src/util/config.rs @@ -2,23 +2,11 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] pub struct CDEConfig { - pub quadtree: QuadTreeConfig, - pub haz_prox: HazProxConfig, + pub quadtree_depth: u8, + pub hpg_n_cells: usize, pub item_surrogate_config: SPSurrogateConfig, } -#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] -pub enum QuadTreeConfig { - FixedDepth(u8), - Auto, -} - -#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] -pub enum HazProxConfig { - Disabled, - Enabled { n_cells: usize }, -} - #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] pub struct SPSurrogateConfig { /// poles will be generated until this percentage of the shape is covered diff --git a/jaguars/src/util/mod.rs b/jaguars/src/util/mod.rs index fed2f47..8729d98 100644 --- a/jaguars/src/util/mod.rs +++ b/jaguars/src/util/mod.rs @@ -8,12 +8,12 @@ pub mod polygon_simplification; ///Intended for debugging purposes pub fn print_layout(layout: &Layout) { - println!("let mut layout = Layout::new(0, instance.bin({}).clone());", layout.bin().id()); + println!("let mut layout = Layout::new(0, instance.bin({}).clone());", layout.bin().id); println!(); for pi in layout.placed_items() { let transformation_str = { - let t_decomp = &pi.uid().d_transf; + let t_decomp = &pi.uid.d_transf; let (tr, (tx, ty)) = (t_decomp.rotation(), t_decomp.translation()); format!("&DTransformation::new({:.6},({:.6},{:.6}))", tr, tx, ty) }; diff --git a/jaguars/src/util/polygon_simplification.rs b/jaguars/src/util/polygon_simplification.rs index c22568a..317b842 100644 --- a/jaguars/src/util/polygon_simplification.rs +++ b/jaguars/src/util/polygon_simplification.rs @@ -1,4 +1,5 @@ use std::cmp::Ordering; + use itertools::Itertools; use log::{Level, log}; use serde::{Deserialize, Serialize}; @@ -9,8 +10,11 @@ use crate::geometry::primitives::point::Point; use crate::geometry::primitives::simple_polygon::SimplePolygon; #[derive(Serialize, Deserialize, Clone, Copy, Debug)] +#[serde(tag = "mode", content = "params")] pub enum PolySimplConfig { + #[serde(rename = "disabled")] Disabled, + #[serde(rename = "enabled")] Enabled { tolerance: f64, //max deviation from the original polygon area (in %) }, @@ -111,9 +115,9 @@ pub fn simplify_shape(shape: &SimplePolygon, mode: PolySimplMode, max_area_delta } let mut prev_corner = corners.last().expect("corners is empty"); - let mut prev_corner_type = CornerType::from(prev_corner.to_points(ref_shape.points())); + let mut prev_corner_type = CornerType::from(prev_corner.to_points(&ref_shape.points)); for corner in corners.iter() { - let corner_type = CornerType::from(corner.to_points(ref_shape.points())); + let corner_type = CornerType::from(corner.to_points(&ref_shape.points)); //Generate a removal candidate (or not) let candidate = match (&corner_type, &prev_corner_type) { @@ -225,7 +229,7 @@ fn candidate_is_valid(shape: &SimplePolygon, candidate: &Candidate) -> bool { } fn execute_candidate(shape: &SimplePolygon, candidate: &Candidate) -> SimplePolygon { - let mut points = shape.points().clone(); + let mut points = shape.points.clone(); match candidate { Candidate::Collinear(c) => { points.remove(c.1); } Candidate::Concave(c) => { points.remove(c.1); } diff --git a/lbf/benches/edge_sensitivity_bench.rs b/lbf/benches/edge_sensitivity_bench.rs index 6593007..574a167 100644 --- a/lbf/benches/edge_sensitivity_bench.rs +++ b/lbf/benches/edge_sensitivity_bench.rs @@ -1,13 +1,14 @@ use std::fs::File; use std::io::BufReader; use std::path::Path; -use std::sync::Arc; + use criterion::{BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main}; use criterion::measurement::WallTime; use itertools::Itertools; -use rand::prelude::{IteratorRandom, SmallRng}; +use rand::prelude::SmallRng; use rand::SeedableRng; -use jaguars::entities::instance::{BPInstance, Instance, SPInstance, InstanceGeneric}; + +use jaguars::entities::instance::{BPInstance, Instance, InstanceGeneric, SPInstance}; use jaguars::entities::item::Item; use jaguars::entities::problems::problem::{LayoutIndex, ProblemGeneric}; use jaguars::geometry::geo_traits::{Shape, TransformableFrom}; @@ -15,12 +16,11 @@ use jaguars::geometry::primitives::point::Point; use jaguars::geometry::primitives::simple_polygon::SimplePolygon; use jaguars::io::json_instance::JsonInstance; use lbf::config::Config; - use lbf::io; use lbf::io::svg_util::SvgDrawOptions; use lbf::samplers::hpg_sampler::HPGSampler; -use lbf::samplers::uniform_rect_sampler::UniformAARectSampler; -use crate::util::{N_ITEMS_REMOVED, N_SAMPLES_PER_ITER, N_TOTAL_SAMPLES, SWIM_PATH}; + +use crate::util::{N_ITEMS_REMOVED, SWIM_PATH}; criterion_main!(benches); criterion_group!(benches, edge_sensitivity_bench_no_ff, edge_sensitivity_bench_with_ff); @@ -29,6 +29,9 @@ mod util; const EDGE_MULTIPLIERS: [u8; 5] = [1, 2, 4, 8, 16]; +const N_TOTAL_SAMPLES: usize = 100_000; +const N_SAMPLES_PER_ITER: usize = 1000; + fn edge_sensitivity_bench_no_ff(c: &mut Criterion){ let mut config = util::create_base_config(); config.cde_config.item_surrogate_config.n_ff_poles = 0; @@ -53,7 +56,7 @@ fn edge_sensitivity_bench(config: Config, mut g: BenchmarkGroup) { modify_instance(&instance, edge_multiplier as usize, config) }; - let (mut problem,selected_pi_uids) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); + let (problem,selected_pi_uids) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); { let draw_options = SvgDrawOptions{ @@ -92,12 +95,12 @@ fn edge_sensitivity_bench(config: Config, mut g: BenchmarkGroup) { for i in 0..N_ITEMS_REMOVED { let pi_uid = &selected_pi_uids[i]; let item = instance.item(pi_uid.item_id); - let mut buffer_shape = item.shape().clone(); + let mut buffer_shape = item.shape.as_ref().clone(); for transf in samples_cycler.next().unwrap() { - let collides = match layout.cde().surrogate_collides(item.shape().surrogate(), transf, &[]) { + let collides = match layout.cde().surrogate_collides(item.shape.surrogate(), transf, &[]) { true => true, false => { - buffer_shape.transform_from(item.shape(), transf); + buffer_shape.transform_from(&item.shape, transf); layout.cde().shape_collides(&buffer_shape, &[]) } }; @@ -116,15 +119,15 @@ fn edge_sensitivity_bench(config: Config, mut g: BenchmarkGroup) { fn modify_instance(instance: &Instance, multiplier: usize, config: Config) -> Instance { let modified_items = instance.items().iter().map(|(item, qty)| { - let modified_shape = multiply_edge_count(item.shape(), multiplier); + let modified_shape = multiply_edge_count(&item.shape, multiplier); let modified_item = Item::new( - item.id(), + item.id, modified_shape, - item.value(), - item.allowed_rotation().clone(), - item.centering_transform().clone(), - item.base_quality(), + item.value, + item.allowed_rotation.clone(), + item.centering_transform.clone(), + item.base_quality, config.cde_config.item_surrogate_config ); (modified_item, *qty) @@ -149,7 +152,7 @@ fn multiply_edge_count(shape: &SimplePolygon, multiplier: usize) -> SimplePolygo } } - let mut new_polygon = SimplePolygon::new(new_points); + let new_polygon = SimplePolygon::new(new_points); assert!(almost::equal(shape.area(), new_polygon.area())); new_polygon } \ No newline at end of file diff --git a/lbf/benches/fast_fail_bench.rs b/lbf/benches/fast_fail_bench.rs index 5ed0e87..93e75fa 100644 --- a/lbf/benches/fast_fail_bench.rs +++ b/lbf/benches/fast_fail_bench.rs @@ -1,12 +1,10 @@ use std::fs::File; use std::io::BufReader; -use std::ops::RangeInclusive; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use itertools::Itertools; -use rand::prelude::{IteratorRandom, SmallRng}; +use rand::prelude::SmallRng; use rand::SeedableRng; -use rand_distr::num_traits::real::Real; use jaguars::entities::instance::InstanceGeneric; use jaguars::entities::problems::problem::{LayoutIndex, ProblemGeneric}; @@ -17,23 +15,23 @@ use jaguars::geometry::geo_traits::TransformableFrom; use jaguars::geometry::primitives::circle::Circle; use jaguars::geometry::primitives::simple_polygon::SimplePolygon; use jaguars::io::json_instance::JsonInstance; -use jaguars::util::config::HazProxConfig; use lbf::samplers::hpg_sampler::HPGSampler; -use crate::util::{create_base_config, N_ITEMS_REMOVED, N_SAMPLES_PER_ITER, N_TOTAL_SAMPLES, SWIM_PATH}; +use crate::util::{create_base_config, N_ITEMS_REMOVED, SWIM_PATH}; criterion_main!(benches); criterion_group!(benches, fast_fail_query_bench); mod util; -//const FF_POLE_RANGE: &[usize] = &[0,1,2,3,4]; -//const FF_PIER_RANGE: &[usize] = &[0,1,2,3,4]; +const FF_POLE_RANGE: &[usize] = &[0,1,2,3,4]; +const FF_PIER_RANGE: &[usize] = &[0,1,2,3,4]; -const FF_POLE_RANGE: &[usize] = &[5,6,7,8,9,10]; -const FF_PIER_RANGE: &[usize] = &[0]; const ITEMS_ID_TO_TEST: &[usize] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +const N_TOTAL_SAMPLES: usize = 100_000; +const N_SAMPLES_PER_ITER: usize = 1000; + /// Benchmark the query operation of the quadtree for different depths /// We validate 1000 sampled transformations for each of the 5 removed items fn fast_fail_query_bench(c: &mut Criterion) { @@ -46,12 +44,13 @@ fn fast_fail_query_bench(c: &mut Criterion) { .flatten().collect_vec(); let mut config = create_base_config(); - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: 5000}; + config.cde_config.quadtree_depth = 5; + config.cde_config.hpg_n_cells = 2000; let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); - let (mut problem, _) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); + let (problem, _) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); - println!("avg number of edges per item: {}", ITEMS_ID_TO_TEST.iter().map(|&item_id| instance.item(item_id).shape().number_of_points()).sum::() as f64 / ITEMS_ID_TO_TEST.len() as f64); + println!("avg number of edges per item: {}", ITEMS_ID_TO_TEST.iter().map(|&item_id| instance.item(item_id).shape.number_of_points()).sum::() as f64 / ITEMS_ID_TO_TEST.len() as f64); let mut rng = SmallRng::seed_from_u64(0); let layout = problem.get_layout(LayoutIndex::Existing(0)); @@ -65,7 +64,7 @@ fn fast_fail_query_bench(c: &mut Criterion) { let (n_ff_poles, n_ff_piers) = ff_surr_config; let custom_surrogates = ITEMS_ID_TO_TEST.iter() - .map(|&item_id| create_custom_surrogate(instance.item(item_id).shape(), n_ff_poles, n_ff_piers)) + .map(|&item_id| create_custom_surrogate(&instance.item(item_id).shape, n_ff_poles, n_ff_piers)) .collect_vec(); let mut samples_cyclers = samples.iter() @@ -79,7 +78,7 @@ fn fast_fail_query_bench(c: &mut Criterion) { let mut buffer_shapes = ITEMS_ID_TO_TEST.iter() .map(|&item_id| instance.item(item_id)) - .map(|item| item.shape().clone_and_strip_surrogate()) + .map(|item| item.shape.clone_and_strip_surrogate()) .collect_vec(); group.bench_function(BenchmarkId::from_parameter(format!("{n_ff_poles}_poles_{n_ff_piers}_piers")), |b| { @@ -92,7 +91,7 @@ fn fast_fail_query_bench(c: &mut Criterion) { let collides = match layout.cde().surrogate_collides(surrogate, transf, &[]) { true => true, false => { - buffer_shape.transform_from(item.shape(), transf); + buffer_shape.transform_from(&item.shape, transf); layout.cde().shape_collides(&buffer_shape, &[]) } }; @@ -110,7 +109,7 @@ fn fast_fail_query_bench(c: &mut Criterion) { pub fn create_custom_surrogate(simple_poly: &SimplePolygon, n_poles: usize, n_piers: usize) -> SPSurrogate { let convex_hull_indices = convex_hull::convex_hull_indices(simple_poly); - let mut poles = vec![simple_poly.poi().clone()]; + let mut poles = vec![simple_poly.poi.clone()]; poles.extend(poi::generate_additional_surrogate_poles(simple_poly, n_poles.saturating_sub(1), 0.9)); let poles_bounding_circle = Circle::bounding_circle(&poles); diff --git a/lbf/benches/hpg_bench.rs b/lbf/benches/hpg_bench.rs index 7fdeb26..ae2a437 100644 --- a/lbf/benches/hpg_bench.rs +++ b/lbf/benches/hpg_bench.rs @@ -1,11 +1,8 @@ use std::fs::File; use std::io::BufReader; -use std::ops::RangeInclusive; -use std::path::Path; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use itertools::Itertools; -use rand::prelude::IteratorRandom; use rand::rngs::SmallRng; use rand::SeedableRng; @@ -17,33 +14,32 @@ use jaguars::entities::problems::strip_packing::SPProblem; use jaguars::geometry::geo_traits::Shape; use jaguars::geometry::geo_traits::TransformableFrom; use jaguars::io::json_instance::JsonInstance; -use jaguars::util::config::HazProxConfig; -use lbf::io; -use lbf::io::svg_util::SvgDrawOptions; use lbf::samplers::hpg_sampler::HPGSampler; -use crate::util::{create_base_config, N_ITEMS_REMOVED, N_VALID_SAMPLES, SWIM_PATH}; +use crate::util::{create_base_config, N_ITEMS_REMOVED, SWIM_PATH}; criterion_main!(benches); criterion_group!(benches,hpg_query_bench, hpg_update_bench); mod util; -pub const N_HPG_CELLS: [usize; 6] = [100, 500, 1000, 2000, 5000, 10000]; -pub const SELECTED_ITEM_ID: usize = 1; // relatively small and "round" item, guaranteed to find valid samples even without HPG +const N_HPG_CELLS: [usize; 6] = [100, 500, 1000, 2000, 5000, 10000]; +const SELECTED_ITEM_ID: usize = 1; // relatively small and "round" item, guaranteed to find valid samples even without HPG + +const N_VALID_SAMPLES: usize = 1000; fn hpg_query_bench(c: &mut Criterion) { - ///HPG density has side effects on the LBF optimize, so we create a single problem instance and create a solution from it. + //HPG density has side effects on the LBF optimize, so we create a single problem instance and create a solution from it. let json_instance: JsonInstance = serde_json::from_reader(BufReader::new(File::open(SWIM_PATH).unwrap())).unwrap(); - let mut base_config = create_base_config(); + let base_config = create_base_config(); let base_instance = util::create_instance(&json_instance, base_config.cde_config, base_config.poly_simpl_config); - let (mut base_problem, removed_items) = util::create_blf_problem(base_instance.clone(), base_config, N_ITEMS_REMOVED); - let base_pi_uids = base_problem.get_layout(LayoutIndex::Existing(0)).placed_items().iter().map(|pi| pi.uid().clone()).collect_vec(); + let (base_problem, _) = util::create_blf_problem(base_instance.clone(), base_config, N_ITEMS_REMOVED); + let base_pi_uids = base_problem.get_layout(LayoutIndex::Existing(0)).placed_items().iter().map(|pi| pi.uid.clone()).collect_vec(); let mut group = c.benchmark_group("hpg_bench_query"); for n_hpg_cells in N_HPG_CELLS { let mut config = base_config; - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: n_hpg_cells }; + config.cde_config.hpg_n_cells = n_hpg_cells; //create the instance and problem with the specific HPG config let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); let mut problem = match instance.clone() { @@ -77,26 +73,23 @@ fn hpg_query_bench(c: &mut Criterion) { // Search N_VALID_SAMPLES for each item let item = instance.item(SELECTED_ITEM_ID); let layout = problem.get_layout(LayoutIndex::Existing(0)); - let surrogate = item.shape().surrogate(); - let mut buffer_shape = item.shape().clone(); + let surrogate = item.shape.surrogate(); + let mut buffer_shape = item.shape.as_ref().clone(); let sampler = HPGSampler::new(item, layout).unwrap(); println!("[{}] sampler coverage: {:.3}% with {} samplers", n_hpg_cells, sampler.coverage_area / layout.bin().bbox().area() * 100.0, sampler.cell_samplers.len()); group.bench_function(BenchmarkId::from_parameter(n_hpg_cells), |b| { b.iter(|| { let mut n_valid_samples = 0; - let mut n_samples = 0; while n_valid_samples < N_VALID_SAMPLES { let transf = sampler.sample(&mut rng); if !layout.cde().surrogate_collides(surrogate, &transf, &[]) { - buffer_shape.transform_from(item.shape(), &transf); + buffer_shape.transform_from(&item.shape, &transf); if !layout.cde().shape_collides(&buffer_shape, &[]) { n_valid_samples += 1; } } - n_samples += 1; } - //println!("n_samples: {}", n_samples); }) }); } @@ -104,17 +97,17 @@ fn hpg_query_bench(c: &mut Criterion) { } fn hpg_update_bench(c: &mut Criterion) { - ///HPG density has side effects on the LBF optimize, so we create a single problem instance and create a solution from it. + //HPG density has side effects on the LBF optimize, so we create a single problem instance and create a solution from it. let json_instance: JsonInstance = serde_json::from_reader(BufReader::new(File::open(SWIM_PATH).unwrap())).unwrap(); - let mut base_config = create_base_config(); + let base_config = create_base_config(); let base_instance = util::create_instance(&json_instance, base_config.cde_config, base_config.poly_simpl_config); - let (mut base_problem, removed_items) = util::create_blf_problem(base_instance.clone(), base_config, N_ITEMS_REMOVED); - let base_pi_uids = base_problem.get_layout(LayoutIndex::Existing(0)).placed_items().iter().map(|pi| pi.uid().clone()).collect_vec(); + let (base_problem, _) = util::create_blf_problem(base_instance.clone(), base_config, N_ITEMS_REMOVED); + let base_pi_uids = base_problem.get_layout(LayoutIndex::Existing(0)).placed_items().iter().map(|pi| pi.uid.clone()).collect_vec(); let mut group = c.benchmark_group("hpg_bench_update"); for n_hpg_cells in N_HPG_CELLS { let mut config = base_config; - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: n_hpg_cells }; + config.cde_config.hpg_n_cells = n_hpg_cells; //create the instance and problem with the specific HPG config let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); let mut problem = match instance.clone() { @@ -147,8 +140,8 @@ fn hpg_update_bench(c: &mut Criterion) { // Search N_VALID_SAMPLES for each item let item = instance.item(SELECTED_ITEM_ID); let layout = problem.get_layout(LayoutIndex::Existing(0)); - let surrogate = item.shape().surrogate(); - let mut buffer_shape = item.shape().clone(); + let surrogate = item.shape.surrogate(); + let mut buffer_shape = item.shape.as_ref().clone(); let sampler = HPGSampler::new(item, layout).unwrap(); println!("[{}] sampler coverage: {:.3}% with {} samplers", n_hpg_cells, sampler.coverage_area / layout.bin().bbox().area() * 100.0, sampler.cell_samplers.len()); @@ -157,7 +150,7 @@ fn hpg_update_bench(c: &mut Criterion) { while valid_placements.len() < N_VALID_SAMPLES { let transf = sampler.sample(&mut rng); if !layout.cde().surrogate_collides(surrogate, &transf, &[]) { - buffer_shape.transform_from(item.shape(), &transf); + buffer_shape.transform_from(&item.shape, &transf); if !layout.cde().shape_collides(&buffer_shape, &[]) { let d_transf = transf.decompose(); valid_placements.push(PlacingOption { diff --git a/lbf/benches/quadtree_bench.rs b/lbf/benches/quadtree_bench.rs index 4cd4833..ca8f3ca 100644 --- a/lbf/benches/quadtree_bench.rs +++ b/lbf/benches/quadtree_bench.rs @@ -8,15 +8,13 @@ use rand::SeedableRng; use rand::seq::IteratorRandom; use jaguars::entities::instance::InstanceGeneric; -use jaguars::entities::placed_item::PlacedItemUID; use jaguars::entities::placing_option::PlacingOption; use jaguars::entities::problems::problem::{LayoutIndex, ProblemGeneric}; use jaguars::geometry::geo_traits::TransformableFrom; use jaguars::io::json_instance::JsonInstance; -use jaguars::util::config::{HazProxConfig, QuadTreeConfig}; use lbf::samplers::uniform_rect_sampler::UniformAARectSampler; -use crate::util::{create_base_config, N_ITEMS_REMOVED, N_SAMPLES_PER_ITER, N_TOTAL_SAMPLES, SWIM_PATH}; +use crate::util::{create_base_config, N_ITEMS_REMOVED, SWIM_PATH}; criterion_main!(benches); criterion_group!(benches, quadtree_query_update_1000_1, quadtree_query_bench, quadtree_update_bench); @@ -25,6 +23,9 @@ mod util; const QT_DEPTHS: [u8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; +const N_TOTAL_SAMPLES: usize = 100_000; +const N_SAMPLES_PER_ITER: usize = 1000; + /// Benchmark the update operation of the quadtree for different depths /// From a solution, created by the LBF optimizer, 5 items are removed and then inserted back again fn quadtree_update_bench(c: &mut Criterion) { @@ -34,11 +35,11 @@ fn quadtree_update_bench(c: &mut Criterion) { config.cde_config.item_surrogate_config.n_ff_poles = 0; config.cde_config.item_surrogate_config.n_ff_piers = 0; //disable haz prox grid - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: 1 }; + config.cde_config.hpg_n_cells = 1; let mut group = c.benchmark_group("quadtree_update"); for depth in QT_DEPTHS { - config.cde_config.quadtree = QuadTreeConfig::FixedDepth(depth); + config.cde_config.quadtree_depth = depth; let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); let (mut problem, _) = util::create_blf_problem(instance.clone(), config, 0); @@ -49,7 +50,7 @@ fn quadtree_update_bench(c: &mut Criterion) { b.iter(|| { // Remove an item from the layout let pi_uid = problem.get_layout(&layout_index).placed_items().iter() - .map(|p_i| p_i.uid().clone()) + .map(|p_i| p_i.uid.clone()) .choose(&mut rng).expect("No items in layout"); //println!("Removing item with id: {}\n", pi_uid.item_id); @@ -78,13 +79,13 @@ fn quadtree_query_bench(c: &mut Criterion) { config.cde_config.item_surrogate_config.n_ff_poles = 0; config.cde_config.item_surrogate_config.n_ff_piers = 0; //disable haz prox grid - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: 1 }; + config.cde_config.hpg_n_cells = 1; let mut group = c.benchmark_group("quadtree_query"); for depth in QT_DEPTHS { - config.cde_config.quadtree = QuadTreeConfig::FixedDepth(depth); + config.cde_config.quadtree_depth = depth; let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); - let (mut problem, selected_pi_uids) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); + let (problem, selected_pi_uids) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); let layout = problem.get_layout(LayoutIndex::Existing(0)); let sampler = UniformAARectSampler::new(layout.bin().bbox(), instance.item(0)); @@ -104,9 +105,9 @@ fn quadtree_query_bench(c: &mut Criterion) { let item_id = item_id_cycler.next().unwrap(); let item = instance.item(item_id); let layout = problem.get_layout(LayoutIndex::Existing(0)); - let mut buffer_shape = item.shape().clone(); + let mut buffer_shape = item.shape.as_ref().clone(); for transf in sample_cycler.next().unwrap() { - buffer_shape.transform_from(item.shape(), transf); + buffer_shape.transform_from(&item.shape, transf); let collides = layout.cde().shape_collides(&buffer_shape, &[]); if collides { n_invalid += 1; @@ -128,14 +129,14 @@ fn quadtree_query_update_1000_1(c: &mut Criterion) { config.cde_config.item_surrogate_config.n_ff_poles = 0; config.cde_config.item_surrogate_config.n_ff_piers = 0; //disable haz prox grid - config.cde_config.haz_prox = HazProxConfig::Enabled { n_cells: 1 }; + config.cde_config.hpg_n_cells = 1; let mut group = c.benchmark_group("quadtree_query_update_1000_1"); for depth in QT_DEPTHS { - config.cde_config.quadtree = QuadTreeConfig::FixedDepth(depth); + config.cde_config.quadtree_depth = depth; let instance = util::create_instance(&json_instance, config.cde_config, config.poly_simpl_config); - let (mut problem, selected_pi_uids) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); + let (mut problem, _) = util::create_blf_problem(instance.clone(), config, N_ITEMS_REMOVED); let layout = problem.get_layout(LayoutIndex::Existing(0)); let sampler = UniformAARectSampler::new(layout.bin().bbox(), instance.item(0)); @@ -148,7 +149,7 @@ fn quadtree_query_update_1000_1(c: &mut Criterion) { group.bench_function(BenchmarkId::from_parameter(depth), |b| { b.iter(|| { let pi_uid = problem.get_layout(LayoutIndex::Existing(0)).placed_items().iter() - .map(|p_i| p_i.uid().clone()) + .map(|p_i| p_i.uid.clone()) .choose(&mut rng).expect("No items in layout"); problem.remove_item(LayoutIndex::Existing(0), &pi_uid, true); @@ -157,9 +158,9 @@ fn quadtree_query_update_1000_1(c: &mut Criterion) { let item_id = pi_uid.item_id; let item = instance.item(item_id); let layout = problem.get_layout(LayoutIndex::Existing(0)); - let mut buffer_shape = item.shape().clone(); + let mut buffer_shape = item.shape.as_ref().clone(); for transf in sample_cycler.next().unwrap() { - buffer_shape.transform_from(item.shape(), &transf); + buffer_shape.transform_from(&item.shape, &transf); let collides = layout.cde().shape_collides(&buffer_shape, &[]); criterion::black_box(collides); //prevent the compiler from optimizing the loop away } diff --git a/lbf/benches/util.rs b/lbf/benches/util.rs index 51d0642..9489c50 100644 --- a/lbf/benches/util.rs +++ b/lbf/benches/util.rs @@ -1,32 +1,25 @@ use std::path::Path; -use std::sync::Arc; -use criterion::Criterion; -use log::{info, Level, LevelFilter}; + +use log::info; use rand::prelude::{IteratorRandom, SmallRng}; use rand::SeedableRng; + use jaguars::entities::instance::Instance; use jaguars::entities::instance::InstanceGeneric; use jaguars::entities::placed_item::PlacedItemUID; -use jaguars::entities::problems::problem::{LayoutIndex, ProblemGeneric, Problem}; +use jaguars::entities::problems::problem::{LayoutIndex, Problem, ProblemGeneric}; use jaguars::entities::problems::strip_packing::SPProblem; use jaguars::io::json_instance::JsonInstance; use jaguars::io::parser::Parser; -use jaguars::util::config::{CDEConfig, HazProxConfig, SPSurrogateConfig}; -use jaguars::util::config::QuadTreeConfig::FixedDepth; +use jaguars::util::config::{CDEConfig, SPSurrogateConfig}; use jaguars::util::polygon_simplification::PolySimplConfig; use lbf::config::Config; use lbf::io; -use lbf::io::init_logger; use lbf::io::svg_util::SvgDrawOptions; use lbf::lbf_optimizer::LBFOptimizer; pub const SWIM_PATH: &str = "../assets/swim.json"; pub const N_ITEMS_REMOVED: usize = 5; -pub const N_TOTAL_SAMPLES: usize = 100_000; - -pub const N_SAMPLES_PER_ITER: usize = 1000; - -pub const N_VALID_SAMPLES: usize = 1000; pub fn create_instance(json_instance: &JsonInstance, cde_config: CDEConfig, poly_simpl_config: PolySimplConfig) -> Instance { let parser = Parser::new(poly_simpl_config, cde_config, true); @@ -50,12 +43,12 @@ pub fn create_blf_problem(instance: Instance, config: Config, n_items_removed: u let layout_index = LayoutIndex::Existing(0); // Remove some items from the layout let removed_pi_uids = problem.get_layout(&layout_index).placed_items().iter() - .map(|p_i| p_i.uid().clone()) + .map(|p_i| p_i.uid.clone()) .choose_multiple(&mut rng, n_items_removed); for pi_uid in removed_pi_uids.iter() { problem.remove_item(layout_index, pi_uid, true); - info!("Removed item: {} with {} edges", pi_uid.item_id, lbf_optimizer.instance().item(pi_uid.item_id).shape().number_of_points()); + info!("Removed item: {} with {} edges", pi_uid.item_id, lbf_optimizer.instance().item(pi_uid.item_id).shape.number_of_points()); } problem.flush_changes(); @@ -76,8 +69,8 @@ pub fn create_blf_problem(instance: Instance, config: Config, n_items_removed: u pub fn create_base_config() -> Config { Config { cde_config: CDEConfig { - quadtree: FixedDepth(5), - haz_prox: HazProxConfig::Enabled {n_cells: 5000}, + quadtree_depth: 5, + hpg_n_cells: 2000, item_surrogate_config: SPSurrogateConfig { pole_coverage_goal: 0.9, max_poles: 10, diff --git a/lbf/src/config.rs b/lbf/src/config.rs index 6db932a..d02cecb 100644 --- a/lbf/src/config.rs +++ b/lbf/src/config.rs @@ -1,6 +1,8 @@ -use jaguars::util::config::{CDEConfig, HazProxConfig, QuadTreeConfig, SPSurrogateConfig}; use serde::{Deserialize, Serialize}; + +use jaguars::util::config::{CDEConfig, SPSurrogateConfig}; use jaguars::util::polygon_simplification::PolySimplConfig; + use crate::io::svg_util::SvgDrawOptions; #[derive(Debug, Serialize, Deserialize, Clone, Copy)] @@ -18,8 +20,8 @@ impl Default for Config { fn default() -> Self { Self { cde_config: CDEConfig{ - quadtree: QuadTreeConfig::FixedDepth(4), - haz_prox: HazProxConfig::Enabled{n_cells: 10000}, + quadtree_depth: 5, + hpg_n_cells: 2000, item_surrogate_config: SPSurrogateConfig{ pole_coverage_goal: 0.9, max_poles: 10, diff --git a/lbf/src/io/cli.rs b/lbf/src/io/cli.rs index df2659b..e29130b 100644 --- a/lbf/src/io/cli.rs +++ b/lbf/src/io/cli.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; + use clap::Parser; #[derive(Parser, Debug)] @@ -7,7 +8,7 @@ pub struct Cli{ #[arg(short, long, value_name = "FILE")] pub input_file: PathBuf, #[arg(short, long, value_name = "FILE")] - pub config_file: PathBuf, + pub config_file: Option, #[arg(short, long, value_name = "FOLDER")] pub solution_folder: PathBuf } \ No newline at end of file diff --git a/lbf/src/io/json_output.rs b/lbf/src/io/json_output.rs index 6b77111..b7fec51 100644 --- a/lbf/src/io/json_output.rs +++ b/lbf/src/io/json_output.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; + use jaguars::io::json_instance::JsonInstance; use jaguars::io::json_solution::JsonSolution; + use crate::config::Config; #[derive(Serialize, Deserialize, Clone)] diff --git a/lbf/src/io/layout_to_svg.rs b/lbf/src/io/layout_to_svg.rs index a5deba9..39bc039 100644 --- a/lbf/src/io/layout_to_svg.rs +++ b/lbf/src/io/layout_to_svg.rs @@ -1,5 +1,6 @@ use svg::Document; use svg::node::element::Group; + use jaguars::entities::instance::Instance; use jaguars::entities::instance::InstanceGeneric; use jaguars::entities::layout::Layout; @@ -7,8 +8,9 @@ use jaguars::entities::layout::LayoutSnapshot; use jaguars::geometry::geo_enums::GeoPosition; use jaguars::geometry::geo_traits::Transformable; use jaguars::geometry::primitives::circle::Circle; + use crate::io::{svg_export, svg_util}; -use crate::io::svg_util::{SvgDrawOptions}; +use crate::io::svg_util::SvgDrawOptions; pub fn s_layout_to_svg(s_layout: &LayoutSnapshot, instance: &Instance, options: SvgDrawOptions) -> Document { let layout = Layout::new_from_stored(s_layout.id, s_layout); @@ -34,7 +36,7 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio //outer group = group.add( svg_export::data_to_path( - svg_export::simple_polygon_data(bin.outer()), + svg_export::simple_polygon_data(&bin.outer), &[ ("fill", &*format!("{}", theme.bin_fill)), ("stroke", "black"), @@ -44,7 +46,7 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio ); //holes - for hole in bin.holes() { + for hole in &bin.holes { group = group.add( svg_export::data_to_path( svg_export::simple_polygon_data(hole), @@ -63,7 +65,7 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio let mut group = Group::new(); //quality zones - for qz in bin.quality_zones().iter().rev().flatten() { + for qz in bin.quality_zones.iter().rev().flatten() { let color = theme.qz_fill[qz.quality]; let stroke_color = svg_util::change_brightness(color, 0.5); for qz_shape in qz.zones.iter() { @@ -93,8 +95,8 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio for pi in layout.placed_items() { let mut group = Group::new(); let item = instance.item(pi.item_id()); - let shape = pi.shape(); - let color = match item.base_quality() { + let shape = &pi.shape; + let color = match item.base_quality { None => theme.item_fill.to_owned(), Some(q) => { svg_util::blend_colors( @@ -138,7 +140,7 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio ("stroke-linejoin", "round") ]; - let transformed_surrogate = item.shape().surrogate().transform_clone(&pi.d_transformation().compose()); + let transformed_surrogate = item.shape.surrogate().transform_clone(&pi.d_transformation().compose()); let poi = &transformed_surrogate.poles[0]; let ff_poles = transformed_surrogate.ff_poles(); @@ -202,7 +204,8 @@ pub fn layout_to_svg(layout: &Layout, instance: &Instance, options: SvgDrawOptio let haz_prox_grid_group = { let mut group = Group::new(); if options.haz_prox_grid { - for hp_cell in layout.cde().haz_prox_grid().unwrap().cells().iter().flatten() { + let hpg = layout.cde().haz_prox_grid().unwrap(); + for hp_cell in hpg.grid.cells.iter().flatten() { let center = hp_cell.centroid(); let prox = hp_cell.hazard_proximity(None); diff --git a/lbf/src/io/mod.rs b/lbf/src/io/mod.rs index f9f9bde..6d3a2d2 100644 --- a/lbf/src/io/mod.rs +++ b/lbf/src/io/mod.rs @@ -2,9 +2,12 @@ use std::fs; use std::fs::File; use std::io::{BufReader, BufWriter}; use std::path::Path; + use log::{info, Level, LevelFilter, log}; use svg::Document; + use jaguars::io::json_instance::JsonInstance; + use crate::EPOCH; use crate::io::json_output::JsonOutput; diff --git a/lbf/src/io/svg_export.rs b/lbf/src/io/svg_export.rs index 341e978..0d4931e 100644 --- a/lbf/src/io/svg_export.rs +++ b/lbf/src/io/svg_export.rs @@ -1,19 +1,14 @@ use svg::node::element::{Circle, Path}; use svg::node::element::path::Data; -use jaguars::collision_detection::hazard::HazardEntity; +use jaguars::collision_detection::hazard::HazardEntity; use jaguars::collision_detection::quadtree::qt_hazard::QTHazPresence; use jaguars::collision_detection::quadtree::qt_node::QTNode; - - - use jaguars::geometry; use jaguars::geometry::primitives::edge::Edge; - use jaguars::geometry::primitives::point::Point; use jaguars::geometry::primitives::simple_polygon::SimplePolygon; - pub fn simple_polygon_data(s_poly: &SimplePolygon) -> Data { let mut data = Data::new().move_to::<(f64, f64)>(s_poly.get_point(0).into()); for i in 1..s_poly.number_of_points() { @@ -35,10 +30,10 @@ fn qt_node_data( ) -> (Data, Data, Data) { //Only draw qt_nodes that do not have a child - match (qt_node.has_children(), qt_node.hazards().strongest(irrelevant_hazards)) { + match (qt_node.has_children(), qt_node.hazards.strongest(irrelevant_hazards)) { (true, Some(_)) => { //not a leaf node, go to children - for child in qt_node.children().as_ref().unwrap().iter() { + for child in qt_node.children.as_ref().unwrap().iter() { let data = qt_node_data(child, data_eh, data_ph, data_nh, irrelevant_hazards); data_eh = data.0; data_ph = data.1; @@ -47,7 +42,7 @@ fn qt_node_data( } (true, None) | (false, _) => { //leaf node, draw it - let rect = qt_node.bbox(); + let rect = &qt_node.bbox; let draw = |data: Data| -> Data { data .move_to((rect.x_min, rect.y_min)) @@ -57,7 +52,7 @@ fn qt_node_data( .close() }; - match qt_node.hazards().strongest(irrelevant_hazards) { + match qt_node.hazards.strongest(irrelevant_hazards) { Some(ch) => { match ch.presence { QTHazPresence::Entire => data_eh = draw(data_eh), diff --git a/lbf/src/io/svg_util.rs b/lbf/src/io/svg_util.rs index af0af7d..2fb2fd3 100644 --- a/lbf/src/io/svg_util.rs +++ b/lbf/src/io/svg_util.rs @@ -2,7 +2,9 @@ use std::convert::Into; use std::fmt::{Display, Formatter}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use jaguars::entities::quality_zone::N_QUALITIES; + #[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Copy)] pub struct SvgDrawOptions { #[serde(default)] diff --git a/lbf/src/lbf_cost.rs b/lbf/src/lbf_cost.rs index e0e5644..f078f8d 100644 --- a/lbf/src/lbf_cost.rs +++ b/lbf/src/lbf_cost.rs @@ -1,6 +1,8 @@ use std::cmp::Ordering; use std::fmt::{Display, Formatter}; + use ordered_float::NotNan; + use jaguars::geometry::geo_traits::Shape; use jaguars::geometry::primitives::simple_polygon::SimplePolygon; diff --git a/lbf/src/lbf_optimizer.rs b/lbf/src/lbf_optimizer.rs index d9f88dc..bd32a2a 100644 --- a/lbf/src/lbf_optimizer.rs +++ b/lbf/src/lbf_optimizer.rs @@ -1,6 +1,5 @@ use std::cmp::{Ordering, Reverse}; use std::f64::consts::PI; -use std::sync::Arc; use std::time::Instant; use itertools::Itertools; @@ -16,7 +15,7 @@ use jaguars::entities::item::Item; use jaguars::entities::layout::Layout; use jaguars::entities::placing_option::PlacingOption; use jaguars::entities::problems::bin_packing::BPProblem; -use jaguars::entities::problems::problem::{LayoutIndex, ProblemGeneric, Problem}; +use jaguars::entities::problems::problem::{LayoutIndex, Problem, ProblemGeneric}; use jaguars::entities::problems::strip_packing::SPProblem; use jaguars::entities::solution::Solution; use jaguars::geometry::convex_hull::convex_hull_from_points; @@ -69,7 +68,7 @@ impl LBFOptimizer { .sorted_by_cached_key(|i| { let item = &self.instance.items()[*i].0; let ch = SimplePolygon::new( - convex_hull_from_points(item.shape().points().clone()) + convex_hull_from_points(item.shape.points.clone()) ); let ch_diam = NotNan::new(ch.diameter()).expect("convex hull diameter is NaN"); Reverse(ch_diam) @@ -147,13 +146,13 @@ fn find_placement(problem: &Problem, item: &Item, config: &Config, rng: &mut imp } pub fn sample_layout(problem: &Problem, layout_index: LayoutIndex, item: &Item, config: &Config, rng: &mut impl Rng) -> Option { - let item_id = item.id(); + let item_id = item.id; let layout: &Layout = problem.get_layout(&layout_index); - let entities_to_ignore = item.hazard_filter() + let entities_to_ignore = item.hazard_filter.as_ref() .map_or(vec![], |hf| hazard_filter::get_irrelevant_hazard_entities(hf, layout.cde().all_hazards())); - let shape = item.shape(); - let surrogate = item.shape().surrogate(); + let shape = &item.shape; + let surrogate = item.shape.surrogate(); let mut buffer_shape = shape.clone_and_strip_surrogate(); let mut best = None; diff --git a/lbf/src/lib.rs b/lbf/src/lib.rs index 4322d7f..6c626db 100644 --- a/lbf/src/lib.rs +++ b/lbf/src/lib.rs @@ -1,4 +1,5 @@ use std::time::Instant; + use mimalloc::MiMalloc; use once_cell::sync::Lazy; diff --git a/lbf/src/main.rs b/lbf/src/main.rs index a4535dd..7c4dc51 100644 --- a/lbf/src/main.rs +++ b/lbf/src/main.rs @@ -1,37 +1,44 @@ -use std::{fs}; +use std::fs; use std::fs::File; -use std::io::{BufReader}; -use std::path::{Path}; -use std::sync::Arc; +use std::io::BufReader; +use std::path::Path; use clap::Parser as ClapParser; -use log::{info, LevelFilter, warn}; +use log::{error, LevelFilter, warn}; use rand::prelude::SmallRng; use rand::SeedableRng; use jaguars::io::parser; use jaguars::io::parser::Parser; - -use lbf::config::Config; use lbf::{EPOCH, io}; -use lbf::lbf_optimizer::LBFOptimizer; +use lbf::config::Config; use lbf::io::cli::Cli; use lbf::io::json_output::JsonOutput; -use lbf::io::layout_to_svg::{s_layout_to_svg}; +use lbf::io::layout_to_svg::s_layout_to_svg; +use lbf::lbf_optimizer::LBFOptimizer; fn main() { io::init_logger(Some(LevelFilter::Info)); let args = Cli::parse(); - let config_file = File::open(args.config_file).expect("could not open config file"); - let config: Config = serde_json::from_reader(BufReader::new(config_file)).unwrap_or_else(|err| { - warn!("Config file could not be parsed: {}", err); - warn!("Falling back on default config"); - Config::default() - }); - - info!("Config: {}", serde_json::to_string(&config).unwrap()); - + let config = match args.config_file { + None => { + warn!("No config file provided, use --config-file to provide a custom config"); + warn!("Falling back default config:\n{}", serde_json::to_string(&Config::default()).unwrap()); + Config::default() + } + Some(config_file) => { + let file = File::open(config_file).unwrap_or_else(|err| { + panic!("Config file could not be opened: {}", err); + }); + let reader = BufReader::new(file); + serde_json::from_reader(reader).unwrap_or_else(|err| { + error!("Config file could not be parsed: {}", err); + error!("Omit the --config-file argument to use the default config"); + panic!(); + }) + } + }; let json_instance = io::read_json_instance(args.input_file.as_path()); let parser = Parser::new(config.poly_simpl_config, config.cde_config, true); diff --git a/lbf/src/samplers/hpg_sampler.rs b/lbf/src/samplers/hpg_sampler.rs index e9bab7f..5ecdb27 100644 --- a/lbf/src/samplers/hpg_sampler.rs +++ b/lbf/src/samplers/hpg_sampler.rs @@ -1,20 +1,15 @@ use itertools::Itertools; -use log::{debug}; - -use rand::prelude::{SliceRandom}; +use log::debug; +use rand::prelude::SliceRandom; use rand::Rng; use jaguars::entities::item::Item; use jaguars::entities::layout::Layout; - - -use jaguars::geometry::geo_traits::{Shape}; +use jaguars::geometry::geo_traits::Shape; use jaguars::geometry::primitives::aa_rectangle::AARectangle; - - use jaguars::geometry::transformation::Transformation; -use crate::lbf_cost::LBFCost; +use crate::lbf_cost::LBFCost; use crate::samplers::uniform_rect_sampler::UniformAARectSampler; pub struct HPGSampler<'a> { @@ -27,12 +22,12 @@ pub struct HPGSampler<'a> { impl<'a> HPGSampler<'a> { pub fn new(item: &'a Item, layout: &Layout) -> Option> { - let poi = item.shape().poi(); + let poi = &item.shape.poi; let layout_bbox = layout.bin().bbox(); let hpg = layout.cde().haz_prox_grid().expect("grid changes present"); - let hpg_cells = hpg.cells(); + let hpg_cells = &hpg.grid.cells; //center the shape's POI to the origin let pretransform = Transformation::from_translation((-poi.center.0, -poi.center.1)); @@ -74,7 +69,7 @@ impl<'a> HPGSampler<'a> { } pub fn tighten_x_bound(&mut self, best: &LBFCost){ - let poi_rad = self.item.shape().poi().radius; + let poi_rad = self.item.shape.poi.radius; let new_x_bound = *best.x_max - poi_rad; //we need at least one POI radius of space to the left of the best solution if new_x_bound < self.x_bound { diff --git a/lbf/src/samplers/ls_sampler.rs b/lbf/src/samplers/ls_sampler.rs index a1478ae..4575b8f 100644 --- a/lbf/src/samplers/ls_sampler.rs +++ b/lbf/src/samplers/ls_sampler.rs @@ -5,6 +5,7 @@ use rand_distr::Normal; use jaguars::entities::item::Item; use jaguars::geometry::d_transformation::DTransformation; use jaguars::geometry::transformation::Transformation; + use crate::samplers::rotation_distr::NormalRotDistr; pub struct LSSampler { diff --git a/lbf/src/samplers/rotation_distr.rs b/lbf/src/samplers/rotation_distr.rs index e4c00f8..1c74913 100644 --- a/lbf/src/samplers/rotation_distr.rs +++ b/lbf/src/samplers/rotation_distr.rs @@ -1,6 +1,7 @@ use std::f64::consts::PI; + use rand::distributions::Uniform; -use rand::prelude::{Distribution}; +use rand::prelude::Distribution; use rand::Rng; use rand::seq::SliceRandom; use rand_distr::Normal; @@ -26,7 +27,7 @@ pub enum NormalRotDistr { impl UniformRotDistr { pub fn from_item(item: &Item) -> Self { - match item.allowed_rotation() { + match &item.allowed_rotation { AllowedRotation::None => UniformRotDistr::None, AllowedRotation::Continuous => UniformRotDistr::Range(Uniform::new(0.0, 2.0 * PI)), AllowedRotation::Discrete(a_o) => UniformRotDistr::Discrete(a_o.clone()) @@ -47,7 +48,7 @@ impl UniformRotDistr { impl NormalRotDistr { pub fn from_item(item: &Item, r_ref: f64, stddev: f64) -> Self { - match item.allowed_rotation() { + match &item.allowed_rotation { AllowedRotation::None => NormalRotDistr::None, AllowedRotation::Continuous => NormalRotDistr::Range(Normal::new(r_ref, stddev).unwrap()), AllowedRotation::Discrete(_) => NormalRotDistr::Discrete(r_ref) diff --git a/lbf/src/samplers/uniform_rect_sampler.rs b/lbf/src/samplers/uniform_rect_sampler.rs index dba6e6a..426b7ac 100644 --- a/lbf/src/samplers/uniform_rect_sampler.rs +++ b/lbf/src/samplers/uniform_rect_sampler.rs @@ -4,6 +4,7 @@ use rand::Rng; use jaguars::entities::item::Item; use jaguars::geometry::d_transformation::DTransformation; use jaguars::geometry::primitives::aa_rectangle::AARectangle; + use crate::samplers::rotation_distr::UniformRotDistr; pub struct UniformAARectSampler {