1use crate::{Point, Polygon};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Rectangle<T> {
10 pub min: Point<T, T>,
11 pub max: Point<T, T>,
12}
13
14impl<T: Clone + Ord + Copy + std::ops::Add<Output = T>> Rectangle<T> {
15 pub fn new(min: Point<T, T>, max: Point<T, T>) -> Self {
33 assert!(min.xcoord <= max.xcoord && min.ycoord <= max.ycoord);
34 Rectangle { min, max }
35 }
36
37 pub fn from_dimensions(origin: Point<T, T>, width: T, height: T) -> Self {
39 Rectangle::new(
40 origin,
41 Point::new(origin.xcoord + width, origin.ycoord + height),
42 )
43 }
44
45 pub fn overlaps(&self, other: &Self) -> bool {
60 self.min.xcoord <= other.max.xcoord
61 && self.max.xcoord >= other.min.xcoord
62 && self.min.ycoord <= other.max.ycoord
63 && self.max.ycoord >= other.min.ycoord
64 }
65
66 pub fn contains(&self, other: &Self) -> bool {
68 self.min.xcoord <= other.min.xcoord
69 && self.max.xcoord >= other.max.xcoord
70 && self.min.ycoord <= other.min.ycoord
71 && self.max.ycoord >= other.max.ycoord
72 }
73
74 pub fn contains_point(&self, point: &Point<T, T>) -> bool {
76 point.xcoord >= self.min.xcoord
77 && point.xcoord <= self.max.xcoord
78 && point.ycoord >= self.min.ycoord
79 && point.ycoord <= self.max.ycoord
80 }
81
82 pub fn intersect(&self, other: &Self) -> Option<Self>
86 where
87 T: std::ops::Add<Output = T>,
88 {
89 if !self.overlaps(other) {
90 return None;
91 }
92
93 Some(Rectangle::new(
94 Point::new(
95 self.min.xcoord.max(other.min.xcoord),
96 self.min.ycoord.max(other.min.ycoord),
97 ),
98 Point::new(
99 self.max.xcoord.min(other.max.xcoord),
100 self.max.ycoord.min(other.max.ycoord),
101 ),
102 ))
103 }
104
105 pub fn area(&self) -> T
107 where
108 T: std::ops::Sub<Output = T> + std::ops::Mul<Output = T>,
109 {
110 let width = self.max.xcoord - self.min.xcoord;
111 let height = self.max.ycoord - self.min.ycoord;
112 width * height
113 }
114
115 pub fn bounding_rect(&self, other: &Self) -> Self {
117 Rectangle::new(
118 Point::new(
119 self.min.xcoord.min(other.min.xcoord),
120 self.min.ycoord.min(other.min.ycoord),
121 ),
122 Point::new(
123 self.max.xcoord.max(other.max.xcoord),
124 self.max.ycoord.max(other.max.ycoord),
125 ),
126 )
127 }
128
129 pub fn to_polygon(&self) -> Polygon<T>
131 where
132 T: Clone + std::ops::Add<Output = T> + num_traits::Num + std::ops::AddAssign + Ord,
133 {
134 Polygon::new(&[
135 self.min,
136 Point::new(self.max.xcoord, self.min.ycoord),
137 self.max,
138 Point::new(self.min.xcoord, self.max.ycoord),
139 ])
140 }
141}
142
143pub fn detect_overlap<T>(rectangles: &[Rectangle<T>]) -> Option<(usize, usize)>
174where
175 T: Copy + Ord,
176{
177 if rectangles.len() < 2 {
178 return None;
179 }
180
181 let mut events: Vec<(T, bool, usize)> = Vec::with_capacity(rectangles.len() * 2);
183 for (idx, rect) in rectangles.iter().enumerate() {
184 events.push((rect.min.xcoord, true, idx));
185 events.push((rect.max.xcoord, false, idx));
186 }
187
188 events.sort_by(|a, b| a.0.cmp(&b.0));
189
190 let mut active: Vec<(usize, T, T)> = Vec::new();
192
193 for &(_x, is_start, idx) in &events {
194 let rect = &rectangles[idx];
195
196 if is_start {
197 for &(other_idx, other_y_min, other_y_max) in &active {
199 if rect.min.ycoord <= other_y_max && other_y_min <= rect.max.ycoord {
200 return Some((other_idx, idx));
201 }
202 }
203 active.push((idx, rect.min.ycoord, rect.max.ycoord));
204 } else {
205 for i in 0..active.len() {
207 if active[i].0 == idx {
208 active.swap_remove(i);
209 break;
210 }
211 }
212 }
213 }
214
215 None
216}
217
218pub fn manhattan_distance<T>(p1: &Point<T, T>, p2: &Point<T, T>) -> T
239where
240 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
241{
242 let dx = if p1.xcoord > p2.xcoord {
243 p1.xcoord - p2.xcoord
244 } else {
245 p2.xcoord - p1.xcoord
246 };
247 let dy = if p1.ycoord > p2.ycoord {
248 p1.ycoord - p2.ycoord
249 } else {
250 p2.ycoord - p1.ycoord
251 };
252 dx + dy
253}
254
255pub fn check_spacing<T>(rect1: &Rectangle<T>, rect2: &Rectangle<T>, min_spacing: T) -> bool
279where
280 T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
281{
282 if rect1.overlaps(rect2) {
283 return false;
284 }
285
286 let horiz_spacing = if rect1.max.xcoord <= rect2.min.xcoord {
288 rect2.min.xcoord - rect1.max.xcoord
289 } else if rect2.max.xcoord <= rect1.min.xcoord {
290 rect1.min.xcoord - rect2.max.xcoord
291 } else {
292 T::clone(&min_spacing) };
294
295 let vert_spacing = if rect1.max.ycoord <= rect2.min.ycoord {
297 rect2.min.ycoord - rect1.max.ycoord
298 } else if rect2.max.ycoord <= rect1.min.ycoord {
299 rect1.min.ycoord - rect2.max.ycoord
300 } else {
301 T::clone(&min_spacing) };
303
304 horiz_spacing >= min_spacing && vert_spacing >= min_spacing
305}
306
307pub fn bounding_rect<T>(rects: &[Rectangle<T>]) -> Option<Rectangle<T>>
328where
329 T: Ord + Copy + std::ops::Add<Output = T>,
330{
331 if rects.is_empty() {
332 return None;
333 }
334
335 let mut min_x = rects[0].min.xcoord;
336 let mut min_y = rects[0].min.ycoord;
337 let mut max_x = rects[0].max.xcoord;
338 let mut max_y = rects[0].max.ycoord;
339
340 for rect in &rects[1..] {
341 min_x = min_x.min(rect.min.xcoord);
342 min_y = min_y.min(rect.min.ycoord);
343 max_x = max_x.max(rect.max.xcoord);
344 max_y = max_y.max(rect.max.ycoord);
345 }
346
347 Some(Rectangle::new(
348 Point::new(min_x, min_y),
349 Point::new(max_x, max_y),
350 ))
351}
352
353pub fn total_area(rects: &[Rectangle<i32>]) -> i32 {
370 if rects.is_empty() {
371 return 0;
372 }
373
374 let mut total = 0;
375 for (i, rect) in rects.iter().enumerate() {
376 total += rect.area();
377 for other in &rects[..i] {
379 if let Some(intersection) = rect.intersect(other) {
380 total -= intersection.area();
381 }
382 }
383 }
384
385 total
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_rectangle_creation() {
394 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
395 assert_eq!(rect.min, Point::new(0, 0));
396 assert_eq!(rect.max, Point::new(10, 20));
397 }
398
399 #[test]
400 fn test_rectangle_overlap() {
401 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
402 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
403 assert!(rect1.overlaps(&rect2));
404
405 let rect3 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
406 assert!(!rect1.overlaps(&rect3));
407 }
408
409 #[test]
410 fn test_rectangle_area() {
411 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
412 assert_eq!(rect.area(), 200);
413 }
414
415 #[test]
416 fn test_manhattan_distance() {
417 let p1 = Point::new(0, 0);
418 let p2 = Point::new(3, 4);
419 assert_eq!(manhattan_distance(&p1, &p2), 7);
420
421 let p3 = Point::new(5, 0);
422 assert_eq!(manhattan_distance(&p1, &p3), 5);
423 }
424
425 #[test]
426 fn test_check_spacing() {
427 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
428 let rect2 = Rectangle::new(Point::new(10, 0), Point::new(20, 10));
429 assert!(!check_spacing(&rect1, &rect2, 1));
430
431 let rect3 = Rectangle::new(Point::new(12, 0), Point::new(22, 10));
432 assert!(check_spacing(&rect1, &rect3, 1));
433 }
434
435 #[test]
436 fn test_bounding_rect() {
437 let rects = vec![
438 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
439 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
440 ];
441
442 let bbox = bounding_rect(&rects).unwrap();
443 assert_eq!(bbox.min, Point::new(0, 0));
444 assert_eq!(bbox.max, Point::new(15, 15));
445 }
446
447 #[test]
448 fn test_total_area() {
449 let rects = vec![
450 Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
451 Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
452 ];
453
454 let area = total_area(&rects);
455 assert_eq!(area, 175);
456 }
457
458 #[test]
459 fn test_from_dimensions() {
460 let origin = Point::new(5, 10);
461 let rect = Rectangle::from_dimensions(origin, 20, 30);
462 assert_eq!(rect.min, origin);
463 assert_eq!(rect.max, Point::new(25, 40));
464 }
465
466 #[test]
467 fn test_rectangle_contains() {
468 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
469 let inner = Rectangle::new(Point::new(2, 2), Point::new(8, 8));
470 let outer = Rectangle::new(Point::new(-5, -5), Point::new(15, 15));
471
472 assert!(rect.contains(&inner));
473 assert!(!rect.contains(&outer));
474 }
475
476 #[test]
477 fn test_rectangle_contains_point() {
478 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
479 let inside = Point::new(5, 5);
480 let outside = Point::new(15, 15);
481 let on_edge = Point::new(10, 5);
482
483 assert!(rect.contains_point(&inside));
484 assert!(!rect.contains_point(&outside));
485 assert!(rect.contains_point(&on_edge));
486 }
487
488 #[test]
489 fn test_bounding_rect_method() {
490 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
491 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
492 let bbox = rect1.bounding_rect(&rect2);
493
494 assert_eq!(bbox.min, Point::new(0, 0));
495 assert_eq!(bbox.max, Point::new(15, 15));
496 }
497
498 #[test]
499 fn test_to_polygon() {
500 let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
501 let polygon = rect.to_polygon();
502
503 assert_eq!(polygon.vertices().len(), 4);
504 assert!(polygon.is_rectilinear());
505 }
506
507 #[test]
508 fn test_bounding_rect_empty() {
509 let rects: Vec<Rectangle<i32>> = vec![];
510 assert!(bounding_rect(&rects).is_none());
511 }
512
513 #[test]
514 fn test_total_area_empty() {
515 let rects: Vec<Rectangle<i32>> = vec![];
516 assert_eq!(total_area(&rects), 0);
517 }
518
519 #[test]
520 fn test_manhattan_distance_reverse() {
521 let p1 = Point::new(5, 3);
523 let p2 = Point::new(1, 0);
524 assert_eq!(manhattan_distance(&p1, &p2), 7); }
526
527 #[test]
528 fn test_manhattan_distance_same() {
529 let p1 = Point::new(5, 5);
531 let p2 = Point::new(5, 5);
532 assert_eq!(manhattan_distance(&p1, &p2), 0);
533 }
534
535 #[test]
536 fn test_check_spacing_vertical() {
537 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
539 let rect2 = Rectangle::new(Point::new(0, 10), Point::new(10, 20));
540 assert!(!check_spacing(&rect1, &rect2, 1)); let rect3 = Rectangle::new(Point::new(0, 12), Point::new(10, 22));
543 assert!(check_spacing(&rect1, &rect3, 1)); }
545
546 #[test]
547 fn test_check_spacing_overlapping() {
548 let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
550 let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
551 assert!(!check_spacing(&rect1, &rect2, 1)); }
553}