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(¢er_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;