146 lines
4.5 KiB
Rust
146 lines
4.5 KiB
Rust
use crate::geometry::{Point, Rectangle};
|
|
use std::collections::HashMap;
|
|
use std::ops::{Add, Mul};
|
|
|
|
impl Add<Point> 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<u32> 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<T>(rects: T, distance_vector: Point) -> impl Iterator<Item = Rectangle>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
rects.map(move |r| r + distance_vector)
|
|
}
|
|
|
|
/// Returns a stream of rectangles by scaling each rectangle by a given amount.
|
|
pub fn scale<T>(rects: T, scale_factor: u32) -> impl Iterator<Item = Rectangle>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
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<T>(rects: T) -> impl Iterator<Item = Point>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
rects.map(|r| r.bottom_left())
|
|
}
|
|
|
|
/// Returns a stream containing all rectangles that intersect with the given rectangle.
|
|
pub fn get_all_intersecting<T>(rects: T, rectangle: &Rectangle) -> impl Iterator<Item = Rectangle>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
// 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<Item = Rectangle>) -> Option<u32> {
|
|
rects.map(|r| r.area()).max()
|
|
}
|
|
|
|
/// Returns the largest height among the given rectangles
|
|
pub fn get_largest_height(rects: impl Iterator<Item = Rectangle>) -> Option<u32> {
|
|
rects.map(|r| r.height()).max()
|
|
}
|
|
|
|
/// Returns the sum of the areas of the rectangles
|
|
pub fn get_area_sum(rects: impl Iterator<Item = Rectangle>) -> 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<T>(rects: T, rectangle: &Rectangle) -> u32
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
get_area_sum(get_all_intersecting(rects, rectangle))
|
|
}
|
|
|
|
/// Returns map from rectangle to computed area
|
|
pub fn get_area_map<T>(rects: T) -> HashMap<Rectangle, u32>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
// 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<T>(mut rects: T) -> Option<Rectangle>
|
|
where
|
|
T: Iterator<Item = Rectangle>,
|
|
{
|
|
// 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<Rectangle>, rect: Rectangle) -> Option<Rectangle> {
|
|
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..)));
|
|
}
|
|
}
|