use crate::compat::Vec;
use crate::core::Rect;
pub fn merge_intersecting_rects(rects: &[Rect]) -> Vec<Rect> {
if rects.is_empty() {
return Vec::new();
}
if rects.len() == 1 {
return rects.to_vec();
}
let mut merged = Vec::new();
for &rect in rects {
let mut current = rect;
let mut i = 0;
while i < merged.len() {
if current.intersects(&merged[i]) {
current = current.union(&merged[i]);
merged.swap_remove(i);
i = 0;
} else {
i += 1;
}
}
merged.push(current);
}
merged
}
pub fn bounding_rect(rects: &[Rect]) -> Option<Rect> {
if rects.is_empty() {
return None;
}
let mut result = rects[0];
for r in &rects[1..] {
result = result.union(r);
}
Some(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::Rect;
#[test]
fn test_merge_intersecting_empty() {
assert!(merge_intersecting_rects(&[]).is_empty());
}
#[test]
fn test_merge_intersecting_single() {
let r = Rect::new(10, 10, 50, 50);
assert_eq!(merge_intersecting_rects(&[r]), vec![r]);
}
#[test]
fn test_merge_intersecting_overlap() {
let rects = vec![Rect::new(0, 0, 100, 100), Rect::new(50, 50, 100, 100)];
let merged = merge_intersecting_rects(&rects);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0], Rect::new(0, 0, 150, 150));
}
#[test]
fn test_merge_intersecting_disjoint() {
let rects =
vec![Rect::new(0, 0, 10, 10), Rect::new(100, 100, 10, 10), Rect::new(200, 200, 10, 10)];
let merged = merge_intersecting_rects(&rects);
assert_eq!(merged.len(), 3);
}
#[test]
fn test_bounding_rect_empty() {
assert!(bounding_rect(&[]).is_none());
}
#[test]
fn test_bounding_rect_single() {
let r = Rect::new(5, 5, 20, 20);
assert_eq!(bounding_rect(&[r]), Some(r));
}
#[test]
fn test_bounding_rect_multiple() {
let rects =
vec![Rect::new(0, 0, 10, 10), Rect::new(20, 20, 10, 10), Rect::new(100, 100, 50, 50)];
assert_eq!(bounding_rect(&rects), Some(Rect::new(0, 0, 150, 150)));
}
#[test]
fn a_later_union_never_overlaps_an_earlier_output() {
let rects = vec![Rect::new(0, 0, 10, 2), Rect::new(0, 0, 2, 10), Rect::new(4, 4, 1, 1)];
let merged = merge_intersecting_rects(&rects);
for (i, a) in merged.iter().enumerate() {
for b in &merged[i + 1..] {
assert!(!a.intersects(b), "output rects must not overlap: {a:?} vs {b:?}");
}
}
assert_eq!(merged, vec![Rect::new(0, 0, 10, 10)], "the L-shape absorbs the interior rect");
}
#[test]
fn merging_is_idempotent_and_covers_the_input() {
let rects = vec![
Rect::new(0, 0, 10, 10),
Rect::new(5, 5, 20, 20),
Rect::new(100, 100, 10, 10),
Rect::new(110, 100, 5, 5),
];
let once = merge_intersecting_rects(&rects);
let twice = merge_intersecting_rects(&once);
assert_eq!(once.len(), twice.len(), "a second pass does not change the count");
for out in &twice {
assert!(once.contains(out), "the second pass is the same set");
}
for input in &rects {
assert!(
once.iter().any(|out| out.contains_rect(input)),
"every input rect is covered by an output rect: {input:?} not in {once:?}"
);
}
}
}