jagua_rs/entities/
container.rs1use std::sync::Arc;
2
3use itertools::Itertools;
4
5use crate::collision_detection::hazards::BasicHazardEntity;
6use crate::collision_detection::hazards::Hazard;
7use crate::collision_detection::{CDEConfig, CDEngine};
8use crate::geometry::OriginalShape;
9use crate::geometry::fail_fast::SPSurrogateConfig;
10use crate::geometry::primitives::SPolygon;
11
12use anyhow::{Result, ensure};
13
14#[derive(Clone, Debug)]
16pub struct Container {
17 pub(crate) outer_orig: Arc<OriginalShape>,
19 pub(crate) outer_cd: Arc<SPolygon>,
21 pub(crate) quality_zones: [Option<InferiorQualityZone>; N_QUALITIES],
23 pub(crate) base_cde: Arc<CDEngine>,
25}
26
27impl Container {
28 pub fn new(
29 original_outer: OriginalShape,
30 quality_zones: Vec<InferiorQualityZone>,
31 cde_config: CDEConfig,
32 ) -> Result<Self> {
33 let outer = Arc::new(original_outer.convert_to_internal()?);
34 let outer_orig = Arc::new(original_outer);
35 ensure!(
36 quality_zones.len() == quality_zones.iter().map(|qz| qz.quality).unique().count(),
37 "Quality zones must have unique qualities"
38 );
39 ensure!(
40 quality_zones
41 .iter()
42 .map(|qz| qz.quality)
43 .all(|q| q < N_QUALITIES),
44 "All quality zones must be below N_QUALITIES: {N_QUALITIES}"
45 );
46 let quality_zones = {
47 let mut qz = <[_; N_QUALITIES]>::default();
48 for q in quality_zones {
49 let quality = q.quality;
50 qz[quality] = Some(q);
51 }
52 qz
53 };
54
55 let base_cde = {
56 let mut hazards = vec![Hazard::new(BasicHazardEntity::Exterior, outer.clone())];
57 let qz_hazards = quality_zones
58 .iter()
59 .flatten()
60 .flat_map(InferiorQualityZone::to_hazards);
61 hazards.extend(qz_hazards);
62 let base_cde = CDEngine::new(outer.bbox.inflate_to_square(), hazards, cde_config);
63 Arc::new(base_cde)
64 };
65
66 Ok(Self {
67 outer_cd: outer,
68 outer_orig,
69 quality_zones,
70 base_cde,
71 })
72 }
73
74 #[must_use]
76 pub fn area(&self) -> f32 {
77 self.outer_orig.area()
78 }
79
80 #[must_use]
82 pub fn outer_orig(&self) -> &Arc<OriginalShape> {
83 &self.outer_orig
84 }
85
86 #[must_use]
88 pub fn outer_cd(&self) -> &Arc<SPolygon> {
89 &self.outer_cd
90 }
91
92 #[must_use]
94 pub fn quality_zones(&self) -> &[Option<InferiorQualityZone>; N_QUALITIES] {
95 &self.quality_zones
96 }
97
98 #[must_use]
100 pub fn base_cde(&self) -> &Arc<CDEngine> {
101 &self.base_cde
102 }
103}
104
105pub const N_QUALITIES: usize = 10;
107
108#[derive(Clone, Debug)]
110pub struct InferiorQualityZone {
111 pub(crate) quality: usize,
113 pub(crate) shapes_orig: Vec<Arc<OriginalShape>>,
115 pub(crate) shapes_cd: Vec<Arc<SPolygon>>,
117}
118
119impl InferiorQualityZone {
120 pub fn new(
124 quality: usize,
125 original_shapes: Vec<OriginalShape>,
126 surrogate_config: SPSurrogateConfig,
127 ) -> Result<Self> {
128 ensure!(
129 quality < N_QUALITIES,
130 "Quality must be in range of N_QUALITIES"
131 );
132 let shapes: Result<Vec<Arc<SPolygon>>> = original_shapes
133 .iter()
134 .map(|orig| {
135 let mut shape = orig.convert_to_internal()?;
136 shape.generate_surrogate(surrogate_config)?;
137 Ok(Arc::new(shape))
138 })
139 .collect();
140
141 let original_shapes = original_shapes.into_iter().map(Arc::new).collect_vec();
142
143 Ok(Self {
144 quality,
145 shapes_cd: shapes?,
146 shapes_orig: original_shapes,
147 })
148 }
149
150 #[must_use]
153 pub fn filtered(&self, mut f: impl FnMut(&SPolygon) -> bool) -> Self {
154 let (shapes_orig, shapes_cd) = self
155 .shapes_orig
156 .iter()
157 .zip(&self.shapes_cd)
158 .filter(|(_, shape)| f(shape))
159 .map(|(orig, cd)| (orig.clone(), cd.clone()))
160 .unzip();
161 Self {
162 quality: self.quality,
163 shapes_orig,
164 shapes_cd,
165 }
166 }
167
168 pub fn to_hazards(&self) -> impl Iterator<Item = Hazard> {
170 self.shapes_cd.iter().enumerate().map(|(idx, shape)| {
171 let entity = match self.quality {
172 0 => BasicHazardEntity::Hole { idx },
173 _ => BasicHazardEntity::InferiorQualityZone {
174 quality: self.quality,
175 idx,
176 },
177 };
178 Hazard::new(entity, shape.clone())
179 })
180 }
181
182 #[must_use]
183 pub fn area(&self) -> f32 {
184 self.shapes_orig.iter().map(|shape| shape.area()).sum()
185 }
186
187 #[must_use]
189 pub fn quality(&self) -> usize {
190 self.quality
191 }
192
193 #[must_use]
195 pub fn shapes_orig(&self) -> &[Arc<OriginalShape>] {
196 &self.shapes_orig
197 }
198
199 #[must_use]
201 pub fn shapes_cd(&self) -> &[Arc<SPolygon>] {
202 &self.shapes_cd
203 }
204}