1use crate::geo::Point;
2use geo::Rect;
3use serde::{Deserialize, Serialize};
4use std::time::SystemTime;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct BoundingBox2D {
12 pub rect: Rect,
14}
15
16impl BoundingBox2D {
17 pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
34 Self {
35 rect: Rect::new(
36 geo::coord! { x: min_x, y: min_y },
37 geo::coord! { x: max_x, y: max_y },
38 ),
39 }
40 }
41
42 pub fn from_rect(rect: Rect) -> Self {
44 Self { rect }
45 }
46
47 pub fn min_x(&self) -> f64 {
49 self.rect.min().x
50 }
51
52 pub fn min_y(&self) -> f64 {
54 self.rect.min().y
55 }
56
57 pub fn max_x(&self) -> f64 {
59 self.rect.max().x
60 }
61
62 pub fn max_y(&self) -> f64 {
64 self.rect.max().y
65 }
66
67 pub fn center(&self) -> Point {
69 Point::new(
70 (self.min_x() + self.max_x()) / 2.0,
71 (self.min_y() + self.max_y()) / 2.0,
72 )
73 }
74
75 pub fn width(&self) -> f64 {
77 self.max_x() - self.min_x()
78 }
79
80 pub fn height(&self) -> f64 {
82 self.max_y() - self.min_y()
83 }
84
85 pub fn contains_point(&self, point: &Point) -> bool {
87 point.x() >= self.min_x()
88 && point.x() <= self.max_x()
89 && point.y() >= self.min_y()
90 && point.y() <= self.max_y()
91 }
92
93 pub fn intersects(&self, other: &BoundingBox2D) -> bool {
95 !(self.max_x() < other.min_x()
96 || self.min_x() > other.max_x()
97 || self.max_y() < other.min_y()
98 || self.min_y() > other.max_y())
99 }
100
101 pub fn expand(&self, amount: f64) -> Self {
103 Self::new(
104 self.min_x() - amount,
105 self.min_y() - amount,
106 self.max_x() + amount,
107 self.max_y() + amount,
108 )
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117pub struct BoundingBox3D {
118 pub min_x: f64,
120 pub min_y: f64,
122 pub min_z: f64,
124 pub max_x: f64,
126 pub max_y: f64,
128 pub max_z: f64,
130}
131
132impl BoundingBox3D {
133 #[must_use]
156 pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Self {
157 let (min_x, max_x) = if min_x <= max_x {
158 (min_x, max_x)
159 } else {
160 (max_x, min_x)
161 };
162 let (min_y, max_y) = if min_y <= max_y {
163 (min_y, max_y)
164 } else {
165 (max_y, min_y)
166 };
167 let (min_z, max_z) = if min_z <= max_z {
168 (min_z, max_z)
169 } else {
170 (max_z, min_z)
171 };
172 Self {
173 min_x,
174 min_y,
175 min_z,
176 max_x,
177 max_y,
178 max_z,
179 }
180 }
181
182 #[inline]
184 #[must_use]
185 pub fn center(&self) -> (f64, f64, f64) {
186 (
187 (self.min_x + self.max_x) / 2.0,
188 (self.min_y + self.max_y) / 2.0,
189 (self.min_z + self.max_z) / 2.0,
190 )
191 }
192
193 #[inline]
195 #[must_use]
196 pub fn width(&self) -> f64 {
197 self.max_x - self.min_x
198 }
199
200 #[inline]
202 #[must_use]
203 pub fn height(&self) -> f64 {
204 self.max_y - self.min_y
205 }
206
207 #[inline]
209 #[must_use]
210 pub fn depth(&self) -> f64 {
211 self.max_z - self.min_z
212 }
213
214 #[inline]
216 #[must_use]
217 pub fn volume(&self) -> f64 {
218 self.width() * self.height() * self.depth()
219 }
220
221 #[inline]
223 #[must_use]
224 pub fn contains_point(&self, x: f64, y: f64, z: f64) -> bool {
225 x >= self.min_x
226 && x <= self.max_x
227 && y >= self.min_y
228 && y <= self.max_y
229 && z >= self.min_z
230 && z <= self.max_z
231 }
232
233 #[inline]
235 #[must_use]
236 pub fn intersects(&self, other: &BoundingBox3D) -> bool {
237 !(self.max_x < other.min_x
238 || self.min_x > other.max_x
239 || self.max_y < other.min_y
240 || self.min_y > other.max_y
241 || self.max_z < other.min_z
242 || self.min_z > other.max_z)
243 }
244
245 #[must_use]
251 pub fn expand(&self, amount: f64) -> Self {
252 Self::new(
253 self.min_x - amount,
254 self.min_y - amount,
255 self.min_z - amount,
256 self.max_x + amount,
257 self.max_y + amount,
258 self.max_z + amount,
259 )
260 }
261
262 #[inline]
264 #[must_use]
265 pub fn to_2d(&self) -> BoundingBox2D {
266 BoundingBox2D::new(self.min_x, self.min_y, self.max_x, self.max_y)
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274pub struct TemporalBoundingBox2D {
275 pub bbox: BoundingBox2D,
277 pub timestamp: SystemTime,
279}
280
281impl TemporalBoundingBox2D {
282 pub fn new(bbox: BoundingBox2D, timestamp: SystemTime) -> Self {
284 Self { bbox, timestamp }
285 }
286
287 pub fn bbox(&self) -> &BoundingBox2D {
289 &self.bbox
290 }
291
292 pub fn timestamp(&self) -> &SystemTime {
294 &self.timestamp
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct TemporalBoundingBox3D {
303 pub bbox: BoundingBox3D,
305 pub timestamp: SystemTime,
307}
308
309impl TemporalBoundingBox3D {
310 pub fn new(bbox: BoundingBox3D, timestamp: SystemTime) -> Self {
312 Self { bbox, timestamp }
313 }
314
315 pub fn bbox(&self) -> &BoundingBox3D {
317 &self.bbox
318 }
319
320 pub fn timestamp(&self) -> &SystemTime {
322 &self.timestamp
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_bbox3d_new_normalizes_swapped_corners() {
332 let bbox = BoundingBox3D::new(10.0, 20.0, 30.0, 0.0, 5.0, 15.0);
334 assert_eq!((bbox.min_x, bbox.max_x), (0.0, 10.0));
335 assert_eq!((bbox.min_y, bbox.max_y), (5.0, 20.0));
336 assert_eq!((bbox.min_z, bbox.max_z), (15.0, 30.0));
337 assert!(bbox.width() >= 0.0 && bbox.height() >= 0.0 && bbox.depth() >= 0.0);
338 assert!(bbox.contains_point(5.0, 10.0, 20.0));
339 }
340
341 #[test]
342 fn test_bbox2d_creation() {
343 let bbox = BoundingBox2D::new(-74.0, 40.7, -73.9, 40.8);
344 assert_eq!(bbox.min_x(), -74.0);
345 assert_eq!(bbox.min_y(), 40.7);
346 assert_eq!(bbox.max_x(), -73.9);
347 assert_eq!(bbox.max_y(), 40.8);
348 }
349
350 #[test]
351 fn test_bbox2d_dimensions() {
352 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 5.0);
353 assert_eq!(bbox.width(), 10.0);
354 assert_eq!(bbox.height(), 5.0);
355 }
356
357 #[test]
358 fn test_bbox2d_center() {
359 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
360 let center = bbox.center();
361 assert_eq!(center.x(), 5.0);
362 assert_eq!(center.y(), 5.0);
363 }
364
365 #[test]
366 fn test_bbox2d_contains() {
367 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
368 assert!(bbox.contains_point(&Point::new(5.0, 5.0)));
369 assert!(bbox.contains_point(&Point::new(0.0, 0.0)));
370 assert!(bbox.contains_point(&Point::new(10.0, 10.0)));
371 assert!(!bbox.contains_point(&Point::new(-1.0, 5.0)));
372 assert!(!bbox.contains_point(&Point::new(11.0, 5.0)));
373 }
374
375 #[test]
376 fn test_bbox2d_intersects() {
377 let bbox1 = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
378 let bbox2 = BoundingBox2D::new(5.0, 5.0, 15.0, 15.0);
379 let bbox3 = BoundingBox2D::new(20.0, 20.0, 30.0, 30.0);
380
381 assert!(bbox1.intersects(&bbox2));
382 assert!(bbox2.intersects(&bbox1));
383 assert!(!bbox1.intersects(&bbox3));
384 assert!(!bbox3.intersects(&bbox1));
385 }
386
387 #[test]
388 fn test_bbox2d_expand() {
389 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
390 let expanded = bbox.expand(5.0);
391 assert_eq!(expanded.min_x(), -5.0);
392 assert_eq!(expanded.min_y(), -5.0);
393 assert_eq!(expanded.max_x(), 15.0);
394 assert_eq!(expanded.max_y(), 15.0);
395 }
396
397 #[test]
398 fn test_bbox3d_creation() {
399 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
400 assert_eq!(bbox.min_x, 0.0);
401 assert_eq!(bbox.min_y, 0.0);
402 assert_eq!(bbox.min_z, 0.0);
403 assert_eq!(bbox.max_x, 10.0);
404 assert_eq!(bbox.max_y, 10.0);
405 assert_eq!(bbox.max_z, 10.0);
406 }
407
408 #[test]
409 fn test_bbox3d_dimensions() {
410 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 5.0, 3.0);
411 assert_eq!(bbox.width(), 10.0);
412 assert_eq!(bbox.height(), 5.0);
413 assert_eq!(bbox.depth(), 3.0);
414 assert_eq!(bbox.volume(), 150.0);
415 }
416
417 #[test]
418 fn test_bbox3d_center() {
419 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
420 let (x, y, z) = bbox.center();
421 assert_eq!(x, 5.0);
422 assert_eq!(y, 5.0);
423 assert_eq!(z, 5.0);
424 }
425
426 #[test]
427 fn test_bbox3d_contains() {
428 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
429 assert!(bbox.contains_point(5.0, 5.0, 5.0));
430 assert!(bbox.contains_point(0.0, 0.0, 0.0));
431 assert!(bbox.contains_point(10.0, 10.0, 10.0));
432 assert!(!bbox.contains_point(-1.0, 5.0, 5.0));
433 assert!(!bbox.contains_point(5.0, 5.0, 11.0));
434 }
435
436 #[test]
437 fn test_bbox3d_intersects() {
438 let bbox1 = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
439 let bbox2 = BoundingBox3D::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
440 let bbox3 = BoundingBox3D::new(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
441
442 assert!(bbox1.intersects(&bbox2));
443 assert!(bbox2.intersects(&bbox1));
444 assert!(!bbox1.intersects(&bbox3));
445 assert!(!bbox3.intersects(&bbox1));
446 }
447
448 #[test]
449 fn test_bbox3d_to_2d() {
450 let bbox3d = BoundingBox3D::new(0.0, 0.0, 5.0, 10.0, 10.0, 15.0);
451 let bbox2d = bbox3d.to_2d();
452 assert_eq!(bbox2d.min_x(), 0.0);
453 assert_eq!(bbox2d.min_y(), 0.0);
454 assert_eq!(bbox2d.max_x(), 10.0);
455 assert_eq!(bbox2d.max_y(), 10.0);
456 }
457
458 #[test]
459 fn test_temporal_bbox2d() {
460 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
461 let timestamp = SystemTime::now();
462 let temporal_bbox = TemporalBoundingBox2D::new(bbox.clone(), timestamp);
463
464 assert_eq!(temporal_bbox.bbox(), &bbox);
465 assert_eq!(temporal_bbox.timestamp(), ×tamp);
466 }
467
468 #[test]
469 fn test_temporal_bbox3d() {
470 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
471 let timestamp = SystemTime::now();
472 let temporal_bbox = TemporalBoundingBox3D::new(bbox.clone(), timestamp);
473
474 assert_eq!(temporal_bbox.bbox(), &bbox);
475 assert_eq!(temporal_bbox.timestamp(), ×tamp);
476 }
477}