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 {
85 point.xcoord >= self.min.xcoord
86 && point.xcoord <= self.max.xcoord
87 && point.ycoord >= self.min.ycoord
88 && point.ycoord <= self.max.ycoord
89 }
90
91 pub fn intersect(&self, other: &Self) -> Option<Self>
97 where
98 T: std::ops::Add<Output = T>,
99 {
100 if !self.overlaps(other) {
101 return None;
102 }
103
104 Some(Rectangle::new(
105 Point::new(
106 self.min.xcoord.max(other.min.xcoord),
107 self.min.ycoord.max(other.min.ycoord),
108 ),
109 Point::new(
110 self.max.xcoord.min(other.max.xcoord),
111 self.max.ycoord.min(other.max.ycoord),
112 ),
113 ))
114 }
115
116 pub fn area(&self) -> T
120 where
121 T: std::ops::Sub<Output = T> + std::ops::Mul<Output = T>,
122 {
123 let width = self.max.xcoord - self.min.xcoord;
124 let height = self.max.ycoord - self.min.ycoord;
125 width * height
126 }
127
128 pub fn bounding_rect(&self, other: &Self) -> Self {
132 Rectangle::new(
133 Point::new(
134 self.min.xcoord.min(other.min.xcoord),
135 self.min.ycoord.min(other.min.ycoord),
136 ),
137 Point::new(
138 self.max.xcoord.max(other.max.xcoord),
139 self.max.ycoord.max(other.max.ycoord),
140 ),
141 )
142 }
143
144 pub fn to_polygon(&self) -> Polygon<T>
146 where
147 T: Clone + std::ops::Add<Output = T> + num_traits::Num + std::ops::AddAssign + Ord,
148 {
149 Polygon::new(&[
150 self.min,
151 Point::new(self.max.xcoord, self.min.ycoord),
152 self.max,
153 Point::new(self.min.xcoord, self.max.ycoord),
154 ])
155 }
156}
157
158pub fn detect_overlap<T>(rectangles: &[Rectangle<T>]) -> Option<(usize, usize)>
189where
190 T: Copy + Ord,
191{
192 if rectangles.len() < 2 {
193 return None;
194 }
195
196 let mut events: Vec<(T, bool, usize)> = Vec::with_capacity(rectangles.len() * 2);
198 for (idx, rect) in rectangles.iter().enumerate() {
199 events.push((rect.min.xcoord, true, idx));
200 events.push((rect.max.xcoord, false, idx));
201 }
202
203 events.sort_by_key(|a| a.0);
204
205 let mut active: Vec<(usize, T, T)> = Vec::new();
207
208 for &(_x, is_start, idx) in &events {
209 let rect = &rectangles[idx];
210
211 if is_start {
212 for &(other_idx, other_y_min, other_y_max) in &active {
214 if rect.min.ycoord <= other_y_max && other_y_min <= rect.max.ycoord {
215 return Some((other_idx, idx));
216 }
217 }
218 active.push((idx, rect.min.ycoord, rect.max.ycoord));
219 } else {
220 for i in 0..active.len() {
222 if active[i].0 == idx {
223 active.swap_remove(i);
224 break;
225 }
226 }
227 }
228 }
229
230 None
231}
232
233pub fn manhattan_distance<T>(p1: &Point<T, T>, p2: &Point<T, T>) -> T
256where
257 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
258{
259 let dx = if p1.xcoord > p2.xcoord {
260 p1.xcoord - p2.xcoord
261 } else {
262 p2.xcoord - p1.xcoord
263 };
264 let dy = if p1.ycoord > p2.ycoord {
265 p1.ycoord - p2.ycoord
266 } else {
267 p2.ycoord - p1.ycoord
268 };
269 dx + dy
270}
271
272pub fn check_spacing<T>(rect1: &Rectangle<T>, rect2: &Rectangle<T>, min_spacing: T) -> bool
296where
297 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
298{
299 if rect1.overlaps(rect2) {
300 return false;
301 }
302
303 let horiz_spacing = if rect1.max.xcoord <= rect2.min.xcoord {
305 rect2.min.xcoord - rect1.max.xcoord
306 } else if rect2.max.xcoord <= rect1.min.xcoord {
307 rect1.min.xcoord - rect2.max.xcoord
308 } else {
309 T::clone(&min_spacing) };
311
312 let vert_spacing = if rect1.max.ycoord <= rect2.min.ycoord {
314 rect2.min.ycoord - rect1.max.ycoord
315 } else if rect2.max.ycoord <= rect1.min.ycoord {
316 rect1.min.ycoord - rect2.max.ycoord
317 } else {
318 T::clone(&min_spacing) };
320
321 horiz_spacing >= min_spacing && vert_spacing >= min_spacing
322}
323
324pub fn bounding_rect<T>(rects: &[Rectangle<T>]) -> Option<Rectangle<T>>
347where
348 T: Ord + Copy + std::ops::Add<Output = T>,
349{
350 if rects.is_empty() {
351 return None;
352 }
353
354 let mut min_x = rects[0].min.xcoord;
355 let mut min_y = rects[0].min.ycoord;
356 let mut max_x = rects[0].max.xcoord;
357 let mut max_y = rects[0].max.ycoord;
358
359 for rect in &rects[1..] {
360 min_x = min_x.min(rect.min.xcoord);
361 min_y = min_y.min(rect.min.ycoord);
362 max_x = max_x.max(rect.max.xcoord);
363 max_y = max_y.max(rect.max.ycoord);
364 }
365
366 Some(Rectangle::new(
367 Point::new(min_x, min_y),
368 Point::new(max_x, max_y),
369 ))
370}
371
372pub fn total_area(rects: &[Rectangle<i32>]) -> i32 {
389 if rects.is_empty() {
390 return 0;
391 }
392
393 let mut total = 0;
394 for (i, rect) in rects.iter().enumerate() {
395 total += rect.area();
396 for other in &rects[..i] {
398 if let Some(intersection) = rect.intersect(other) {
399 total -= intersection.area();
400 }
401 }
402 }
403
404 total
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn test_rectangle_creation() {
413 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
414 assert_eq!(rect.min, Point::new(0, 0));
415 assert_eq!(rect.max, Point::new(10, 20));
416 }
417
418 #[test]
419 fn test_rectangle_overlap() {
420 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
421 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
422 assert!(rect1.overlaps(&rect2));
423
424 let rect3 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
425 assert!(!rect1.overlaps(&rect3));
426 }
427
428 #[test]
429 fn test_rectangle_area() {
430 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
431 assert_eq!(rect.area(), 200);
432 }
433
434 #[test]
435 fn test_manhattan_distance() {
436 let p1 = Point::new(0, 0);
437 let p2 = Point::new(3, 4);
438 assert_eq!(manhattan_distance(&p1, &p2), 7);
439
440 let p3 = Point::new(5, 0);
441 assert_eq!(manhattan_distance(&p1, &p3), 5);
442 }
443
444 #[test]
445 fn test_check_spacing() {
446 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
447 let rect2 = Rectangle::new(Point::new(10, 0), Point::new(20, 10));
448 assert!(!check_spacing(&rect1, &rect2, 1));
449
450 let rect3 = Rectangle::new(Point::new(12, 0), Point::new(22, 10));
451 assert!(check_spacing(&rect1, &rect3, 1));
452 }
453
454 #[test]
455 fn test_bounding_rect() {
456 let rects = vec![
457 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
458 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
459 ];
460
461 let bbox = bounding_rect(&rects).unwrap();
462 assert_eq!(bbox.min, Point::new(0, 0));
463 assert_eq!(bbox.max, Point::new(15, 15));
464 }
465
466 #[test]
467 fn test_total_area() {
468 let rects = vec![
469 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
470 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
471 ];
472
473 let area = total_area(&rects);
474 assert_eq!(area, 175);
475 }
476
477 #[test]
478 fn test_from_dimensions() {
479 let origin = Point::new(5, 10);
480 let rect = Rectangle::from_dimensions(origin, 20, 30);
481 assert_eq!(rect.min, origin);
482 assert_eq!(rect.max, Point::new(25, 40));
483 }
484
485 #[test]
486 fn test_rectangle_contains() {
487 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
488 let inner = Rectangle::new(Point::new(2, 2), Point::new(8, 8));
489 let outer = Rectangle::new(Point::new(-5, -5), Point::new(15, 15));
490
491 assert!(rect.contains(&inner));
492 assert!(!rect.contains(&outer));
493 }
494
495 #[test]
496 fn test_rectangle_contains_point() {
497 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
498 let inside = Point::new(5, 5);
499 let outside = Point::new(15, 15);
500 let on_edge = Point::new(10, 5);
501
502 assert!(rect.contains_point(&inside));
503 assert!(!rect.contains_point(&outside));
504 assert!(rect.contains_point(&on_edge));
505 }
506
507 #[test]
508 fn test_bounding_rect_method() {
509 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
510 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
511 let bbox = rect1.bounding_rect(&rect2);
512
513 assert_eq!(bbox.min, Point::new(0, 0));
514 assert_eq!(bbox.max, Point::new(15, 15));
515 }
516
517 #[test]
518 fn test_to_polygon() {
519 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
520 let polygon = rect.to_polygon();
521
522 assert_eq!(polygon.vertices().len(), 4);
523 assert!(polygon.is_rectilinear());
524 }
525
526 #[test]
527 fn test_bounding_rect_empty() {
528 let rects: Vec<Rectangle<i32>> = vec![];
529 assert!(bounding_rect(&rects).is_none());
530 }
531
532 #[test]
533 fn test_total_area_empty() {
534 let rects: Vec<Rectangle<i32>> = vec![];
535 assert_eq!(total_area(&rects), 0);
536 }
537
538 #[test]
539 fn test_manhattan_distance_reverse() {
540 let p1 = Point::new(5, 3);
542 let p2 = Point::new(1, 0);
543 assert_eq!(manhattan_distance(&p1, &p2), 7); }
545
546 #[test]
547 fn test_manhattan_distance_same() {
548 let p1 = Point::new(5, 5);
550 let p2 = Point::new(5, 5);
551 assert_eq!(manhattan_distance(&p1, &p2), 0);
552 }
553
554 #[test]
555 fn test_check_spacing_vertical() {
556 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
558 let rect2 = Rectangle::new(Point::new(0, 10), Point::new(10, 20));
559 assert!(!check_spacing(&rect1, &rect2, 1)); let rect3 = Rectangle::new(Point::new(0, 12), Point::new(10, 22));
562 assert!(check_spacing(&rect1, &rect3, 1)); }
564
565 #[test]
566 fn test_check_spacing_overlapping() {
567 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
569 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
570 assert!(!check_spacing(&rect1, &rect2, 1)); }
572
573 #[test]
574 fn test_rectangle_intersect_non_overlapping() {
575 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
576 let r2 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
577 assert!(r1.intersect(&r2).is_none());
578 }
579
580 #[test]
581 fn test_detect_overlap_empty() {
582 let rects: Vec<Rectangle<i32>> = vec![];
583 assert!(detect_overlap(&rects).is_none());
584 }
585
586 #[test]
587 fn test_detect_overlap_single() {
588 let rects = vec![Rectangle::new(Point::new(0, 0), Point::new(10, 10))];
589 assert!(detect_overlap(&rects).is_none());
590 }
591
592 #[test]
593 fn test_detect_overlap_non_overlapping() {
594 let rects = vec![
595 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
596 Rectangle::new(Point::new(20, 20), Point::new(30, 30)),
597 ];
598 assert!(detect_overlap(&rects).is_none());
599 }
600
601 #[test]
602 fn test_detect_overlap_three_rects() {
603 let rects = vec![
605 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
606 Rectangle::new(Point::new(15, 15), Point::new(25, 25)),
607 Rectangle::new(Point::new(5, 5), Point::new(12, 12)), ];
609 let result = detect_overlap(&rects);
610 assert!(result.is_some());
611 let (i, j) = result.unwrap();
613 assert!((i == 0 && j == 2) || (i == 2 && j == 0));
614 }
615
616 #[test]
617 fn test_check_spacing_x_overlap_y_separate() {
618 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
620 let r2 = Rectangle::new(Point::new(5, 20), Point::new(15, 30));
621 assert!(check_spacing(&r1, &r2, 10));
623 assert!(!check_spacing(&r1, &r2, 11));
624 }
625
626 #[test]
627 fn test_check_spacing_vertical_overlap_horizontal_separate() {
628 let r1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
630 let r2 = Rectangle::new(Point::new(20, 5), Point::new(30, 15));
631 assert!(check_spacing(&r1, &r2, 10));
632 assert!(!check_spacing(&r1, &r2, 11));
633 }
634}