1use crate::collision_detection::CDEConfig;
2use crate::entities::Item;
3use crate::entities::{Container, InferiorQualityZone, N_QUALITIES};
4use crate::geometry::OriginalShape;
5use crate::geometry::geo_enums::RotationRange;
6use crate::geometry::primitives::Point;
7use crate::geometry::primitives::Rect;
8use crate::geometry::primitives::SPolygon;
9use crate::geometry::shape_modification::{ShapeModifyConfig, ShapeModifyMode};
10use crate::geometry::{DTransformation, Transformation};
11use crate::io::ext_repr::{ExtContainer, ExtItem, ExtSPolygon, ExtShape};
12use anyhow::{Result, bail};
13use float_cmp::approx_eq;
14use itertools::Itertools;
15use log::{debug, warn};
16
17#[derive(Clone, Debug, Copy)]
19pub struct Importer {
20 pub shape_modify_config: ShapeModifyConfig,
21 pub cde_config: CDEConfig,
22}
23
24impl Importer {
25 pub fn new(
32 cde_config: CDEConfig,
33 simplify_tolerance: Option<f32>,
34 min_item_separation: Option<f32>,
35 narrow_concavity_cutoff_ratio: Option<f32>,
36 ) -> Importer {
37 Importer {
38 shape_modify_config: ShapeModifyConfig {
39 offset: min_item_separation.map(|f| f / 2.0),
40 simplify_tolerance,
41 narrow_concavity_cutoff_ratio,
42 },
43 cde_config,
44 }
45 }
46
47 pub fn import_item(&self, ext_item: &ExtItem) -> Result<Item> {
48 debug!("[IMPORT] starting item {:?}", ext_item.id);
49
50 let original_shape = {
51 let shape = match &ext_item.shape {
52 ExtShape::Rectangle {
53 x_min,
54 y_min,
55 width,
56 height,
57 } => {
58 let rect = Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)?;
59 SPolygon::from(rect)
60 }
61 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp)?,
62 ExtShape::Polygon(ep) => {
63 warn!("No native support for polygons yet, ignoring the holes");
64 import_simple_polygon(&ep.outer)?
65 }
66 ExtShape::MultiPolygon(_) => {
67 bail!("No support for multipolygons yet")
68 }
69 };
70 OriginalShape {
71 pre_transform: centering_transformation(&shape),
72 shape,
73 modify_mode: ShapeModifyMode::Inflate,
74 modify_config: self.shape_modify_config,
75 }
76 };
77
78 let base_quality = ext_item.min_quality;
79
80 let allowed_orientations = match ext_item.allowed_orientations.as_ref() {
81 Some(a_o) => {
82 if a_o.is_empty() || (a_o.len() == 1 && a_o[0] == 0.0) {
83 RotationRange::None
84 } else {
85 RotationRange::Discrete(a_o.iter().map(|angle| angle.to_radians()).collect())
86 }
87 }
88 None => RotationRange::Continuous,
89 };
90
91 Item::new(
92 ext_item.id as usize,
93 original_shape,
94 allowed_orientations,
95 base_quality,
96 self.cde_config.item_surrogate_config,
97 )
98 }
99
100 pub fn import_container(&self, ext_cont: &ExtContainer) -> Result<Container> {
101 assert!(
102 ext_cont.zones.iter().all(|zone| zone.quality < N_QUALITIES),
103 "All quality zones must have lower quality than N_QUALITIES, set N_QUALITIES to a higher value if required"
104 );
105
106 let original_outer = {
107 let outer = match &ext_cont.shape {
108 ExtShape::Rectangle {
109 x_min,
110 y_min,
111 width,
112 height,
113 } => Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)?.into(),
114 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp)?,
115 ExtShape::Polygon(ep) => import_simple_polygon(&ep.outer)?,
116 ExtShape::MultiPolygon(_) => {
117 bail!("No support for multipolygon shapes yet")
118 }
119 };
120 OriginalShape {
121 shape: outer,
122 pre_transform: DTransformation::empty(),
123 modify_mode: ShapeModifyMode::Deflate,
124 modify_config: self.shape_modify_config,
125 }
126 };
127
128 let holes = match &ext_cont.shape {
129 ExtShape::SimplePolygon(_) | ExtShape::Rectangle { .. } => vec![],
130 ExtShape::Polygon(jp) => {
131 let json_holes = &jp.inner;
132 json_holes
133 .iter()
134 .map(import_simple_polygon)
135 .collect::<Result<Vec<SPolygon>>>()?
136 }
137 ExtShape::MultiPolygon(_) => {
138 unimplemented!("No support for multipolygon shapes yet")
139 }
140 };
141
142 let mut shapes_inferior_qzones = (0..N_QUALITIES)
143 .map(|q| {
144 ext_cont
145 .zones
146 .iter()
147 .filter(|zone| zone.quality == q)
148 .map(|zone| match &zone.shape {
149 ExtShape::Rectangle {
150 x_min,
151 y_min,
152 width,
153 height,
154 } => Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)
155 .map(|r| r.into()),
156 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp),
157 ExtShape::Polygon(_) => {
158 unimplemented!("No support for polygon to simplepolygon conversion yet")
159 }
160 ExtShape::MultiPolygon(_) => {
161 unimplemented!("No support for multipolygon shapes yet")
162 }
163 })
164 .collect::<Result<Vec<SPolygon>>>()
165 })
166 .collect::<Result<Vec<Vec<SPolygon>>>>()?;
167
168 shapes_inferior_qzones[0].extend(holes);
170
171 let quality_zones = shapes_inferior_qzones
173 .into_iter()
174 .enumerate()
175 .map(|(q, zone_shapes)| {
176 let original_shapes = zone_shapes
177 .into_iter()
178 .map(|s| OriginalShape {
179 shape: s,
180 pre_transform: DTransformation::empty(),
181 modify_mode: ShapeModifyMode::Inflate,
182 modify_config: self.shape_modify_config,
183 })
184 .collect_vec();
185 InferiorQualityZone::new(q, original_shapes)
186 })
187 .collect::<Result<Vec<InferiorQualityZone>>>()?;
188
189 Container::new(
190 ext_cont.id as usize,
191 original_outer,
192 quality_zones,
193 self.cde_config,
194 )
195 }
196}
197
198pub fn import_simple_polygon(sp: &ExtSPolygon) -> Result<SPolygon> {
199 let mut points = sp.0.iter().map(|(x, y)| Point(*x, *y)).collect_vec();
200 if points.len() > 1 && points[0] == points[points.len() - 1] {
202 points.pop();
203 }
204 eliminate_degenerate_vertices(&mut points);
206 if points.len() != points.iter().unique().count() {
208 bail!("Simple polygon has non-consecutive duplicate vertices");
209 }
210 SPolygon::new(points)
211}
212
213pub fn centering_transformation(shape: &SPolygon) -> DTransformation {
215 let Point(cx, cy) = shape.centroid();
216 DTransformation::new(0.0, (-cx, -cy))
217}
218
219pub fn ext_to_int_transformation(
224 ext_transf: &DTransformation,
225 pre_transf: &DTransformation,
226) -> DTransformation {
227 Transformation::empty()
231 .transform(&pre_transf.compose().inverse())
232 .transform_from_decomposed(ext_transf)
233 .decompose()
234}
235
236pub fn eliminate_degenerate_vertices(points: &mut Vec<Point>) {
237 let mut indices_to_remove = vec![];
238 let n_points = points.len();
239 for i in 0..n_points {
240 let j = (i + 1) % n_points;
241 let p_i = points[i];
242 let p_j = points[j];
243 if approx_eq!(f32, p_i.0, p_j.0) && approx_eq!(f32, p_i.1, p_j.1) {
244 indices_to_remove.push(i);
246 }
247 }
248 indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
250 for index in indices_to_remove {
251 if index < points.len() {
252 let j = (index + 1) % points.len();
253 debug!(
254 "[IMPORT] degenerate vertex eliminated (idx: {}, {:?}, {:?})",
255 index, points[index], points[j]
256 );
257 points.remove(index);
258 }
259 }
260}