use std::{
borrow::Borrow,
ops::{Add, Sub},
};
use crate::geometry::Coordinate;
#[derive(Debug, Clone, Copy)]
pub struct BoundingBox(Coordinate, Coordinate);
impl BoundingBox {
pub fn new(coordinate1: Coordinate, coordinate2: Coordinate) -> Self {
let mut bb = Self(coordinate1, coordinate2);
bb.normalize();
bb
}
pub fn from_point(point: Coordinate) -> Self {
BoundingBox::new(Coordinate::origin(), point)
}
pub fn from_points<I>(points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Coordinate>,
{
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
let mut has_points = false;
for p in points {
let (x, y) = p.borrow().to_cartesian();
min_x = min_x.min(x);
min_y = min_y.min(y);
max_x = max_x.max(x);
max_y = max_y.max(y);
has_points = true;
}
if !has_points {
return Self(Coordinate::origin(), Coordinate::origin());
}
Self(
Coordinate::Cartesian { x: min_x, y: min_y },
Coordinate::Cartesian { x: max_x, y: max_y },
)
}
pub fn min(&self) -> Coordinate {
self.0
}
pub fn max(&self) -> Coordinate {
self.1
}
pub fn span(&self) -> Coordinate {
self.1 - self.0
}
pub fn intersects(&self, other: &BoundingBox) -> bool {
let BoundingBox(self_min, self_max) = self;
let BoundingBox(other_min, other_max) = other;
let (self_min_x, self_min_y) = self_min.to_cartesian();
let (self_max_x, self_max_y) = self_max.to_cartesian();
let (other_min_x, other_min_y) = other_min.to_cartesian();
let (other_max_x, other_max_y) = other_max.to_cartesian();
self_min_x <= other_max_x
&& self_max_x >= other_min_x
&& self_min_y <= other_max_y
&& self_max_y >= other_min_y
}
fn normalize(&mut self) {
let (x0, y0) = self.0.to_cartesian();
let (x1, y1) = self.1.to_cartesian();
self.0 = Coordinate::Cartesian {
x: x0.min(x1),
y: y0.min(y1),
};
self.1 = Coordinate::Cartesian {
x: x0.max(x1),
y: y0.max(y1),
};
}
}
impl Add<Coordinate> for BoundingBox {
type Output = BoundingBox;
fn add(self, rhs: Coordinate) -> Self::Output {
BoundingBox(self.0 + rhs, self.1 + rhs)
}
}
impl Sub<Coordinate> for BoundingBox {
type Output = BoundingBox;
fn sub(self, rhs: Coordinate) -> Self::Output {
BoundingBox(self.0 - rhs, self.1 - rhs)
}
}