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