session_rust/
boundingbox.rs

1use crate::{Plane, Point, Vector, Xform};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type", rename = "BoundingBox")]
7pub struct BoundingBox {
8    pub center: Point,
9    pub x_axis: Vector,
10    pub y_axis: Vector,
11    pub z_axis: Vector,
12    pub half_size: Vector,
13    pub guid: String,
14    pub name: String,
15    #[serde(default = "Xform::identity")]
16    pub xform: Xform,
17}
18
19impl BoundingBox {
20    pub fn new(
21        center: Point,
22        x_axis: Vector,
23        y_axis: Vector,
24        z_axis: Vector,
25        half_size: Vector,
26    ) -> Self {
27        BoundingBox {
28            center,
29            x_axis,
30            y_axis,
31            z_axis,
32            half_size,
33            guid: Uuid::new_v4().to_string(),
34            name: "my_boundingbox".to_string(),
35            xform: Xform::identity(),
36        }
37    }
38
39    pub fn from_plane(plane: &Plane, dx: f32, dy: f32, dz: f32) -> Self {
40        BoundingBox {
41            center: plane.origin(),
42            x_axis: plane.x_axis(),
43            y_axis: plane.y_axis(),
44            z_axis: plane.z_axis(),
45            half_size: Vector::new(dx * 0.5, dy * 0.5, dz * 0.5),
46            guid: Uuid::new_v4().to_string(),
47            name: String::new(),
48            xform: Xform::identity(),
49        }
50    }
51
52    pub fn from_point(point: Point, inflate: f32) -> Self {
53        BoundingBox {
54            center: point,
55            x_axis: Vector::new(1.0, 0.0, 0.0),
56            y_axis: Vector::new(0.0, 1.0, 0.0),
57            z_axis: Vector::new(0.0, 0.0, 1.0),
58            half_size: Vector::new(inflate, inflate, inflate),
59            guid: Uuid::new_v4().to_string(),
60            xform: Xform::identity(),
61            name: String::new(),
62        }
63    }
64
65    pub fn from_points(points: &[Point], inflate: f32) -> Self {
66        if points.is_empty() {
67            return BoundingBox::default();
68        }
69
70        let mut min_x = f32::MAX;
71        let mut min_y = f32::MAX;
72        let mut min_z = f32::MAX;
73        let mut max_x = f32::MIN;
74        let mut max_y = f32::MIN;
75        let mut max_z = f32::MIN;
76
77        for pt in points {
78            min_x = min_x.min(pt.x());
79            min_y = min_y.min(pt.y());
80            min_z = min_z.min(pt.z());
81            max_x = max_x.max(pt.x());
82            max_y = max_y.max(pt.y());
83            max_z = max_z.max(pt.z());
84        }
85
86        let center = Point::new(
87            (min_x + max_x) * 0.5,
88            (min_y + max_y) * 0.5,
89            (min_z + max_z) * 0.5,
90        );
91        let half_size = Vector::new(
92            (max_x - min_x) * 0.5 + inflate,
93            (max_y - min_y) * 0.5 + inflate,
94            (max_z - min_z) * 0.5 + inflate,
95        );
96
97        BoundingBox {
98            center,
99            x_axis: Vector::new(1.0, 0.0, 0.0),
100            y_axis: Vector::new(0.0, 1.0, 0.0),
101            z_axis: Vector::new(0.0, 0.0, 1.0),
102            half_size,
103            guid: Uuid::new_v4().to_string(),
104            name: String::new(),
105            xform: Xform::identity(),
106        }
107    }
108
109    pub fn from_line(line: &crate::line::Line, inflate: f32) -> Self {
110        let points = vec![line.start(), line.end()];
111        Self::from_points(&points, inflate)
112    }
113
114    pub fn from_polyline(polyline: &crate::polyline::Polyline, inflate: f32) -> Self {
115        Self::from_points(&polyline.points, inflate)
116    }
117
118    pub fn point_at(&self, x: f32, y: f32, z: f32) -> Point {
119        Point::new(
120            self.center.x() + x * self.x_axis.x() + y * self.y_axis.x() + z * self.z_axis.x(),
121            self.center.y() + x * self.x_axis.y() + y * self.y_axis.y() + z * self.z_axis.y(),
122            self.center.z() + x * self.x_axis.z() + y * self.y_axis.z() + z * self.z_axis.z(),
123        )
124    }
125
126    pub fn corners(&self) -> [Point; 8] {
127        [
128            self.point_at(self.half_size.x(), self.half_size.y(), -self.half_size.z()),
129            self.point_at(-self.half_size.x(), self.half_size.y(), -self.half_size.z()),
130            self.point_at(
131                -self.half_size.x(),
132                -self.half_size.y(),
133                -self.half_size.z(),
134            ),
135            self.point_at(self.half_size.x(), -self.half_size.y(), -self.half_size.z()),
136            self.point_at(self.half_size.x(), self.half_size.y(), self.half_size.z()),
137            self.point_at(-self.half_size.x(), self.half_size.y(), self.half_size.z()),
138            self.point_at(-self.half_size.x(), -self.half_size.y(), self.half_size.z()),
139            self.point_at(self.half_size.x(), -self.half_size.y(), self.half_size.z()),
140        ]
141    }
142
143    pub fn two_rectangles(&self) -> [Point; 10] {
144        [
145            self.point_at(self.half_size.x(), self.half_size.y(), -self.half_size.z()),
146            self.point_at(-self.half_size.x(), self.half_size.y(), -self.half_size.z()),
147            self.point_at(
148                -self.half_size.x(),
149                -self.half_size.y(),
150                -self.half_size.z(),
151            ),
152            self.point_at(self.half_size.x(), -self.half_size.y(), -self.half_size.z()),
153            self.point_at(self.half_size.x(), self.half_size.y(), -self.half_size.z()),
154            self.point_at(self.half_size.x(), self.half_size.y(), self.half_size.z()),
155            self.point_at(-self.half_size.x(), self.half_size.y(), self.half_size.z()),
156            self.point_at(-self.half_size.x(), -self.half_size.y(), self.half_size.z()),
157            self.point_at(self.half_size.x(), -self.half_size.y(), self.half_size.z()),
158            self.point_at(self.half_size.x(), self.half_size.y(), self.half_size.z()),
159        ]
160    }
161
162    pub fn inflate(&mut self, amount: f32) {
163        self.half_size = Vector::new(
164            self.half_size.x() + amount,
165            self.half_size.y() + amount,
166            self.half_size.z() + amount,
167        );
168    }
169
170    fn separating_plane_exists(
171        relative_position: &Vector,
172        axis: &Vector,
173        box1: &BoundingBox,
174        box2: &BoundingBox,
175    ) -> bool {
176        let dot_rp = relative_position.dot(axis).abs();
177
178        let v1 = box1.x_axis.clone() * box1.half_size.x();
179        let v2 = box1.y_axis.clone() * box1.half_size.y();
180        let v3 = box1.z_axis.clone() * box1.half_size.z();
181        let proj1 = v1.dot(axis).abs() + v2.dot(axis).abs() + v3.dot(axis).abs();
182
183        let v4 = box2.x_axis.clone() * box2.half_size.x();
184        let v5 = box2.y_axis.clone() * box2.half_size.y();
185        let v6 = box2.z_axis.clone() * box2.half_size.z();
186        let proj2 = v4.dot(axis).abs() + v5.dot(axis).abs() + v6.dot(axis).abs();
187
188        dot_rp > (proj1 + proj2)
189    }
190
191    pub fn collides_with(&self, other: &BoundingBox) -> bool {
192        let center_vec = Vector::new(self.center.x(), self.center.y(), self.center.z());
193        let other_center_vec = Vector::new(other.center.x(), other.center.y(), other.center.z());
194        let relative_position = Vector::from_start_and_end(&center_vec, &other_center_vec);
195
196        !(Self::separating_plane_exists(&relative_position, &self.x_axis, self, other)
197            || Self::separating_plane_exists(&relative_position, &self.y_axis, self, other)
198            || Self::separating_plane_exists(&relative_position, &self.z_axis, self, other)
199            || Self::separating_plane_exists(&relative_position, &other.x_axis, self, other)
200            || Self::separating_plane_exists(&relative_position, &other.y_axis, self, other)
201            || Self::separating_plane_exists(&relative_position, &other.z_axis, self, other)
202            || Self::separating_plane_exists(
203                &relative_position,
204                &self.x_axis.cross(&other.x_axis),
205                self,
206                other,
207            )
208            || Self::separating_plane_exists(
209                &relative_position,
210                &self.x_axis.cross(&other.y_axis),
211                self,
212                other,
213            )
214            || Self::separating_plane_exists(
215                &relative_position,
216                &self.x_axis.cross(&other.z_axis),
217                self,
218                other,
219            )
220            || Self::separating_plane_exists(
221                &relative_position,
222                &self.y_axis.cross(&other.x_axis),
223                self,
224                other,
225            )
226            || Self::separating_plane_exists(
227                &relative_position,
228                &self.y_axis.cross(&other.y_axis),
229                self,
230                other,
231            )
232            || Self::separating_plane_exists(
233                &relative_position,
234                &self.y_axis.cross(&other.z_axis),
235                self,
236                other,
237            )
238            || Self::separating_plane_exists(
239                &relative_position,
240                &self.z_axis.cross(&other.x_axis),
241                self,
242                other,
243            )
244            || Self::separating_plane_exists(
245                &relative_position,
246                &self.z_axis.cross(&other.y_axis),
247                self,
248                other,
249            )
250            || Self::separating_plane_exists(
251                &relative_position,
252                &self.z_axis.cross(&other.z_axis),
253                self,
254                other,
255            ))
256    }
257
258    pub fn jsondump(&self) -> Result<String, std::boxed::Box<dyn std::error::Error>> {
259        let data = serde_json::json!({
260            "type": "BoundingBox",
261            "center": serde_json::from_str::<serde_json::Value>(&self.center.jsondump()?)?,
262            "x_axis": serde_json::from_str::<serde_json::Value>(&self.x_axis.jsondump()?)?,
263            "y_axis": serde_json::from_str::<serde_json::Value>(&self.y_axis.jsondump()?)?,
264            "z_axis": serde_json::from_str::<serde_json::Value>(&self.z_axis.jsondump()?)?,
265            "half_size": serde_json::from_str::<serde_json::Value>(&self.half_size.jsondump()?)?,
266            "guid": self.guid,
267            "name": self.name,
268        });
269        Ok(serde_json::to_string(&data)?)
270    }
271
272    pub fn jsonload(json_data: &str) -> Result<Self, std::boxed::Box<dyn std::error::Error>> {
273        let data: serde_json::Value = serde_json::from_str(json_data)?;
274        let mut bbox = BoundingBox::new(
275            Point::jsonload(&data["center"].to_string())?,
276            Vector::jsonload(&data["x_axis"].to_string())?,
277            Vector::jsonload(&data["y_axis"].to_string())?,
278            Vector::jsonload(&data["z_axis"].to_string())?,
279            Vector::jsonload(&data["half_size"].to_string())?,
280        );
281        bbox.guid = data["guid"].as_str().unwrap().to_string();
282        bbox.name = data["name"].as_str().unwrap().to_string();
283        Ok(bbox)
284    }
285
286    pub fn to_json(&self, filepath: &str) -> Result<(), std::boxed::Box<dyn std::error::Error>> {
287        let json_string = self.jsondump()?;
288        let value: serde_json::Value = serde_json::from_str(&json_string)?;
289        let pretty = serde_json::to_string_pretty(&value)?;
290        std::fs::write(filepath, pretty)?;
291        Ok(())
292    }
293
294    pub fn from_json(filepath: &str) -> Result<Self, std::boxed::Box<dyn std::error::Error>> {
295        let json_string = std::fs::read_to_string(filepath)?;
296        Self::jsonload(&json_string)
297    }
298}
299
300impl Default for BoundingBox {
301    fn default() -> Self {
302        BoundingBox {
303            center: Point::new(0.0, 0.0, 0.0),
304            x_axis: Vector::new(1.0, 0.0, 0.0),
305            y_axis: Vector::new(0.0, 1.0, 0.0),
306            z_axis: Vector::new(0.0, 0.0, 1.0),
307            half_size: Vector::new(0.5, 0.5, 0.5),
308            guid: Uuid::new_v4().to_string(),
309            name: String::new(),
310            xform: Xform::identity(),
311        }
312    }
313}
314
315#[cfg(test)]
316#[path = "boundingbox_test.rs"]
317mod boundingbox_test;