use prelude::*;
#[derive (Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BoundingBox2D {
min: Point2D,
max: Point2D
}
impl BoundingBox2D {
pub fn new<P1, P2>(min: &P1, max: &P2) -> Result<BoundingBox2D> where
P1: Is2D,
P2: Is2D {
if min.x() == max.x() || min.y() == max.y() {
Err(ErrorKind::MinMaxEqual)
} else if min.x() > max.x() || min.y() > max.y() {
Err(ErrorKind::MinMaxSwapped)
} else {
Ok(BoundingBox2D{min: Point2D{x: min.x(), y: min.y()}, max: Point2D{x: max.x(), y: max.y()}})
}
}
pub fn from_iterator<'a, It2D,P>(source: It2D) -> Result<BoundingBox2D> where
It2D: IntoIterator<Item=&'a P>,
P: 'a + Is2D + Sized {
let mut count = 0;
let mut minx : f64 = 0.0;
let mut miny : f64 = 0.0;
let mut maxx : f64 = 0.0;
let mut maxy : f64 = 0.0;
for p in source {
if count == 0 {
minx = p.x();
miny = p.y();
maxx = p.x();
maxy = p.y();
count += 1;
continue;
}
if p.x() < minx { minx = p.x(); }
if p.y() < miny { miny = p.y(); }
if p.x() > maxx { maxx = p.x(); }
if p.y() > maxy { maxy = p.y(); }
count += 1;
}
if count >= 2 {
Self::new(&Point2D{x: minx, y: miny}, &Point2D{x: maxx, y: maxy})
} else {
Err(ErrorKind::TooFewPoints)
}
}
pub fn min_p(&self) -> Point2D {
self.min.clone()
}
pub fn max_p(&self) -> Point2D {
self.max.clone()
}
pub fn size_x(&self) -> Positive {
Positive::new((self.max.x() - self.min.x()).abs()).unwrap() }
pub fn size_y(&self) -> Positive {
Positive::new((self.max.y() - self.min.y()).abs()).unwrap() }
pub fn center_bb(&self) -> Point2D {
Point2D{x: self.min.x() + (self.max.x() - self.min.x()) / 2.0,
y: self.min.y() + (self.max.y() - self.min.y()) / 2.0}
}
pub fn is_inside(&self, other: &BoundingBox2D) -> bool {
self.min.x() > other.min.x()
&& self.min.y() > other.min.y()
&& self.max.x() < other.max.x()
&& self.max.y() < other.max.y()
}
pub fn contains<P>(&self, other: &P) -> bool where
Self: Sized, P: Is2D {
other.x() > self.min.x()
&& other.x() < self.max.x()
&& other.y() > self.min.y()
&& other.y() < self.max.y()
}
pub fn has_inside(&self, other: &BoundingBox2D) -> bool {
self.min.x() < other.min.x()
&& self.min.y() < other.min.y()
&& self.max.x() > other.max.x()
&& self.max.y() > other.max.y()
}
pub fn collides_with(&self, other: &BoundingBox2D) -> bool {
2.0 * self.center_bb().x - other.center_bb().x < ((self.size_x() + other.size_x()).get())
&& 2.0 * self.center_bb().y - other.center_bb().y < ((self.size_y() + other.size_y()).get())
}
}
impl Default for BoundingBox2D {
fn default() -> Self {
BoundingBox2D {min: Point2D {x: -0.5, y: -0.5}, max: Point2D {x: 0.5, y: 0.5}}
}
}
impl HasBoundingBox2D for BoundingBox2D {
fn bounding_box(&self) -> Result<BoundingBox2D> {
BoundingBox2D::new(&self.min, &self.max)
}
}
impl HasDistanceTo<BoundingBox2D> for BoundingBox2D {
fn sqr_distance(&self, other: &BoundingBox2D) -> NonNegative {
let mut dx = 0.0;
let mut dy = 0.0;
if other.max_p().x() < self.min_p().x() {
dx = other.max_p().x() - self.min_p().x();
}
else if other.min_p().x() > self.max_p().x() {
dx = other.min_p().x() - self.max_p().x();
}
if other.max_p().y() < self.min_p().y() {
dy = other.max_p().y() - self.min_p().y();
}
else if other.min_p().y() > self.max_p().y() {
dy = other.min_p().y() - self.max_p().y();
}
NonNegative::new(dx*dx + dy*dy).unwrap()
}
}
impl IsScalable for BoundingBox2D {
fn scale(&mut self, factor: Positive) {
let c = self.center_bb();
let min_x = c.x - (0.5 * factor.get() * self.size_x().get());
let max_x = c.x + (0.5 * factor.get() * self.size_x().get());
let min_y = c.y - (0.5 * factor.get() * self.size_y().get());
let max_y = c.y + (0.5 * factor.get() * self.size_y().get());
self.min.set_pos(min_x, min_y);
self.max.set_pos(max_x, max_y);
}
}