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 pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Self {
152 Self {
153 min_x,
154 min_y,
155 min_z,
156 max_x,
157 max_y,
158 max_z,
159 }
160 }
161
162 pub fn center(&self) -> (f64, f64, f64) {
164 (
165 (self.min_x + self.max_x) / 2.0,
166 (self.min_y + self.max_y) / 2.0,
167 (self.min_z + self.max_z) / 2.0,
168 )
169 }
170
171 pub fn width(&self) -> f64 {
173 self.max_x - self.min_x
174 }
175
176 pub fn height(&self) -> f64 {
178 self.max_y - self.min_y
179 }
180
181 pub fn depth(&self) -> f64 {
183 self.max_z - self.min_z
184 }
185
186 pub fn volume(&self) -> f64 {
188 self.width() * self.height() * self.depth()
189 }
190
191 pub fn contains_point(&self, x: f64, y: f64, z: f64) -> bool {
193 x >= self.min_x
194 && x <= self.max_x
195 && y >= self.min_y
196 && y <= self.max_y
197 && z >= self.min_z
198 && z <= self.max_z
199 }
200
201 pub fn intersects(&self, other: &BoundingBox3D) -> bool {
203 !(self.max_x < other.min_x
204 || self.min_x > other.max_x
205 || self.max_y < other.min_y
206 || self.min_y > other.max_y
207 || self.max_z < other.min_z
208 || self.min_z > other.max_z)
209 }
210
211 pub fn expand(&self, amount: f64) -> Self {
213 Self::new(
214 self.min_x - amount,
215 self.min_y - amount,
216 self.min_z - amount,
217 self.max_x + amount,
218 self.max_y + amount,
219 self.max_z + amount,
220 )
221 }
222
223 pub fn to_2d(&self) -> BoundingBox2D {
225 BoundingBox2D::new(self.min_x, self.min_y, self.max_x, self.max_y)
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
233pub struct TemporalBoundingBox2D {
234 pub bbox: BoundingBox2D,
236 pub timestamp: SystemTime,
238}
239
240impl TemporalBoundingBox2D {
241 pub fn new(bbox: BoundingBox2D, timestamp: SystemTime) -> Self {
243 Self { bbox, timestamp }
244 }
245
246 pub fn bbox(&self) -> &BoundingBox2D {
248 &self.bbox
249 }
250
251 pub fn timestamp(&self) -> &SystemTime {
253 &self.timestamp
254 }
255}
256
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct TemporalBoundingBox3D {
262 pub bbox: BoundingBox3D,
264 pub timestamp: SystemTime,
266}
267
268impl TemporalBoundingBox3D {
269 pub fn new(bbox: BoundingBox3D, timestamp: SystemTime) -> Self {
271 Self { bbox, timestamp }
272 }
273
274 pub fn bbox(&self) -> &BoundingBox3D {
276 &self.bbox
277 }
278
279 pub fn timestamp(&self) -> &SystemTime {
281 &self.timestamp
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_bbox2d_creation() {
291 let bbox = BoundingBox2D::new(-74.0, 40.7, -73.9, 40.8);
292 assert_eq!(bbox.min_x(), -74.0);
293 assert_eq!(bbox.min_y(), 40.7);
294 assert_eq!(bbox.max_x(), -73.9);
295 assert_eq!(bbox.max_y(), 40.8);
296 }
297
298 #[test]
299 fn test_bbox2d_dimensions() {
300 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 5.0);
301 assert_eq!(bbox.width(), 10.0);
302 assert_eq!(bbox.height(), 5.0);
303 }
304
305 #[test]
306 fn test_bbox2d_center() {
307 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
308 let center = bbox.center();
309 assert_eq!(center.x(), 5.0);
310 assert_eq!(center.y(), 5.0);
311 }
312
313 #[test]
314 fn test_bbox2d_contains() {
315 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
316 assert!(bbox.contains_point(&Point::new(5.0, 5.0)));
317 assert!(bbox.contains_point(&Point::new(0.0, 0.0)));
318 assert!(bbox.contains_point(&Point::new(10.0, 10.0)));
319 assert!(!bbox.contains_point(&Point::new(-1.0, 5.0)));
320 assert!(!bbox.contains_point(&Point::new(11.0, 5.0)));
321 }
322
323 #[test]
324 fn test_bbox2d_intersects() {
325 let bbox1 = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
326 let bbox2 = BoundingBox2D::new(5.0, 5.0, 15.0, 15.0);
327 let bbox3 = BoundingBox2D::new(20.0, 20.0, 30.0, 30.0);
328
329 assert!(bbox1.intersects(&bbox2));
330 assert!(bbox2.intersects(&bbox1));
331 assert!(!bbox1.intersects(&bbox3));
332 assert!(!bbox3.intersects(&bbox1));
333 }
334
335 #[test]
336 fn test_bbox2d_expand() {
337 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
338 let expanded = bbox.expand(5.0);
339 assert_eq!(expanded.min_x(), -5.0);
340 assert_eq!(expanded.min_y(), -5.0);
341 assert_eq!(expanded.max_x(), 15.0);
342 assert_eq!(expanded.max_y(), 15.0);
343 }
344
345 #[test]
346 fn test_bbox3d_creation() {
347 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
348 assert_eq!(bbox.min_x, 0.0);
349 assert_eq!(bbox.min_y, 0.0);
350 assert_eq!(bbox.min_z, 0.0);
351 assert_eq!(bbox.max_x, 10.0);
352 assert_eq!(bbox.max_y, 10.0);
353 assert_eq!(bbox.max_z, 10.0);
354 }
355
356 #[test]
357 fn test_bbox3d_dimensions() {
358 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 5.0, 3.0);
359 assert_eq!(bbox.width(), 10.0);
360 assert_eq!(bbox.height(), 5.0);
361 assert_eq!(bbox.depth(), 3.0);
362 assert_eq!(bbox.volume(), 150.0);
363 }
364
365 #[test]
366 fn test_bbox3d_center() {
367 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
368 let (x, y, z) = bbox.center();
369 assert_eq!(x, 5.0);
370 assert_eq!(y, 5.0);
371 assert_eq!(z, 5.0);
372 }
373
374 #[test]
375 fn test_bbox3d_contains() {
376 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
377 assert!(bbox.contains_point(5.0, 5.0, 5.0));
378 assert!(bbox.contains_point(0.0, 0.0, 0.0));
379 assert!(bbox.contains_point(10.0, 10.0, 10.0));
380 assert!(!bbox.contains_point(-1.0, 5.0, 5.0));
381 assert!(!bbox.contains_point(5.0, 5.0, 11.0));
382 }
383
384 #[test]
385 fn test_bbox3d_intersects() {
386 let bbox1 = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
387 let bbox2 = BoundingBox3D::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
388 let bbox3 = BoundingBox3D::new(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
389
390 assert!(bbox1.intersects(&bbox2));
391 assert!(bbox2.intersects(&bbox1));
392 assert!(!bbox1.intersects(&bbox3));
393 assert!(!bbox3.intersects(&bbox1));
394 }
395
396 #[test]
397 fn test_bbox3d_to_2d() {
398 let bbox3d = BoundingBox3D::new(0.0, 0.0, 5.0, 10.0, 10.0, 15.0);
399 let bbox2d = bbox3d.to_2d();
400 assert_eq!(bbox2d.min_x(), 0.0);
401 assert_eq!(bbox2d.min_y(), 0.0);
402 assert_eq!(bbox2d.max_x(), 10.0);
403 assert_eq!(bbox2d.max_y(), 10.0);
404 }
405
406 #[test]
407 fn test_temporal_bbox2d() {
408 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
409 let timestamp = SystemTime::now();
410 let temporal_bbox = TemporalBoundingBox2D::new(bbox.clone(), timestamp);
411
412 assert_eq!(temporal_bbox.bbox(), &bbox);
413 assert_eq!(temporal_bbox.timestamp(), ×tamp);
414 }
415
416 #[test]
417 fn test_temporal_bbox3d() {
418 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
419 let timestamp = SystemTime::now();
420 let temporal_bbox = TemporalBoundingBox3D::new(bbox.clone(), timestamp);
421
422 assert_eq!(temporal_bbox.bbox(), &bbox);
423 assert_eq!(temporal_bbox.timestamp(), ×tamp);
424 }
425}