1use crate::vector::*;
4use crate::transform::*;
5use crate::BinarySerializable;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
14pub struct BoundingBox {
15 pub min: Vector,
17 pub max: Vector,
19}
20
21impl fmt::Display for BoundingBox {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 let center = self.center();
24 let size = self.size();
25 write!(
26 f,
27 "BoundingBox(Min: ({:.2}, {:.2}, {:.2}), Max: ({:.2}, {:.2}, {:.2}), Center: ({:.2}, {:.2}, {:.2}), Size: ({:.2}, {:.2}, {:.2}))",
28 self.min.x, self.min.y, self.min.z,
29 self.max.x, self.max.y, self.max.z,
30 center.x, center.y, center.z,
31 size.x, size.y, size.z
32 )
33 }
34}
35
36impl BinarySerializable for BoundingBox {}
37
38impl BoundingBox {
39 pub const EMPTY: Self = Self {
41 min: Vector::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
42 max: Vector::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
43 };
44
45 pub fn new(min: Vector, max: Vector) -> Self {
47 Self { min, max }
48 }
49
50 pub fn from_center_and_extent(center: Vector, extent: Vector) -> Self {
52 Self {
53 min: center - extent,
54 max: center + extent,
55 }
56 }
57
58 pub fn from_point(point: Vector) -> Self {
60 Self {
61 min: point,
62 max: point,
63 }
64 }
65
66 pub fn from_points(points: &[Vector]) -> Self {
68 if points.is_empty() {
69 return Self::EMPTY;
70 }
71
72 let mut bbox = Self::from_point(points[0]);
73 for &point in &points[1..] {
74 bbox = bbox.expand_to_include(point);
75 }
76 bbox
77 }
78
79 pub fn center(self) -> Vector {
81 (self.min + self.max) * 0.5
82 }
83
84 pub fn extent(self) -> Vector {
86 (self.max - self.min) * 0.5
87 }
88
89 pub fn size(self) -> Vector {
91 self.max - self.min
92 }
93
94 pub fn volume(self) -> f32 {
96 let size = self.size();
97 size.x * size.y * size.z
98 }
99
100 pub fn surface_area(self) -> f32 {
102 let size = self.size();
103 2.0 * (size.x * size.y + size.y * size.z + size.z * size.x)
104 }
105
106 pub fn is_valid(self) -> bool {
108 self.min.x <= self.max.x && self.min.y <= self.max.y && self.min.z <= self.max.z
109 }
110
111 pub fn is_empty(self) -> bool {
113 self.min.x >= self.max.x || self.min.y >= self.max.y || self.min.z >= self.max.z
114 }
115
116 pub fn contains_point(self, point: Vector) -> bool {
118 point.x >= self.min.x && point.x <= self.max.x
119 && point.y >= self.min.y && point.y <= self.max.y
120 && point.z >= self.min.z && point.z <= self.max.z
121 }
122
123 pub fn contains_box(self, other: BoundingBox) -> bool {
125 self.contains_point(other.min) && self.contains_point(other.max)
126 }
127
128 pub fn intersects(self, other: BoundingBox) -> bool {
130 self.min.x <= other.max.x && self.max.x >= other.min.x
131 && self.min.y <= other.max.y && self.max.y >= other.min.y
132 && self.min.z <= other.max.z && self.max.z >= other.min.z
133 }
134
135 pub fn expand_to_include(self, point: Vector) -> Self {
137 Self {
138 min: self.min.min(point),
139 max: self.max.max(point),
140 }
141 }
142
143 pub fn expand_to_include_box(self, other: BoundingBox) -> Self {
145 if other.is_empty() {
146 return self;
147 }
148 if self.is_empty() {
149 return other;
150 }
151
152 Self {
153 min: self.min.min(other.min),
154 max: self.max.max(other.max),
155 }
156 }
157
158 pub fn expand_by(self, amount: f32) -> Self {
160 let expansion = Vector::splat(amount);
161 Self {
162 min: self.min - expansion,
163 max: self.max + expansion,
164 }
165 }
166
167 pub fn intersection(self, other: BoundingBox) -> Self {
169 if !self.intersects(other) {
170 return Self::EMPTY;
171 }
172
173 Self {
174 min: self.min.max(other.min),
175 max: self.max.min(other.max),
176 }
177 }
178
179 pub fn transform(self, transform: Transform) -> Self {
181 if self.is_empty() {
182 return Self::EMPTY;
183 }
184
185 let corners = [
187 Vector::new(self.min.x, self.min.y, self.min.z),
188 Vector::new(self.max.x, self.min.y, self.min.z),
189 Vector::new(self.min.x, self.max.y, self.min.z),
190 Vector::new(self.max.x, self.max.y, self.min.z),
191 Vector::new(self.min.x, self.min.y, self.max.z),
192 Vector::new(self.max.x, self.min.y, self.max.z),
193 Vector::new(self.min.x, self.max.y, self.max.z),
194 Vector::new(self.max.x, self.max.y, self.max.z),
195 ];
196
197 let transformed_corners: Vec<Vector> = corners
198 .iter()
199 .map(|&corner| transform.transform_point(corner))
200 .collect();
201
202 Self::from_points(&transformed_corners)
203 }
204
205 pub fn distance_to_point(self, point: Vector) -> f32 {
207 let closest = point.clamp(self.min, self.max);
208 (point - closest).length()
209 }
210
211 pub fn closest_point_to(self, point: Vector) -> Vector {
213 point.clamp(self.min, self.max)
214 }
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
222pub struct BoundingSphere {
223 pub center: Vector,
225 pub radius: f32,
227}
228
229impl fmt::Display for BoundingSphere {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 write!(
232 f,
233 "BoundingSphere(Center: ({:.2}, {:.2}, {:.2}), Radius: {:.2})",
234 self.center.x, self.center.y, self.center.z, self.radius
235 )
236 }
237}
238
239impl BinarySerializable for BoundingSphere {}
240
241impl BoundingSphere {
242 pub fn new(center: Vector, radius: f32) -> Self {
244 Self { center, radius }
245 }
246
247 pub fn from_box(bbox: BoundingBox) -> Self {
249 let center = bbox.center();
250 let radius = (bbox.max - center).length();
251 Self { center, radius }
252 }
253
254 pub fn from_points(points: &[Vector]) -> Self {
256 if points.is_empty() {
257 return Self::new(Vector::ZERO, 0.0);
258 }
259
260 let bbox = BoundingBox::from_points(points);
262 let center = bbox.center();
263
264 let radius = points
265 .iter()
266 .map(|&point| (point - center).length())
267 .fold(0.0f32, f32::max);
268
269 Self { center, radius }
270 }
271
272 pub fn contains_point(self, point: Vector) -> bool {
274 (point - self.center).length_squared() <= self.radius * self.radius
275 }
276
277 pub fn contains_sphere(self, other: BoundingSphere) -> bool {
279 let distance = (other.center - self.center).length();
280 distance + other.radius <= self.radius
281 }
282
283 pub fn intersects_sphere(self, other: BoundingSphere) -> bool {
285 let distance_squared = (other.center - self.center).length_squared();
286 let radii_sum = self.radius + other.radius;
287 distance_squared <= radii_sum * radii_sum
288 }
289
290 pub fn intersects_box(self, bbox: BoundingBox) -> bool {
292 let closest_point = bbox.closest_point_to(self.center);
293 self.contains_point(closest_point)
294 }
295
296 pub fn transform(self, transform: Transform) -> Self {
298 let new_center = transform.transform_point(self.center);
299
300 let scale_x = transform.scale.x.abs();
302 let scale_y = transform.scale.y.abs();
303 let scale_z = transform.scale.z.abs();
304 let max_scale = scale_x.max(scale_y).max(scale_z);
305
306 Self {
307 center: new_center,
308 radius: self.radius * max_scale,
309 }
310 }
311
312 pub fn distance_to_point(self, point: Vector) -> f32 {
314 (point - self.center).length() - self.radius
315 }
316
317 pub fn expand_to_include(self, point: Vector) -> Self {
319 let distance = (point - self.center).length();
320 if distance <= self.radius {
321 return self;
322 }
323
324 Self {
325 center: self.center,
326 radius: distance,
327 }
328 }
329
330 pub fn expand_to_include_sphere(self, other: BoundingSphere) -> Self {
332 let distance = (other.center - self.center).length();
333 let new_radius = (distance + other.radius).max(self.radius);
334
335 Self {
336 center: self.center,
337 radius: new_radius,
338 }
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_bounding_box_creation() {
348 let bbox = BoundingBox::new(
349 Vector::new(-1.0, -1.0, -1.0),
350 Vector::new(1.0, 1.0, 1.0)
351 );
352
353 assert_eq!(bbox.center(), Vector::ZERO);
354 assert_eq!(bbox.extent(), Vector::ONE);
355 assert_eq!(bbox.size(), Vector::splat(2.0));
356 }
357
358 #[test]
359 fn test_bounding_box_contains() {
360 let bbox = BoundingBox::new(
361 Vector::new(-1.0, -1.0, -1.0),
362 Vector::new(1.0, 1.0, 1.0)
363 );
364
365 assert!(bbox.contains_point(Vector::ZERO));
366 assert!(bbox.contains_point(Vector::new(0.5, 0.5, 0.5)));
367 assert!(!bbox.contains_point(Vector::new(2.0, 0.0, 0.0)));
368 }
369
370 #[test]
371 fn test_bounding_box_intersection() {
372 let bbox1 = BoundingBox::new(
373 Vector::new(-1.0, -1.0, -1.0),
374 Vector::new(1.0, 1.0, 1.0)
375 );
376 let bbox2 = BoundingBox::new(
377 Vector::new(0.0, 0.0, 0.0),
378 Vector::new(2.0, 2.0, 2.0)
379 );
380
381 assert!(bbox1.intersects(bbox2));
382
383 let intersection = bbox1.intersection(bbox2);
384 assert_eq!(intersection.min, Vector::ZERO);
385 assert_eq!(intersection.max, Vector::ONE);
386 }
387
388 #[test]
389 fn test_bounding_sphere_creation() {
390 let sphere = BoundingSphere::new(Vector::ZERO, 1.0);
391
392 assert!(sphere.contains_point(Vector::new(0.5, 0.0, 0.0)));
393 assert!(!sphere.contains_point(Vector::new(2.0, 0.0, 0.0)));
394 }
395
396 #[test]
397 fn test_sphere_box_intersection() {
398 let sphere = BoundingSphere::new(Vector::ZERO, 1.0);
399 let bbox = BoundingBox::new(
400 Vector::new(0.5, -0.5, -0.5),
401 Vector::new(1.5, 0.5, 0.5)
402 );
403
404 assert!(sphere.intersects_box(bbox));
405 }
406
407 #[test]
408 fn test_bounding_box_display() {
409 let bbox = BoundingBox::new(
410 Vector::new(-1.0, -2.0, -3.0),
411 Vector::new(1.0, 2.0, 3.0)
412 );
413
414 let display_str = format!("{}", bbox);
415 assert!(display_str.contains("Min: (-1.00, -2.00, -3.00)"));
416 assert!(display_str.contains("Max: (1.00, 2.00, 3.00)"));
417 assert!(display_str.contains("Center: (0.00, 0.00, 0.00)"));
418 assert!(display_str.contains("Size: (2.00, 4.00, 6.00)"));
419 }
420
421 #[test]
422 fn test_bounding_sphere_display() {
423 let sphere = BoundingSphere::new(Vector::new(1.0, 2.0, 3.0), 5.0);
424
425 let display_str = format!("{}", sphere);
426 assert!(display_str.contains("Center: (1.00, 2.00, 3.00)"));
427 assert!(display_str.contains("Radius: 5.00"));
428 }
429
430 #[test]
431 fn test_bounding_box_json_serialization() {
432 let bbox = BoundingBox::new(
433 Vector::new(-1.0, -2.0, -3.0),
434 Vector::new(1.0, 2.0, 3.0)
435 );
436
437 let json = serde_json::to_string(&bbox).unwrap();
439 let deserialized: BoundingBox = serde_json::from_str(&json).unwrap();
440
441 assert_eq!(bbox, deserialized);
442 }
443
444 #[test]
445 fn test_bounding_sphere_json_serialization() {
446 let sphere = BoundingSphere::new(Vector::new(1.0, 2.0, 3.0), 5.0);
447
448 let json = serde_json::to_string(&sphere).unwrap();
450 let deserialized: BoundingSphere = serde_json::from_str(&json).unwrap();
451
452 assert_eq!(sphere, deserialized);
453 }
454
455 #[test]
456 fn test_bounding_box_binary_serialization() {
457 let bbox = BoundingBox::new(
458 Vector::new(-1.0, -2.0, -3.0),
459 Vector::new(1.0, 2.0, 3.0)
460 );
461
462 let binary = bbox.to_binary().unwrap();
464 let deserialized = BoundingBox::from_binary(&binary).unwrap();
465
466 assert_eq!(bbox, deserialized);
467 }
468
469 #[test]
470 fn test_bounding_sphere_binary_serialization() {
471 let sphere = BoundingSphere::new(Vector::new(1.0, 2.0, 3.0), 5.0);
472
473 let binary = sphere.to_binary().unwrap();
475 let deserialized = BoundingSphere::from_binary(&binary).unwrap();
476
477 assert_eq!(sphere, deserialized);
478 }
479}