diff --git a/src/geometry.rs b/src/geometry.rs index 72b466a..36758ad 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -152,6 +152,43 @@ impl Rectangle { || self.top_right().left_of(&other.top_left()) || self.bottom_left().above(&other.top_left())) } + + pub fn intersection(&self, other: &Rectangle) -> Option { + if !self.intersects(other) { + return None; + } + + // idk wtf this is lol + let in_top_left; + let in_bottom_right; + if self.top_left().right_of(&other.top_left()) { + if self.top_left().below(&other.top_left()) { + in_top_left = self.top_left(); + } else { + in_top_left = Point::new(self.top_left().x(), other.top_left().y()); + } + } else { + if self.top_left().below(&other.top_left()) { + in_top_left = Point::new(other.top_left().x(), self.top_left().y()); + } else { + in_top_left = other.top_left(); + } + } + if self.bottom_right().left_of(&other.bottom_right()) { + if self.bottom_right().below(&other.bottom_right()) { + in_bottom_right = Point::new(self.bottom_right().x(), other.bottom_right().y()); + } else { + in_bottom_right = self.bottom_right(); + } + } else { + if self.bottom_right().below(&other.bottom_right()) { + in_bottom_right = other.bottom_right(); + } else { + in_bottom_right = Point::new(other.bottom_right().x(), self.bottom_right().y()); + } + } + Some(Rectangle::from_corners(in_top_left, in_bottom_right)) + } } #[cfg(test)] diff --git a/src/stream_algos.rs b/src/stream_algos.rs index 7807576..5ce94d6 100644 --- a/src/stream_algos.rs +++ b/src/stream_algos.rs @@ -88,3 +88,58 @@ where // The type of the collect() function is chosen based on the return type that it's meant to fit rects.map(|r| (r, r.area())).collect() } + +pub fn intersect_all(mut rects: T) -> Option +where + T: Iterator, +{ + let first = rects.next(); + + if let None = first { + // Can't do intersection if no items. + return None; + } + + fn intersect_helper(acc: Option, rect: Rectangle) -> Option { + match acc { + None => None, + Some(a) => a.intersection(&rect), + } + } + + rects.fold(first, intersect_helper) +} + +#[cfg(test)] +mod tests { + use super::*; + + const INCREASING_ENCLOSING: [Rectangle; 5] = [ + Rectangle::at_origin_with_size(1, 1), + Rectangle::at_origin_with_size(2, 2), + Rectangle::at_origin_with_size(3, 3), + Rectangle::at_origin_with_size(4, 4), + Rectangle::at_origin_with_size(5, 5), + ]; + + #[test] + fn test_intersect_all_1() { + let mut v = Vec::new(); + v.extend(INCREASING_ENCLOSING.iter()); + // Drain is a bit different to iter in that it releases ownership of each object, + // instead of just giving a reference. + assert_eq!(Some(INCREASING_ENCLOSING[0]), intersect_all(v.drain(0..))); + } + + #[test] + fn test_intersect_all_2() { + // Deliberately inconsistent with above code to show a different way of doing things. + let mut various_rects = vec![ + Rectangle::from_corner_width_height(Point::new(20, 110), 10, 10), + Rectangle::from_corner_width_height(Point::new(25, 115), 10, 10), + Rectangle::from_corner_width_height(Point::new(10, 100), 1000, 1000), + Rectangle::from_corner_width_height(Point::new(2010, 2100), 1, 1), + ]; + assert_eq!(None, intersect_all(various_rects.drain(0..))); + } +}