jagua_rs/entities/
container.rs1use std::sync::Arc;
2
3use itertools::Itertools;
4
5use crate::collision_detection::hazards::Hazard;
6use crate::collision_detection::hazards::HazardEntity;
7use crate::collision_detection::{CDEConfig, CDEngine};
8use crate::geometry::OriginalShape;
9use crate::geometry::primitives::SPolygon;
10
11use anyhow::{Result, ensure};
12
13#[derive(Clone, Debug)]
15pub struct Container {
16 pub id: usize,
17 pub outer_orig: Arc<OriginalShape>,
19 pub outer_cd: Arc<SPolygon>,
21 pub quality_zones: [Option<InferiorQualityZone>; N_QUALITIES],
23 pub base_cde: Arc<CDEngine>,
25}
26
27impl Container {
28 pub fn new(
29 id: usize,
30 original_outer: OriginalShape,
31 quality_zones: Vec<InferiorQualityZone>,
32 cde_config: CDEConfig,
33 ) -> Result<Self> {
34 let outer = Arc::new(original_outer.convert_to_internal()?);
35 let outer_orig = Arc::new(original_outer);
36 ensure!(
37 quality_zones.len() == quality_zones.iter().map(|qz| qz.quality).unique().count(),
38 "Quality zones must have unique qualities"
39 );
40 ensure!(
41 quality_zones
42 .iter()
43 .map(|qz| qz.quality)
44 .all(|q| q < N_QUALITIES),
45 "All quality zones must be below N_QUALITIES: {N_QUALITIES}"
46 );
47 let quality_zones = {
48 let mut qz = <[_; N_QUALITIES]>::default();
49 for q in quality_zones {
50 let quality = q.quality;
51 qz[quality] = Some(q);
52 }
53 qz
54 };
55
56 let base_cde = {
57 let mut hazards = vec![Hazard::new(HazardEntity::Exterior, outer.clone())];
58 let qz_hazards = quality_zones
59 .iter()
60 .flatten()
61 .flat_map(|qz| qz.to_hazards());
62 hazards.extend(qz_hazards);
63 let base_cde = CDEngine::new(outer.bbox.inflate_to_square(), hazards, cde_config);
64 Arc::new(base_cde)
65 };
66
67 Ok(Self {
68 id,
69 outer_cd: outer,
70 outer_orig,
71 quality_zones,
72 base_cde,
73 })
74 }
75
76 pub fn area(&self) -> f32 {
78 self.outer_orig.area() - self.quality_zones[0].as_ref().map_or(0.0, |qz| qz.area())
79 }
80}
81
82pub const N_QUALITIES: usize = 10;
84
85#[derive(Clone, Debug)]
87pub struct InferiorQualityZone {
88 pub quality: usize,
90 pub shapes_orig: Vec<Arc<OriginalShape>>,
92 pub shapes_cd: Vec<Arc<SPolygon>>,
94}
95
96impl InferiorQualityZone {
97 pub fn new(quality: usize, original_shapes: Vec<OriginalShape>) -> Result<Self> {
98 assert!(
99 quality < N_QUALITIES,
100 "Quality must be in range of N_QUALITIES"
101 );
102 let shapes: Result<Vec<Arc<SPolygon>>> = original_shapes
103 .iter()
104 .map(|orig| orig.convert_to_internal().map(Arc::new))
105 .collect();
106
107 let original_shapes = original_shapes.into_iter().map(Arc::new).collect_vec();
108
109 Ok(Self {
110 quality,
111 shapes_cd: shapes?,
112 shapes_orig: original_shapes,
113 })
114 }
115
116 pub fn to_hazards(&self) -> impl Iterator<Item = Hazard> {
118 self.shapes_cd.iter().enumerate().map(|(idx, shape)| {
119 let entity = match self.quality {
120 0 => HazardEntity::Hole { idx },
121 _ => HazardEntity::InferiorQualityZone {
122 quality: self.quality,
123 idx,
124 },
125 };
126 Hazard::new(entity, shape.clone())
127 })
128 }
129
130 pub fn area(&self) -> f32 {
131 self.shapes_orig.iter().map(|shape| shape.area()).sum()
132 }
133}