use core::cmp::Reverse;
use num::{Num, One};
pub use num;
mod basic_rectangle;
pub use basic_rectangle::BasicRectangle;
pub trait Rectangle
where
Self: Sized + Copy,
{
type Unit: Num + One + Copy + PartialEq + PartialOrd + Ord;
fn left(&self) -> Self::Unit;
fn right(&self) -> Self::Unit;
fn top(&self) -> Self::Unit;
fn bottom(&self) -> Self::Unit;
fn new_from_sides(
left: Self::Unit,
right: Self::Unit,
top: Self::Unit,
bottom: Self::Unit,
) -> Self;
fn width(&self) -> Self::Unit {
self.right() - self.left()
}
fn height(&self) -> Self::Unit {
self.top() - self.bottom()
}
fn translate(&self, x: Self::Unit, y: Self::Unit) -> Self {
Self::new_from_sides(
self.left() + x,
self.right() + x,
self.top() + y,
self.bottom() + y,
)
}
fn perimeter(&self) -> Self::Unit {
(self.width() + self.height()) * (Self::Unit::one() + Self::Unit::one())
}
fn area(&self) -> Self::Unit {
self.width() * self.height() }
fn contains_point(&self, x: Self::Unit, y: Self::Unit) -> bool {
x >= self.left() && x <= self.right() && y <= self.top() && y >= self.bottom()
}
fn contains_rectangle(&self, other: &impl Rectangle<Unit = Self::Unit>) -> bool {
self.left() <= other.left()
&& self.right() >= other.right()
&& self.top() >= other.top()
&& self.bottom() <= other.bottom()
}
fn overlaps(&self, other: &impl Rectangle<Unit = Self::Unit>) -> bool {
self.left() <= other.right()
&& self.right() >= other.left()
&& self.top() >= other.bottom()
&& self.bottom() <= other.top()
}
fn intersection(&self, other: &impl Rectangle<Unit = Self::Unit>) -> Option<Self> {
let left = self.left().max(other.left());
let right = self.right().min(other.right());
let top = self.top().min(other.top());
let bottom = self.bottom().max(other.bottom());
if left <= right && bottom <= top {
Some(Self::new_from_sides(left, right, top, bottom))
} else {
None
}
}
fn unobstructed_subrectangles(
&self,
obstructions: &[&impl Rectangle<Unit = Self::Unit>],
) -> Vec<Self> {
#[derive(Clone)]
struct UnfinishedRect<T: Rectangle> {
left: T::Unit,
top: T::Unit,
bottom: T::Unit,
}
struct Gap<T: Rectangle> {
top: T::Unit,
bottom: T::Unit,
}
struct Line<T: Rectangle> {
x: T::Unit,
opens: bool,
}
let mut obstructions = obstructions.to_vec();
obstructions.sort_unstable_by(
|rect_a, rect_b| {
rect_b.top().cmp(&rect_a.top()) },
);
let mut lines: Vec<Line<Self>> = vec![Line {
x: self.left(),
opens: true,
}];
for rect in &obstructions {
lines.push(Line {
x: rect.left(),
opens: false,
});
lines.push(Line {
x: rect.right() + Self::Unit::one(),
opens: true,
});
}
lines.sort_unstable_by_key(|line| line.x);
lines.dedup_by_key(|line| line.x);
let lines = lines
.into_iter()
.filter(|line| self.left() <= line.x && line.x <= self.right());
let mut unique_rectangles: Vec<Self> = Vec::new();
let mut active_rectangles: Vec<UnfinishedRect<Self>> = Vec::new();
for line in lines {
let mut gaps: Vec<Gap<Self>> = Vec::new();
let mut last_rectange_bottom: Self::Unit = self.top();
for obstruction in obstructions
.iter()
.filter(|rect| rect.left() <= line.x && line.x <= rect.right())
{
if last_rectange_bottom > obstruction.top() {
gaps.push(Gap {
top: last_rectange_bottom,
bottom: obstruction.top() + Self::Unit::one(), });
}
last_rectange_bottom =
last_rectange_bottom.min(obstruction.bottom() - Self::Unit::one());
}
if last_rectange_bottom >= self.bottom() {
gaps.push(Gap {
top: last_rectange_bottom,
bottom: self.bottom(),
});
}
active_rectangles.sort_unstable_by_key(|rect| Reverse(rect.left));
if line.opens {
for gap in gaps {
if !active_rectangles
.iter()
.any(|rect| gap.top == rect.top && gap.bottom == rect.bottom)
{
active_rectangles.push(UnfinishedRect {
left: line.x,
top: gap.top,
bottom: gap.bottom,
});
}
}
continue;
}
let mut new_active_rectangles: Vec<UnfinishedRect<Self>> = Vec::new();
active_rectangles = active_rectangles
.iter()
.filter(|rect| {
for gap in gaps.iter() {
if gap.top >= rect.top && rect.bottom >= gap.bottom {
return true;
}
}
unique_rectangles.push(Self::new_from_sides(
rect.left, line.x - Self::Unit::one(), rect.top, rect.bottom, ));
for gap in gaps
.iter()
.filter(|gap| gap.top <= rect.top || rect.bottom <= gap.bottom)
{
let top_limit = rect.top.min(gap.top);
let bottom_limit = rect.bottom.max(gap.bottom);
if !active_rectangles
.iter()
.chain(new_active_rectangles.iter())
.any(|rect| top_limit == rect.top && bottom_limit == rect.bottom)
{
new_active_rectangles.push(UnfinishedRect {
left: rect.left,
top: top_limit,
bottom: bottom_limit,
});
}
}
false
})
.cloned()
.collect();
active_rectangles.append(&mut new_active_rectangles);
}
for rect in active_rectangles {
unique_rectangles.push(Self::new_from_sides(
rect.left,
self.right(),
rect.top,
rect.bottom,
));
}
unique_rectangles
}
}