1use geo::{Point, Rect};
2use serde::{Deserialize, Serialize};
3use std::time::SystemTime;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct BoundingBox2D {
11 pub rect: Rect,
13}
14
15impl BoundingBox2D {
16 pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
33 Self {
34 rect: Rect::new(
35 geo::coord! { x: min_x, y: min_y },
36 geo::coord! { x: max_x, y: max_y },
37 ),
38 }
39 }
40
41 pub fn from_rect(rect: Rect) -> Self {
43 Self { rect }
44 }
45
46 pub fn min_x(&self) -> f64 {
48 self.rect.min().x
49 }
50
51 pub fn min_y(&self) -> f64 {
53 self.rect.min().y
54 }
55
56 pub fn max_x(&self) -> f64 {
58 self.rect.max().x
59 }
60
61 pub fn max_y(&self) -> f64 {
63 self.rect.max().y
64 }
65
66 pub fn center(&self) -> Point {
68 Point::new(
69 (self.min_x() + self.max_x()) / 2.0,
70 (self.min_y() + self.max_y()) / 2.0,
71 )
72 }
73
74 pub fn width(&self) -> f64 {
76 self.max_x() - self.min_x()
77 }
78
79 pub fn height(&self) -> f64 {
81 self.max_y() - self.min_y()
82 }
83
84 pub fn contains_point(&self, point: &Point) -> bool {
86 point.x() >= self.min_x()
87 && point.x() <= self.max_x()
88 && point.y() >= self.min_y()
89 && point.y() <= self.max_y()
90 }
91
92 pub fn intersects(&self, other: &BoundingBox2D) -> bool {
94 !(self.max_x() < other.min_x()
95 || self.min_x() > other.max_x()
96 || self.max_y() < other.min_y()
97 || self.min_y() > other.max_y())
98 }
99
100 pub fn expand(&self, amount: f64) -> Self {
102 Self::new(
103 self.min_x() - amount,
104 self.min_y() - amount,
105 self.max_x() + amount,
106 self.max_y() + amount,
107 )
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct BoundingBox3D {
117 pub min_x: f64,
119 pub min_y: f64,
121 pub min_z: f64,
123 pub max_x: f64,
125 pub max_y: f64,
127 pub max_z: f64,
129}
130
131impl BoundingBox3D {
132 pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Self {
151 Self {
152 min_x,
153 min_y,
154 min_z,
155 max_x,
156 max_y,
157 max_z,
158 }
159 }
160
161 pub fn center(&self) -> (f64, f64, f64) {
163 (
164 (self.min_x + self.max_x) / 2.0,
165 (self.min_y + self.max_y) / 2.0,
166 (self.min_z + self.max_z) / 2.0,
167 )
168 }
169
170 pub fn width(&self) -> f64 {
172 self.max_x - self.min_x
173 }
174
175 pub fn height(&self) -> f64 {
177 self.max_y - self.min_y
178 }
179
180 pub fn depth(&self) -> f64 {
182 self.max_z - self.min_z
183 }
184
185 pub fn volume(&self) -> f64 {
187 self.width() * self.height() * self.depth()
188 }
189
190 pub fn contains_point(&self, x: f64, y: f64, z: f64) -> bool {
192 x >= self.min_x
193 && x <= self.max_x
194 && y >= self.min_y
195 && y <= self.max_y
196 && z >= self.min_z
197 && z <= self.max_z
198 }
199
200 pub fn intersects(&self, other: &BoundingBox3D) -> bool {
202 !(self.max_x < other.min_x
203 || self.min_x > other.max_x
204 || self.max_y < other.min_y
205 || self.min_y > other.max_y
206 || self.max_z < other.min_z
207 || self.min_z > other.max_z)
208 }
209
210 pub fn expand(&self, amount: f64) -> Self {
212 Self::new(
213 self.min_x - amount,
214 self.min_y - amount,
215 self.min_z - amount,
216 self.max_x + amount,
217 self.max_y + amount,
218 self.max_z + amount,
219 )
220 }
221
222 pub fn to_2d(&self) -> BoundingBox2D {
224 BoundingBox2D::new(self.min_x, self.min_y, self.max_x, self.max_y)
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct TemporalBoundingBox2D {
233 pub bbox: BoundingBox2D,
235 pub timestamp: SystemTime,
237}
238
239impl TemporalBoundingBox2D {
240 pub fn new(bbox: BoundingBox2D, timestamp: SystemTime) -> Self {
242 Self { bbox, timestamp }
243 }
244
245 pub fn bbox(&self) -> &BoundingBox2D {
247 &self.bbox
248 }
249
250 pub fn timestamp(&self) -> &SystemTime {
252 &self.timestamp
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct TemporalBoundingBox3D {
261 pub bbox: BoundingBox3D,
263 pub timestamp: SystemTime,
265}
266
267impl TemporalBoundingBox3D {
268 pub fn new(bbox: BoundingBox3D, timestamp: SystemTime) -> Self {
270 Self { bbox, timestamp }
271 }
272
273 pub fn bbox(&self) -> &BoundingBox3D {
275 &self.bbox
276 }
277
278 pub fn timestamp(&self) -> &SystemTime {
280 &self.timestamp
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn test_bbox2d_creation() {
290 let bbox = BoundingBox2D::new(-74.0, 40.7, -73.9, 40.8);
291 assert_eq!(bbox.min_x(), -74.0);
292 assert_eq!(bbox.min_y(), 40.7);
293 assert_eq!(bbox.max_x(), -73.9);
294 assert_eq!(bbox.max_y(), 40.8);
295 }
296
297 #[test]
298 fn test_bbox2d_dimensions() {
299 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 5.0);
300 assert_eq!(bbox.width(), 10.0);
301 assert_eq!(bbox.height(), 5.0);
302 }
303
304 #[test]
305 fn test_bbox2d_center() {
306 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
307 let center = bbox.center();
308 assert_eq!(center.x(), 5.0);
309 assert_eq!(center.y(), 5.0);
310 }
311
312 #[test]
313 fn test_bbox2d_contains() {
314 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
315 assert!(bbox.contains_point(&Point::new(5.0, 5.0)));
316 assert!(bbox.contains_point(&Point::new(0.0, 0.0)));
317 assert!(bbox.contains_point(&Point::new(10.0, 10.0)));
318 assert!(!bbox.contains_point(&Point::new(-1.0, 5.0)));
319 assert!(!bbox.contains_point(&Point::new(11.0, 5.0)));
320 }
321
322 #[test]
323 fn test_bbox2d_intersects() {
324 let bbox1 = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
325 let bbox2 = BoundingBox2D::new(5.0, 5.0, 15.0, 15.0);
326 let bbox3 = BoundingBox2D::new(20.0, 20.0, 30.0, 30.0);
327
328 assert!(bbox1.intersects(&bbox2));
329 assert!(bbox2.intersects(&bbox1));
330 assert!(!bbox1.intersects(&bbox3));
331 assert!(!bbox3.intersects(&bbox1));
332 }
333
334 #[test]
335 fn test_bbox2d_expand() {
336 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
337 let expanded = bbox.expand(5.0);
338 assert_eq!(expanded.min_x(), -5.0);
339 assert_eq!(expanded.min_y(), -5.0);
340 assert_eq!(expanded.max_x(), 15.0);
341 assert_eq!(expanded.max_y(), 15.0);
342 }
343
344 #[test]
345 fn test_bbox3d_creation() {
346 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
347 assert_eq!(bbox.min_x, 0.0);
348 assert_eq!(bbox.min_y, 0.0);
349 assert_eq!(bbox.min_z, 0.0);
350 assert_eq!(bbox.max_x, 10.0);
351 assert_eq!(bbox.max_y, 10.0);
352 assert_eq!(bbox.max_z, 10.0);
353 }
354
355 #[test]
356 fn test_bbox3d_dimensions() {
357 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 5.0, 3.0);
358 assert_eq!(bbox.width(), 10.0);
359 assert_eq!(bbox.height(), 5.0);
360 assert_eq!(bbox.depth(), 3.0);
361 assert_eq!(bbox.volume(), 150.0);
362 }
363
364 #[test]
365 fn test_bbox3d_center() {
366 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
367 let (x, y, z) = bbox.center();
368 assert_eq!(x, 5.0);
369 assert_eq!(y, 5.0);
370 assert_eq!(z, 5.0);
371 }
372
373 #[test]
374 fn test_bbox3d_contains() {
375 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
376 assert!(bbox.contains_point(5.0, 5.0, 5.0));
377 assert!(bbox.contains_point(0.0, 0.0, 0.0));
378 assert!(bbox.contains_point(10.0, 10.0, 10.0));
379 assert!(!bbox.contains_point(-1.0, 5.0, 5.0));
380 assert!(!bbox.contains_point(5.0, 5.0, 11.0));
381 }
382
383 #[test]
384 fn test_bbox3d_intersects() {
385 let bbox1 = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
386 let bbox2 = BoundingBox3D::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
387 let bbox3 = BoundingBox3D::new(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
388
389 assert!(bbox1.intersects(&bbox2));
390 assert!(bbox2.intersects(&bbox1));
391 assert!(!bbox1.intersects(&bbox3));
392 assert!(!bbox3.intersects(&bbox1));
393 }
394
395 #[test]
396 fn test_bbox3d_to_2d() {
397 let bbox3d = BoundingBox3D::new(0.0, 0.0, 5.0, 10.0, 10.0, 15.0);
398 let bbox2d = bbox3d.to_2d();
399 assert_eq!(bbox2d.min_x(), 0.0);
400 assert_eq!(bbox2d.min_y(), 0.0);
401 assert_eq!(bbox2d.max_x(), 10.0);
402 assert_eq!(bbox2d.max_y(), 10.0);
403 }
404
405 #[test]
406 fn test_temporal_bbox2d() {
407 let bbox = BoundingBox2D::new(0.0, 0.0, 10.0, 10.0);
408 let timestamp = SystemTime::now();
409 let temporal_bbox = TemporalBoundingBox2D::new(bbox.clone(), timestamp);
410
411 assert_eq!(temporal_bbox.bbox(), &bbox);
412 assert_eq!(temporal_bbox.timestamp(), ×tamp);
413 }
414
415 #[test]
416 fn test_temporal_bbox3d() {
417 let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
418 let timestamp = SystemTime::now();
419 let temporal_bbox = TemporalBoundingBox3D::new(bbox.clone(), timestamp);
420
421 assert_eq!(temporal_bbox.bbox(), &bbox);
422 assert_eq!(temporal_bbox.timestamp(), ×tamp);
423 }
424}