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 itertools::Itertools;
14use log::warn;
15
16#[derive(Clone, Debug, Copy)]
18pub struct Importer {
19 pub shape_modify_config: ShapeModifyConfig,
20 pub cde_config: CDEConfig,
21}
22
23impl Importer {
24 pub fn new(
30 cde_config: CDEConfig,
31 poly_simpl_tolerance: Option<f32>,
32 min_item_separation: Option<f32>,
33 ) -> Importer {
34 Importer {
35 shape_modify_config: ShapeModifyConfig {
36 offset: min_item_separation.map(|f| f / 2.0),
37 simplify_tolerance: poly_simpl_tolerance,
38 },
39 cde_config,
40 }
41 }
42
43 pub fn import_item(&self, ext_item: &ExtItem) -> Result<Item> {
44 let original_shape = {
45 let shape = match &ext_item.shape {
46 ExtShape::Rectangle {
47 x_min,
48 y_min,
49 width,
50 height,
51 } => {
52 let rect = Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)?;
53 SPolygon::from(rect)
54 }
55 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp)?,
56 ExtShape::Polygon(ep) => {
57 warn!("No native support for polygons yet, ignoring the holes");
58 import_simple_polygon(&ep.outer)?
59 }
60 ExtShape::MultiPolygon(_) => {
61 bail!("No support for multipolygons yet")
62 }
63 };
64 OriginalShape {
65 pre_transform: centering_transformation(&shape),
66 shape,
67 modify_mode: ShapeModifyMode::Inflate,
68 modify_config: self.shape_modify_config,
69 }
70 };
71
72 let base_quality = ext_item.min_quality;
73
74 let allowed_orientations = match ext_item.allowed_orientations.as_ref() {
75 Some(a_o) => {
76 if a_o.is_empty() || (a_o.len() == 1 && a_o[0] == 0.0) {
77 RotationRange::None
78 } else {
79 RotationRange::Discrete(a_o.iter().map(|angle| angle.to_radians()).collect())
80 }
81 }
82 None => RotationRange::Continuous,
83 };
84
85 Item::new(
86 ext_item.id as usize,
87 original_shape,
88 allowed_orientations,
89 base_quality,
90 self.cde_config.item_surrogate_config,
91 )
92 }
93
94 pub fn import_container(&self, ext_cont: &ExtContainer) -> Result<Container> {
95 assert!(
96 ext_cont.zones.iter().all(|zone| zone.quality < N_QUALITIES),
97 "All quality zones must have lower quality than N_QUALITIES, set N_QUALITIES to a higher value if required"
98 );
99
100 let original_outer = {
101 let outer = match &ext_cont.shape {
102 ExtShape::Rectangle {
103 x_min,
104 y_min,
105 width,
106 height,
107 } => Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)?.into(),
108 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp)?,
109 ExtShape::Polygon(ep) => import_simple_polygon(&ep.outer)?,
110 ExtShape::MultiPolygon(_) => {
111 bail!("No support for multipolygon shapes yet")
112 }
113 };
114 OriginalShape {
115 shape: outer,
116 pre_transform: DTransformation::empty(),
117 modify_mode: ShapeModifyMode::Deflate,
118 modify_config: self.shape_modify_config,
119 }
120 };
121
122 let holes = match &ext_cont.shape {
123 ExtShape::SimplePolygon(_) | ExtShape::Rectangle { .. } => vec![],
124 ExtShape::Polygon(jp) => {
125 let json_holes = &jp.inner;
126 json_holes
127 .iter()
128 .map(import_simple_polygon)
129 .collect::<Result<Vec<SPolygon>>>()?
130 }
131 ExtShape::MultiPolygon(_) => {
132 unimplemented!("No support for multipolygon shapes yet")
133 }
134 };
135
136 let mut shapes_inferior_qzones = (0..N_QUALITIES)
137 .map(|q| {
138 ext_cont
139 .zones
140 .iter()
141 .filter(|zone| zone.quality == q)
142 .map(|zone| match &zone.shape {
143 ExtShape::Rectangle {
144 x_min,
145 y_min,
146 width,
147 height,
148 } => Rect::try_new(*x_min, *y_min, x_min + width, y_min + height)
149 .map(|r| r.into()),
150 ExtShape::SimplePolygon(esp) => import_simple_polygon(esp),
151 ExtShape::Polygon(_) => {
152 unimplemented!("No support for polygon to simplepolygon conversion yet")
153 }
154 ExtShape::MultiPolygon(_) => {
155 unimplemented!("No support for multipolygon shapes yet")
156 }
157 })
158 .collect::<Result<Vec<SPolygon>>>()
159 })
160 .collect::<Result<Vec<Vec<SPolygon>>>>()?;
161
162 shapes_inferior_qzones[0].extend(holes);
164
165 let quality_zones = shapes_inferior_qzones
167 .into_iter()
168 .enumerate()
169 .map(|(q, zone_shapes)| {
170 let original_shapes = zone_shapes
171 .into_iter()
172 .map(|s| OriginalShape {
173 shape: s,
174 pre_transform: DTransformation::empty(),
175 modify_mode: ShapeModifyMode::Inflate,
176 modify_config: self.shape_modify_config,
177 })
178 .collect_vec();
179 InferiorQualityZone::new(q, original_shapes)
180 })
181 .collect::<Result<Vec<InferiorQualityZone>>>()?;
182
183 Container::new(
184 ext_cont.id as usize,
185 original_outer,
186 quality_zones,
187 self.cde_config,
188 )
189 }
190}
191
192pub fn import_simple_polygon(sp: &ExtSPolygon) -> Result<SPolygon> {
193 let mut points = sp.0.iter().map(|(x, y)| Point(*x, *y)).collect_vec();
194 if points.len() > 1 && points[0] == points[points.len() - 1] {
196 points.pop();
197 }
198 points.dedup();
200 if points.len() != points.iter().unique().count() {
202 bail!("Simple polygon has non-consecutive duplicate vertices");
203 }
204 SPolygon::new(points)
205}
206
207pub fn centering_transformation(shape: &SPolygon) -> DTransformation {
209 let Point(cx, cy) = shape.centroid();
210 DTransformation::new(0.0, (-cx, -cy))
211}
212
213pub fn ext_to_int_transformation(
218 ext_transf: &DTransformation,
219 pre_transf: &DTransformation,
220) -> DTransformation {
221 Transformation::empty()
225 .transform(&pre_transf.compose().inverse())
226 .transform_from_decomposed(ext_transf)
227 .decompose()
228}