1use glam::{Vec2, Vec3};
31
32#[derive(Debug, Clone, Copy, PartialEq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct Rect {
38 pub min: Vec2,
40 pub max: Vec2,
42}
43
44impl Rect {
45 pub const ZERO: Self = Self {
47 min: Vec2::ZERO,
48 max: Vec2::ZERO,
49 };
50
51 pub fn new(min: Vec2, max: Vec2) -> Self {
73 Self {
74 min: min.min(max),
75 max: min.max(max),
76 }
77 }
78
79 pub fn from_center_size(center: Vec2, size: Vec2) -> Self {
91 let half_size = size * 0.5;
92 Self::new(center - half_size, center + half_size)
93 }
94
95 pub fn from_position_size(position: Vec2, size: Vec2) -> Self {
97 Self::new(position, position + size)
98 }
99
100 pub fn width(&self) -> f32 {
102 self.max.x - self.min.x
103 }
104
105 pub fn height(&self) -> f32 {
107 self.max.y - self.min.y
108 }
109
110 pub fn size(&self) -> Vec2 {
112 self.max - self.min
113 }
114
115 pub fn center(&self) -> Vec2 {
117 (self.min + self.max) * 0.5
118 }
119
120 pub fn area(&self) -> f32 {
122 let size = self.size();
123 size.x * size.y
124 }
125
126 pub fn perimeter(&self) -> f32 {
128 let size = self.size();
129 2.0 * (size.x + size.y)
130 }
131
132 pub fn contains(&self, point: Vec2) -> bool {
145 point.x >= self.min.x && point.x <= self.max.x &&
146 point.y >= self.min.y && point.y <= self.max.y
147 }
148
149 pub fn intersects(&self, other: &Rect) -> bool {
162 self.min.x <= other.max.x && self.max.x >= other.min.x &&
163 self.min.y <= other.max.y && self.max.y >= other.min.y
164 }
165
166 pub fn intersects_circle(&self, circle: &Circle) -> bool {
168 let closest_point = Vec2::new(
170 circle.center.x.clamp(self.min.x, self.max.x),
171 circle.center.y.clamp(self.min.y, self.max.y),
172 );
173
174 let distance_squared = (circle.center - closest_point).length_squared();
176 distance_squared <= circle.radius * circle.radius
177 }
178
179 pub fn intersection(&self, other: &Rect) -> Option<Rect> {
185 if !self.intersects(other) {
186 return None;
187 }
188
189 Some(Rect::new(
190 self.min.max(other.min),
191 self.max.min(other.max),
192 ))
193 }
194
195 pub fn union(&self, other: &Rect) -> Rect {
197 Rect::new(
198 self.min.min(other.min),
199 self.max.max(other.max),
200 )
201 }
202
203 pub fn expand_to_include(&mut self, point: Vec2) {
205 self.min = self.min.min(point);
206 self.max = self.max.max(point);
207 }
208
209 pub fn expand(&self, amount: f32) -> Rect {
215 let expansion = Vec2::splat(amount);
216 Rect::new(self.min - expansion, self.max + expansion)
217 }
218
219 pub fn is_valid(&self) -> bool {
221 self.min.x <= self.max.x && self.min.y <= self.max.y
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct Circle {
229 pub center: Vec2,
231 pub radius: f32,
233}
234
235impl Circle {
236 pub fn new(center: Vec2, radius: f32) -> Self {
253 Self {
254 center,
255 radius: radius.max(0.0), }
257 }
258
259 pub fn area(&self) -> f32 {
261 std::f32::consts::PI * self.radius * self.radius
262 }
263
264 pub fn circumference(&self) -> f32 {
266 2.0 * std::f32::consts::PI * self.radius
267 }
268
269 pub fn contains(&self, point: Vec2) -> bool {
271 (point - self.center).length_squared() <= self.radius * self.radius
272 }
273
274 pub fn intersects(&self, other: &Circle) -> bool {
276 let distance_squared = (self.center - other.center).length_squared();
277 let radius_sum = self.radius + other.radius;
278 distance_squared <= radius_sum * radius_sum
279 }
280
281 pub fn intersects_rect(&self, rect: &Rect) -> bool {
283 rect.intersects_circle(self)
284 }
285
286 pub fn bounding_rect(&self) -> Rect {
288 let radius_vec = Vec2::splat(self.radius);
289 Rect::new(self.center - radius_vec, self.center + radius_vec)
290 }
291}
292
293pub type Bounds2D = Rect;
295
296#[derive(Debug, Clone, Copy, PartialEq)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct Bounds3D {
300 pub min: Vec3,
302 pub max: Vec3,
304}
305
306impl Bounds3D {
307 pub const ZERO: Self = Self {
309 min: Vec3::ZERO,
310 max: Vec3::ZERO,
311 };
312
313 pub fn new(min: Vec3, max: Vec3) -> Self {
315 Self {
316 min: min.min(max),
317 max: min.max(max),
318 }
319 }
320
321 pub fn from_center_size(center: Vec3, size: Vec3) -> Self {
323 let half_size = size * 0.5;
324 Self::new(center - half_size, center + half_size)
325 }
326
327 pub fn size(&self) -> Vec3 {
329 self.max - self.min
330 }
331
332 pub fn center(&self) -> Vec3 {
334 (self.min + self.max) * 0.5
335 }
336
337 pub fn volume(&self) -> f32 {
339 let size = self.size();
340 size.x * size.y * size.z
341 }
342
343 pub fn contains(&self, point: Vec3) -> bool {
345 point.x >= self.min.x && point.x <= self.max.x &&
346 point.y >= self.min.y && point.y <= self.max.y &&
347 point.z >= self.min.z && point.z <= self.max.z
348 }
349
350 pub fn intersects(&self, other: &Bounds3D) -> bool {
352 self.min.x <= other.max.x && self.max.x >= other.min.x &&
353 self.min.y <= other.max.y && self.max.y >= other.min.y &&
354 self.min.z <= other.max.z && self.max.z >= other.min.z
355 }
356
357 pub fn expand_to_include(&mut self, point: Vec3) {
359 self.min = self.min.min(point);
360 self.max = self.max.max(point);
361 }
362
363 pub fn intersection(&self, other: &Bounds3D) -> Option<Bounds3D> {
365 if !self.intersects(other) {
366 return None;
367 }
368
369 Some(Bounds3D::new(
370 self.min.max(other.min),
371 self.max.min(other.max),
372 ))
373 }
374
375 pub fn union(&self, other: &Bounds3D) -> Bounds3D {
377 Bounds3D::new(
378 self.min.min(other.min),
379 self.max.max(other.max),
380 )
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387 use approx::assert_relative_eq;
388
389 #[test]
390 fn test_rect_creation() {
391 let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 20.0));
392 assert_eq!(rect.width(), 10.0);
393 assert_eq!(rect.height(), 20.0);
394 assert_eq!(rect.area(), 200.0);
395 }
396
397 #[test]
398 fn test_rect_from_center_size() {
399 let rect = Rect::from_center_size(Vec2::new(5.0, 10.0), Vec2::new(10.0, 20.0));
400 assert_eq!(rect.center(), Vec2::new(5.0, 10.0));
401 assert_eq!(rect.size(), Vec2::new(10.0, 20.0));
402 }
403
404 #[test]
405 fn test_rect_contains() {
406 let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
407 assert!(rect.contains(Vec2::new(5.0, 5.0)));
408 assert!(rect.contains(Vec2::ZERO)); assert!(rect.contains(Vec2::new(10.0, 10.0))); assert!(!rect.contains(Vec2::new(15.0, 5.0)));
411 }
412
413 #[test]
414 fn test_rect_intersection() {
415 let rect1 = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
416 let rect2 = Rect::new(Vec2::new(5.0, 5.0), Vec2::new(15.0, 15.0));
417
418 assert!(rect1.intersects(&rect2));
419
420 let intersection = rect1.intersection(&rect2).unwrap();
421 assert_eq!(intersection.min, Vec2::new(5.0, 5.0));
422 assert_eq!(intersection.max, Vec2::new(10.0, 10.0));
423 }
424
425 #[test]
426 fn test_rect_union() {
427 let rect1 = Rect::new(Vec2::ZERO, Vec2::new(5.0, 5.0));
428 let rect2 = Rect::new(Vec2::new(3.0, 3.0), Vec2::new(8.0, 8.0));
429
430 let union = rect1.union(&rect2);
431 assert_eq!(union.min, Vec2::ZERO);
432 assert_eq!(union.max, Vec2::new(8.0, 8.0));
433 }
434
435 #[test]
436 fn test_circle_creation() {
437 let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
438 assert_eq!(circle.center, Vec2::new(5.0, 5.0));
439 assert_eq!(circle.radius, 3.0);
440 assert_relative_eq!(circle.area(), std::f32::consts::PI * 9.0, epsilon = 1e-6);
441 }
442
443 #[test]
444 fn test_circle_contains() {
445 let circle = Circle::new(Vec2::ZERO, 5.0);
446 assert!(circle.contains(Vec2::new(3.0, 4.0))); assert!(!circle.contains(Vec2::new(4.0, 4.0))); }
449
450 #[test]
451 fn test_circle_intersection() {
452 let circle1 = Circle::new(Vec2::ZERO, 5.0);
453 let circle2 = Circle::new(Vec2::new(8.0, 0.0), 5.0);
454
455 assert!(circle1.intersects(&circle2)); let circle3 = Circle::new(Vec2::new(12.0, 0.0), 5.0);
458 assert!(!circle1.intersects(&circle3)); }
460
461 #[test]
462 fn test_rect_circle_intersection() {
463 let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
464 let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
465
466 assert!(rect.intersects_circle(&circle));
467 assert!(circle.intersects_rect(&rect));
468 }
469
470 #[test]
471 fn test_bounds3d() {
472 let bounds = Bounds3D::from_center_size(Vec3::ZERO, Vec3::ONE);
473 assert_eq!(bounds.center(), Vec3::ZERO);
474 assert_eq!(bounds.volume(), 1.0);
475 assert!(bounds.contains(Vec3::new(0.4, 0.4, 0.4)));
476 assert!(!bounds.contains(Vec3::new(0.6, 0.6, 0.6)));
477 }
478
479 #[test]
480 fn test_rect_expand() {
481 let rect = Rect::new(Vec2::new(2.0, 2.0), Vec2::new(8.0, 8.0));
482 let expanded = rect.expand(1.0);
483
484 assert_eq!(expanded.min, Vec2::new(1.0, 1.0));
485 assert_eq!(expanded.max, Vec2::new(9.0, 9.0));
486 }
487
488 #[test]
489 fn test_rect_auto_correct() {
490 let rect = Rect::new(Vec2::new(10.0, 10.0), Vec2::ZERO);
492 assert_eq!(rect.min, Vec2::ZERO);
493 assert_eq!(rect.max, Vec2::new(10.0, 10.0));
494 }
495
496 #[test]
497 fn test_rect_zero() {
498 let rect = Rect::ZERO;
499 assert_eq!(rect.width(), 0.0);
500 assert_eq!(rect.height(), 0.0);
501 assert_eq!(rect.area(), 0.0);
502 assert!(rect.contains(Vec2::ZERO));
503 }
504
505 #[test]
506 fn test_rect_from_position_size() {
507 let rect = Rect::from_position_size(Vec2::new(2.0, 3.0), Vec2::new(4.0, 5.0));
508 assert_eq!(rect.min, Vec2::new(2.0, 3.0));
509 assert_eq!(rect.max, Vec2::new(6.0, 8.0));
510 assert_eq!(rect.width(), 4.0);
511 assert_eq!(rect.height(), 5.0);
512 }
513
514 #[test]
515 fn test_rect_perimeter() {
516 let rect = Rect::new(Vec2::ZERO, Vec2::new(3.0, 4.0));
517 assert_eq!(rect.perimeter(), 14.0);
518 }
519
520 #[test]
521 fn test_rect_no_intersection() {
522 let rect1 = Rect::new(Vec2::ZERO, Vec2::new(1.0, 1.0));
523 let rect2 = Rect::new(Vec2::new(5.0, 5.0), Vec2::new(6.0, 6.0));
524 assert!(!rect1.intersects(&rect2));
525 assert!(rect1.intersection(&rect2).is_none());
526 }
527
528 #[test]
529 fn test_rect_expand_to_include() {
530 let mut rect = Rect::new(Vec2::new(1.0, 1.0), Vec2::new(3.0, 3.0));
531 rect.expand_to_include(Vec2::new(5.0, 0.0));
532 assert_eq!(rect.min, Vec2::new(1.0, 0.0));
533 assert_eq!(rect.max, Vec2::new(5.0, 3.0));
534 }
535
536 #[test]
537 fn test_rect_expand_negative() {
538 let rect = Rect::new(Vec2::new(0.0, 0.0), Vec2::new(10.0, 10.0));
539 let contracted = rect.expand(-2.0);
540 assert_eq!(contracted.min, Vec2::new(2.0, 2.0));
541 assert_eq!(contracted.max, Vec2::new(8.0, 8.0));
542 }
543
544 #[test]
545 fn test_rect_is_valid() {
546 let valid = Rect::new(Vec2::ZERO, Vec2::ONE);
547 assert!(valid.is_valid());
548
549 let also_valid = Rect::ZERO;
550 assert!(also_valid.is_valid());
551 }
552
553 #[test]
554 fn test_circle_negative_radius() {
555 let circle = Circle::new(Vec2::ZERO, -5.0);
556 assert_eq!(circle.radius, 0.0); }
558
559 #[test]
560 fn test_circle_zero_radius() {
561 let circle = Circle::new(Vec2::new(1.0, 1.0), 0.0);
562 assert_eq!(circle.area(), 0.0);
563 assert_eq!(circle.circumference(), 0.0);
564 assert!(circle.contains(Vec2::new(1.0, 1.0))); }
566
567 #[test]
568 fn test_circle_bounding_rect() {
569 let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
570 let bounding = circle.bounding_rect();
571 assert_eq!(bounding.min, Vec2::new(2.0, 2.0));
572 assert_eq!(bounding.max, Vec2::new(8.0, 8.0));
573 }
574
575 #[test]
576 fn test_circle_touching() {
577 let c1 = Circle::new(Vec2::ZERO, 3.0);
579 let c2 = Circle::new(Vec2::new(6.0, 0.0), 3.0);
580 assert!(c1.intersects(&c2)); }
582
583 #[test]
584 fn test_bounds3d_intersection() {
585 let b1 = Bounds3D::new(Vec3::ZERO, Vec3::splat(5.0));
586 let b2 = Bounds3D::new(Vec3::splat(3.0), Vec3::splat(8.0));
587
588 let intersection = b1.intersection(&b2).unwrap();
589 assert_eq!(intersection.min, Vec3::splat(3.0));
590 assert_eq!(intersection.max, Vec3::splat(5.0));
591 }
592
593 #[test]
594 fn test_bounds3d_no_intersection() {
595 let b1 = Bounds3D::new(Vec3::ZERO, Vec3::ONE);
596 let b2 = Bounds3D::new(Vec3::splat(5.0), Vec3::splat(6.0));
597 assert!(!b1.intersects(&b2));
598 assert!(b1.intersection(&b2).is_none());
599 }
600
601 #[test]
602 fn test_bounds3d_union() {
603 let b1 = Bounds3D::new(Vec3::ZERO, Vec3::splat(2.0));
604 let b2 = Bounds3D::new(Vec3::splat(3.0), Vec3::splat(5.0));
605 let union = b1.union(&b2);
606 assert_eq!(union.min, Vec3::ZERO);
607 assert_eq!(union.max, Vec3::splat(5.0));
608 }
609
610 #[test]
611 fn test_bounds3d_expand_to_include() {
612 let mut bounds = Bounds3D::new(Vec3::ONE, Vec3::splat(3.0));
613 bounds.expand_to_include(Vec3::new(-1.0, 5.0, 2.0));
614 assert_eq!(bounds.min, Vec3::new(-1.0, 1.0, 1.0));
615 assert_eq!(bounds.max, Vec3::new(3.0, 5.0, 3.0));
616 }
617
618 #[test]
619 fn test_bounds3d_contains_boundary() {
620 let bounds = Bounds3D::new(Vec3::ZERO, Vec3::splat(10.0));
621 assert!(bounds.contains(Vec3::ZERO));
623 assert!(bounds.contains(Vec3::splat(10.0)));
624 assert!(!bounds.contains(Vec3::new(10.001, 5.0, 5.0)));
626 }
627}