use crate::geometry::{Point, Rectangle}; use std::collections::HashMap; use std::ops::{Add, Mul}; impl Add for Rectangle { type Output = Self; fn add(self, rhs: Point) -> Self { Rectangle::from_corner_width_height(self.top_left() + rhs, self.width(), self.height()) } } impl Mul for Rectangle { type Output = Self; fn mul(self, rhs: u32) -> Self { Rectangle::from_corner_width_height( self.top_left(), self.width() * rhs, self.height() * rhs, ) } } /// Returns a stream of rectangles by translating (moving) each rectangle according to the given /// distance vector. pub fn translate(rects: T, distance_vector: Point) -> impl Iterator where T: Iterator, { rects.map(move |r| r + distance_vector) } /// Returns a stream of rectangles by scaling each rectangle by a given amount. pub fn scale(rects: T, scale_factor: u32) -> impl Iterator where T: Iterator, { rects.map(move |r| r * scale_factor) } /// Returns a stream containing, in order, the bottom-left point of each input rectangle. pub fn bottom_left_points(rects: T) -> impl Iterator where T: Iterator, { rects.map(|r| r.bottom_left()) } /// Returns a stream containing all rectangles that intersect with the given rectangle. pub fn get_all_intersecting(rects: T, rectangle: &Rectangle) -> impl Iterator where T: Iterator, { // We need to satisfy the borrow checker here, make a copy. let rectangle = *rectangle; rects.filter(move |r| r.intersects(&rectangle)) } /// Returns the largest area among the given rectangles pub fn get_largest_area(rects: impl Iterator) -> Option { rects.map(|r| r.area()).max() } /// Returns the largest height among the given rectangles pub fn get_largest_height(rects: impl Iterator) -> Option { rects.map(|r| r.height()).max() } /// Returns the sum of the areas of the rectangles pub fn get_area_sum(rects: impl Iterator) -> u32 { rects.map(|r| r.area()).sum() } /// Computes the sum of areas of all rectangles that intersect with the given rectangle. pub fn get_area_sum_of_intersecting(rects: T, rectangle: &Rectangle) -> u32 where T: Iterator, { get_area_sum(get_all_intersecting(rects, rectangle)) } /// Returns map from rectangle to computed area pub fn get_area_map(rects: T) -> HashMap where T: Iterator, { // 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, { // The question mark operator is equivalent to: // let first = match rects.next() { // None => return None, // Some(x) => x, // }; let first = rects.next()?; fn intersect_helper(acc: Option, rect: Rectangle) -> Option { match acc { None => None, Some(a) => a.intersection(&rect), } } rects.fold(Some(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..))); } }