1use crate::{Point, Polygon};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Rectangle<T> {
11 pub min: Point<T, T>,
13 pub max: Point<T, T>,
15}
16
17impl<T: Clone + Ord + Copy + std::ops::Add<Output = T>> Rectangle<T> {
18 pub fn new(min: Point<T, T>, max: Point<T, T>) -> Self {
36 assert!(min.xcoord <= max.xcoord && min.ycoord <= max.ycoord);
37 Rectangle { min, max }
38 }
39
40 pub fn from_dimensions(origin: Point<T, T>, width: T, height: T) -> Self {
42 Rectangle::new(
43 origin,
44 Point::new(origin.xcoord + width, origin.ycoord + height),
45 )
46 }
47
48 pub fn overlaps(&self, other: &Self) -> bool {
67 self.min.xcoord <= other.max.xcoord
68 && self.max.xcoord >= other.min.xcoord
69 && self.min.ycoord <= other.max.ycoord
70 && self.max.ycoord >= other.min.ycoord
71 }
72
73 pub fn contains(&self, other: &Self) -> bool {
77 self.min.xcoord <= other.min.xcoord
78 && self.max.xcoord >= other.max.xcoord
79 && self.min.ycoord <= other.min.ycoord
80 && self.max.ycoord >= other.max.ycoord
81 }
82
83 pub fn contains_point(&self, point: &Point<T, T>) -> bool {
87 point.xcoord >= self.min.xcoord
88 && point.xcoord <= self.max.xcoord
89 && point.ycoord >= self.min.ycoord
90 && point.ycoord <= self.max.ycoord
91 }
92
93 pub fn intersect(&self, other: &Self) -> Option<Self>
99 where
100 T: std::ops::Add<Output = T>,
101 {
102 if !self.overlaps(other) {
103 return None;
104 }
105
106 Some(Rectangle::new(
107 Point::new(
108 self.min.xcoord.max(other.min.xcoord),
109 self.min.ycoord.max(other.min.ycoord),
110 ),
111 Point::new(
112 self.max.xcoord.min(other.max.xcoord),
113 self.max.ycoord.min(other.max.ycoord),
114 ),
115 ))
116 }
117
118 pub fn area(&self) -> T
122 where
123 T: std::ops::Sub<Output = T> + std::ops::Mul<Output = T>,
124 {
125 let width = self.max.xcoord - self.min.xcoord;
126 let height = self.max.ycoord - self.min.ycoord;
127 width * height
128 }
129
130 pub fn bounding_rect(&self, other: &Self) -> Self {
134 Rectangle::new(
135 Point::new(
136 self.min.xcoord.min(other.min.xcoord),
137 self.min.ycoord.min(other.min.ycoord),
138 ),
139 Point::new(
140 self.max.xcoord.max(other.max.xcoord),
141 self.max.ycoord.max(other.max.ycoord),
142 ),
143 )
144 }
145
146 pub fn to_polygon(&self) -> Polygon<T>
148 where
149 T: Clone + std::ops::Add<Output = T> + num_traits::Num + std::ops::AddAssign + Ord,
150 {
151 Polygon::new(&[
152 self.min,
153 Point::new(self.max.xcoord, self.min.ycoord),
154 self.max,
155 Point::new(self.min.xcoord, self.max.ycoord),
156 ])
157 }
158}
159
160pub fn detect_overlap<T>(rectangles: &[Rectangle<T>]) -> Option<(usize, usize)>
195where
196 T: Copy + Ord,
197{
198 if rectangles.len() < 2 {
199 return None;
200 }
201
202 let mut events: Vec<(T, bool, usize)> = Vec::with_capacity(rectangles.len() * 2);
204 for (idx, rect) in rectangles.iter().enumerate() {
205 events.push((rect.min.xcoord, true, idx));
206 events.push((rect.max.xcoord, false, idx));
207 }
208
209 events.sort_by_key(|a| a.0);
210
211 let mut active: Vec<(usize, T, T)> = Vec::new();
213
214 for &(_x, is_start, idx) in &events {
215 let rect = &rectangles[idx];
216
217 if is_start {
218 for &(other_idx, other_y_min, other_y_max) in &active {
220 if rect.min.ycoord <= other_y_max && other_y_min <= rect.max.ycoord {
221 return Some((other_idx, idx));
222 }
223 }
224 active.push((idx, rect.min.ycoord, rect.max.ycoord));
225 } else {
226 for i in 0..active.len() {
228 if active[i].0 == idx {
229 active.swap_remove(i);
230 break;
231 }
232 }
233 }
234 }
235
236 None
237}
238
239pub fn manhattan_distance<T>(p1: &Point<T, T>, p2: &Point<T, T>) -> T
262where
263 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
264{
265 let dx = if p1.xcoord > p2.xcoord {
266 p1.xcoord - p2.xcoord
267 } else {
268 p2.xcoord - p1.xcoord
269 };
270 let dy = if p1.ycoord > p2.ycoord {
271 p1.ycoord - p2.ycoord
272 } else {
273 p2.ycoord - p1.ycoord
274 };
275 dx + dy
276}
277
278pub fn check_spacing<T>(rect1: &Rectangle<T>, rect2: &Rectangle<T>, min_spacing: T) -> bool
307where
308 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
309{
310 if rect1.overlaps(rect2) {
311 return false;
312 }
313
314 let horiz_spacing = if rect1.max.xcoord <= rect2.min.xcoord {
316 rect2.min.xcoord - rect1.max.xcoord
317 } else if rect2.max.xcoord <= rect1.min.xcoord {
318 rect1.min.xcoord - rect2.max.xcoord
319 } else {
320 T::clone(&min_spacing) };
322
323 let vert_spacing = if rect1.max.ycoord <= rect2.min.ycoord {
325 rect2.min.ycoord - rect1.max.ycoord
326 } else if rect2.max.ycoord <= rect1.min.ycoord {
327 rect1.min.ycoord - rect2.max.ycoord
328 } else {
329 T::clone(&min_spacing) };
331
332 horiz_spacing >= min_spacing && vert_spacing >= min_spacing
333}
334
335pub fn bounding_rect<T>(rects: &[Rectangle<T>]) -> Option<Rectangle<T>>
358where
359 T: Ord + Copy + std::ops::Add<Output = T>,
360{
361 if rects.is_empty() {
362 return None;
363 }
364
365 let mut min_x = rects[0].min.xcoord;
366 let mut min_y = rects[0].min.ycoord;
367 let mut max_x = rects[0].max.xcoord;
368 let mut max_y = rects[0].max.ycoord;
369
370 for rect in &rects[1..] {
371 min_x = min_x.min(rect.min.xcoord);
372 min_y = min_y.min(rect.min.ycoord);
373 max_x = max_x.max(rect.max.xcoord);
374 max_y = max_y.max(rect.max.ycoord);
375 }
376
377 Some(Rectangle::new(
378 Point::new(min_x, min_y),
379 Point::new(max_x, max_y),
380 ))
381}
382
383pub fn total_area(rects: &[Rectangle<i32>]) -> i32 {
402 if rects.is_empty() {
403 return 0;
404 }
405
406 let mut total = 0;
407 for (i, rect) in rects.iter().enumerate() {
408 total += rect.area();
409 for other in &rects[..i] {
411 if let Some(intersection) = rect.intersect(other) {
412 total -= intersection.area();
413 }
414 }
415 }
416
417 total
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_rectangle_creation() {
426 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
427 assert_eq!(rect.min, Point::new(0, 0));
428 assert_eq!(rect.max, Point::new(10, 20));
429 }
430
431 #[test]
432 fn test_rectangle_overlap() {
433 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
434 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
435 assert!(rect1.overlaps(&rect2));
436
437 let rect3 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
438 assert!(!rect1.overlaps(&rect3));
439 }
440
441 #[test]
442 fn test_rectangle_area() {
443 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
444 assert_eq!(rect.area(), 200);
445 }
446
447 #[test]
448 fn test_manhattan_distance() {
449 let p1 = Point::new(0, 0);
450 let p2 = Point::new(3, 4);
451 assert_eq!(manhattan_distance(&p1, &p2), 7);
452
453 let p3 = Point::new(5, 0);
454 assert_eq!(manhattan_distance(&p1, &p3), 5);
455 }
456
457 #[test]
458 fn test_check_spacing() {
459 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
460 let rect2 = Rectangle::new(Point::new(10, 0), Point::new(20, 10));
461 assert!(!check_spacing(&rect1, &rect2, 1));
462
463 let rect3 = Rectangle::new(Point::new(12, 0), Point::new(22, 10));
464 assert!(check_spacing(&rect1, &rect3, 1));
465 }
466
467 #[test]
468 fn test_bounding_rect() {
469 let rects = vec![
470 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
471 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
472 ];
473
474 let bbox = bounding_rect(&rects).unwrap();
475 assert_eq!(bbox.min, Point::new(0, 0));
476 assert_eq!(bbox.max, Point::new(15, 15));
477 }
478
479 #[test]
480 fn test_total_area() {
481 let rects = vec![
482 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
483 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
484 ];
485
486 let area = total_area(&rects);
487 assert_eq!(area, 175);
488 }
489
490 #[test]
491 fn test_from_dimensions() {
492 let origin = Point::new(5, 10);
493 let rect = Rectangle::from_dimensions(origin, 20, 30);
494 assert_eq!(rect.min, origin);
495 assert_eq!(rect.max, Point::new(25, 40));
496 }
497
498 #[test]
499 fn test_rectangle_contains() {
500 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
501 let inner = Rectangle::new(Point::new(2, 2), Point::new(8, 8));
502 let outer = Rectangle::new(Point::new(-5, -5), Point::new(15, 15));
503
504 assert!(rect.contains(&inner));
505 assert!(!rect.contains(&outer));
506 }
507
508 #[test]
509 fn test_rectangle_contains_point() {
510 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
511 let inside = Point::new(5, 5);
512 let outside = Point::new(15, 15);
513 let on_edge = Point::new(10, 5);
514
515 assert!(rect.contains_point(&inside));
516 assert!(!rect.contains_point(&outside));
517 assert!(rect.contains_point(&on_edge));
518 }
519
520 #[test]
521 fn test_bounding_rect_method() {
522 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
523 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
524 let bbox = rect1.bounding_rect(&rect2);
525
526 assert_eq!(bbox.min, Point::new(0, 0));
527 assert_eq!(bbox.max, Point::new(15, 15));
528 }
529
530 #[test]
531 fn test_to_polygon() {
532 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
533 let polygon = rect.to_polygon();
534
535 assert_eq!(polygon.vertices().len(), 4);
536 assert!(polygon.is_rectilinear());
537 }
538
539 #[test]
540 fn test_bounding_rect_empty() {
541 let rects: Vec<Rectangle<i32>> = vec![];
542 assert!(bounding_rect(&rects).is_none());
543 }
544
545 #[test]
546 fn test_total_area_empty() {
547 let rects: Vec<Rectangle<i32>> = vec![];
548 assert_eq!(total_area(&rects), 0);
549 }
550
551 #[test]
552 fn test_manhattan_distance_reverse() {
553 let p1 = Point::new(5, 3);
555 let p2 = Point::new(1, 0);
556 assert_eq!(manhattan_distance(&p1, &p2), 7); }
558
559 #[test]
560 fn test_manhattan_distance_same() {
561 let p1 = Point::new(5, 5);
563 let p2 = Point::new(5, 5);
564 assert_eq!(manhattan_distance(&p1, &p2), 0);
565 }
566
567 #[test]
568 fn test_check_spacing_vertical() {
569 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
571 let rect2 = Rectangle::new(Point::new(0, 10), Point::new(10, 20));
572 assert!(!check_spacing(&rect1, &rect2, 1)); let rect3 = Rectangle::new(Point::new(0, 12), Point::new(10, 22));
575 assert!(check_spacing(&rect1, &rect3, 1)); }
577
578 #[test]
579 fn test_check_spacing_overlapping() {
580 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
582 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
583 assert!(!check_spacing(&rect1, &rect2, 1)); }
585
586 #[test]
587 fn test_rectangle_intersect_non_overlapping() {
588 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
589 let r2 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
590 assert!(r1.intersect(&r2).is_none());
591 }
592
593 #[test]
594 fn test_detect_overlap_empty() {
595 let rects: Vec<Rectangle<i32>> = vec![];
596 assert!(detect_overlap(&rects).is_none());
597 }
598
599 #[test]
600 fn test_detect_overlap_single() {
601 let rects = vec![Rectangle::new(Point::new(0, 0), Point::new(10, 10))];
602 assert!(detect_overlap(&rects).is_none());
603 }
604
605 #[test]
606 fn test_detect_overlap_non_overlapping() {
607 let rects = vec![
608 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
609 Rectangle::new(Point::new(20, 20), Point::new(30, 30)),
610 ];
611 assert!(detect_overlap(&rects).is_none());
612 }
613
614 #[test]
615 fn test_detect_overlap_three_rects() {
616 let rects = vec![
618 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
619 Rectangle::new(Point::new(15, 15), Point::new(25, 25)),
620 Rectangle::new(Point::new(5, 5), Point::new(12, 12)), ];
622 let result = detect_overlap(&rects);
623 assert!(result.is_some());
624 let (i, j) = result.unwrap();
626 assert!((i == 0 && j == 2) || (i == 2 && j == 0));
627 }
628
629 #[test]
630 fn test_check_spacing_x_overlap_y_separate() {
631 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
633 let r2 = Rectangle::new(Point::new(5, 20), Point::new(15, 30));
634 assert!(check_spacing(&r1, &r2, 10));
636 assert!(!check_spacing(&r1, &r2, 11));
637 }
638
639 #[test]
640 fn test_check_spacing_vertical_overlap_horizontal_separate() {
641 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
643 let r2 = Rectangle::new(Point::new(20, 5), Point::new(30, 15));
644 assert!(check_spacing(&r1, &r2, 10));
645 assert!(!check_spacing(&r1, &r2, 11));
646 }
647}