Skip to main content

binpack_3d/
aabb.rs

1use hashbrown::HashMap;
2use itertools::iproduct;
3use serde::{
4    Deserialize,
5    Serialize,
6};
7
8use crate::{
9    corners::Corners,
10    items::Item,
11    vector::Vector6,
12};
13
14/// A collision checker
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct AABBVersion1 {
17    grid: HashMap<(u32, u32, u32), Vec<Vector6<u32>>>,
18    cell_size: u32,
19}
20impl Default for AABBVersion1 {
21    fn default() -> Self {
22        Self {
23            grid: Default::default(),
24            cell_size: 10,
25        }
26    }
27}
28/// Gives a item which can be placed into the grid
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct AABBVersion1CheckedItem(pub Item);
31
32impl AABBVersion1 {
33    /// Creates a new AABB Checker
34    #[must_use]
35    pub fn new(length: usize) -> Self {
36        Self {
37            grid: HashMap::with_capacity(length),
38            cell_size: 8,
39        }
40    }
41    /// Add a new value
42    pub fn add(&mut self, item: AABBVersion1CheckedItem, corner: &Corners) -> anyhow::Result<()> {
43        let item = item.0;
44        let mut position_minimum = corner.position;
45        let x_position_minimum = position_minimum;
46        let mut position_maximum = position_minimum + item.size_cube;
47        let x_position_maximum = position_maximum;
48        position_minimum.divide_all(self.cell_size, 1);
49        position_maximum.divide_all(self.cell_size, 1);
50        let aabb = Vector6::new(
51            x_position_minimum.x,
52            x_position_minimum.y,
53            x_position_minimum.z,
54            x_position_maximum.x,
55            x_position_maximum.y,
56            x_position_maximum.z,
57        );
58        let end_x = position_maximum.x;
59        let end_y = position_maximum.y;
60        let end_z = position_maximum.z;
61        for x in position_minimum.x..=end_x {
62            for y in position_minimum.y..=end_y {
63                for z in position_minimum.z..=end_z {
64                    self.grid
65                        .entry((x, y, z))
66                        .or_insert_with(Vec::new)
67                        .push(aabb);
68                }
69            }
70        }
71        Ok(())
72    }
73    /// Version 2 of add
74    pub fn add_v2(
75        &mut self,
76        item: AABBVersion1CheckedItem,
77        corner: &Corners,
78    ) -> anyhow::Result<()> {
79        let item = item.0;
80        let mut position_minimum = corner.position;
81        let x_position_minimum = position_minimum;
82        let mut position_maximum = position_minimum + item.size_cube;
83        let x_position_maximum = position_maximum;
84        position_minimum.divide_all(self.cell_size, 1);
85        position_maximum.divide_all(self.cell_size, 1);
86        let aabb = Vector6::new(
87            x_position_minimum.x,
88            x_position_minimum.y,
89            x_position_minimum.z,
90            x_position_maximum.x,
91            x_position_maximum.y,
92            x_position_maximum.z,
93        );
94        let end_x = position_maximum.x;
95        let end_y = position_maximum.y;
96        let end_z = position_maximum.z;
97        iproduct!(
98            // Check later if = should be here
99            position_minimum.x..=end_x,
100            position_minimum.y..=end_y,
101            position_minimum.z..=end_z
102        )
103        .for_each(|(x, y, z)| {
104            self.grid
105                .entry((x, y, z))
106                .or_insert_with(Vec::new)
107                .push(aabb);
108        });
109        Ok(())
110    }
111    /// Check if a item does colliot or not
112    #[must_use]
113    pub fn check_item(&self, item: Item, corner: &Corners) -> Option<AABBVersion1CheckedItem> {
114        let mut position_minimum = corner.position;
115        let mut position_maximum = position_minimum + item.size_cube;
116        let x_position_minimum = position_minimum;
117        let x_position_maximum = position_maximum;
118        position_minimum.divide_all(self.cell_size, 1);
119        position_maximum.divide_all(self.cell_size, 1);
120        let end_x = position_maximum.x;
121        let end_y = position_maximum.y;
122        let end_z = position_maximum.z;
123        for x in position_minimum.x..=end_x {
124            for y in position_minimum.y..=end_y {
125                for z in position_minimum.z..=end_z {
126                    if let Some(existing) = self.grid.get(&(x, y, z)) {
127                        for existing_vector6 in existing {
128                            let overlay_x = x_position_minimum.x < existing_vector6.w
129                                && existing_vector6.x < x_position_maximum.x;
130                            let overlay_y = x_position_minimum.y < existing_vector6.a
131                                && existing_vector6.y < x_position_maximum.y;
132                            let overlay_z = x_position_minimum.z < existing_vector6.b
133                                && existing_vector6.z < x_position_maximum.z;
134                            if overlay_x && overlay_y && overlay_z {
135                                return None;
136                            }
137                        }
138                    }
139                }
140            }
141        }
142        Some(AABBVersion1CheckedItem(item))
143    }
144
145    /// Check v2
146    #[must_use]
147    pub fn check_item_v2(&self, item: Item, corner: &Corners) -> Option<AABBVersion1CheckedItem> {
148        let mut position_minimum = corner.position;
149        let mut position_maximum = position_minimum + item.size_cube;
150        let x_position_minimum = position_minimum;
151        let x_position_maximum = position_maximum;
152        position_minimum.divide_all(self.cell_size, 1);
153        position_maximum.divide_all(self.cell_size, 1);
154        let end_x = position_maximum.x;
155        let end_y = position_maximum.y;
156        let end_z = position_maximum.z;
157        let result = iproduct!(
158            position_minimum.x..=end_x,
159            position_minimum.y..=end_y,
160            position_minimum.z..=end_z
161        )
162        .any(|(x, y, z)| {
163            self.grid
164                .get(&(x, y, z))
165                .map(|existing| {
166                    existing.iter().any(|existing_vector6| {
167                        let overlay_x = x_position_minimum.x < existing_vector6.w
168                            && existing_vector6.x < x_position_maximum.x;
169                        let overlay_y = x_position_minimum.y < existing_vector6.a
170                            && existing_vector6.y < x_position_maximum.y;
171                        let overlay_z = x_position_minimum.z < existing_vector6.b
172                            && existing_vector6.z < x_position_maximum.z;
173                        overlay_x && overlay_y && overlay_z
174                    })
175                })
176                .unwrap_or_else(|| false)
177        });
178        // only false no collision
179        if !result {
180            Some(AABBVersion1CheckedItem(item))
181        } else {
182            None
183        }
184    }
185    /// checks if a point is valid
186    #[must_use]
187    pub fn point_is_free(&self, p: &Corners) -> bool {
188        let point = p.clone().position;
189        let mut cell = p.position;
190        cell.divide_all(self.cell_size, 1);
191
192        if let Some(existing) = self.grid.get(&(cell.x, cell.y, cell.z)) {
193            for e in existing {
194                let inside = point.x >= e.x
195                    && point.x < e.w
196                    && point.y >= e.y
197                    && point.y < e.a
198                    && point.z >= e.z
199                    && point.z < e.b;
200                if inside {
201                    return false;
202                }
203            }
204        }
205        true
206    }
207}
208#[cfg(test)]
209mod tests {
210    use crate::{
211        aabb::AABBVersion1,
212        corners::Corners,
213        items::Item,
214        vector::Vector3,
215    };
216
217    #[test]
218    fn check_no_collision_and_collision_same_item() {
219        let mut aabb = AABBVersion1::new(10 as usize);
220        let item = Item::new(1, Vector3::new(10, 10, 10), 10, 0);
221        // check with no item
222
223        let test = aabb.check_item_v2(item.clone(), &Corners::new(0, 0, 0));
224        assert!(test.is_some());
225        let test = test.unwrap();
226        assert!(aabb.add(test, &Corners::new(0, 0, 0)).is_ok());
227
228        let test = aabb.check_item(item.clone(), &Corners::new(0, 0, 0));
229        assert!(test.is_none());
230    }
231}