use crate::*;
#[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 from_into_iterator<It2D, P>(source: It2D) -> Result<BoundingBox2D>
where
It2D: IntoIterator<Item = P>,
P: 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 sizes(&self) -> [Positive; 2] {
[self.size_x(), self.size_y()]
}
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).abs()
< ((self.size_x() + other.size_x()).get())
&& 2.0 * (self.center_bb().y - other.center_bb().y).abs()
< ((self.size_y() + other.size_y()).get())
}
pub fn crossing_x_value(&self, x: f64) -> bool {
self.min.x() < x && self.max.x() > x
}
pub fn crossing_y_value(&self, y: f64) -> bool {
self.min.y() < y && self.max.y() > y
}
pub fn corners(&self) -> [Point2D; 4] {
[
Point2D::new(self.min.x(), self.min.y()),
Point2D::new(self.min.x(), self.max.y()),
Point2D::new(self.max.x(), self.min.y()),
Point2D::new(self.max.x(), self.max.y()),
]
}
pub fn distance<P>(&self, other: &P) -> NonNegative
where
P: Is2D,
{
let sqr_dist = self.sqr_distance(other).get();
NonNegative::new(sqr_dist.sqrt()).unwrap()
}
pub fn sqr_distance<P>(&self, other: &P) -> NonNegative
where
P: Is2D,
{
let dx = max_f64_3(
self.min_p().x() - other.x(),
0.0,
other.x() - self.max_p().x(),
);
let dy = max_f64_3(
self.min_p().y() - other.y(),
0.0,
other.y() - self.max_p().y(),
);
NonNegative::new(dx * dx + dy * dy).unwrap()
}
}
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 IsND for BoundingBox2D {
fn n_dimensions() -> usize {
2
}
fn position_nd(&self, dimension: usize) -> Result<f64> {
self.center_bb().position_nd(dimension)
}
}
impl Is2D for BoundingBox2D {
#[inline(always)]
fn x(&self) -> f64 {
self.center_bb().x()
}
#[inline(always)]
fn y(&self) -> f64 {
self.center_bb().y()
}
}
impl IsMovable2D for BoundingBox2D {
fn move_by(&mut self, x: f64, y: f64) {
self.min.move_by(x, y);
self.max.move_by(x, y);
}
}
impl HasBoundingBox2D for BoundingBox2D {
fn bounding_box(&self) -> BoundingBox2D {
BoundingBox2D::new(&self.min, &self.max).unwrap() }
}
impl HasBoundingBox2DMaybe for BoundingBox2D {
fn bounding_box_maybe(&self) -> Result<BoundingBox2D> {
Ok(self.bounding_box())
}
}
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_xy(min_x, min_y);
self.max.set_xy(max_x, max_y);
}
}
impl IsMergeable for BoundingBox2D {
fn consume(&mut self, other: Self) {
let (mut min_x, mut min_y) = (self.min.x(), self.min.y());
let (mut max_x, mut max_y) = (self.max.x(), self.max.y());
if other.min.x() < min_x {
min_x = other.min.x()
}
if other.min.y() < min_y {
min_y = other.min.y()
}
if other.max.x() > max_x {
max_x = other.max.x()
}
if other.max.y() > max_y {
max_y = other.max.y()
}
self.min.set_xy(min_x, min_y);
self.max.set_xy(max_x, max_y);
}
fn combine(&self, other: &Self) -> Self {
let (mut min_x, mut min_y) = (self.min.x(), self.min.y());
let (mut max_x, mut max_y) = (self.max.x(), self.max.y());
if other.min.x() < min_x {
min_x = other.min.x()
}
if other.min.y() < min_y {
min_y = other.min.y()
}
if other.max.x() > max_x {
max_x = other.max.x()
}
if other.max.y() > max_y {
max_y = other.max.y()
}
let min = Point2D::new(min_x, min_y);
let max = Point2D::new(max_x, max_y);
BoundingBox2D { min, max }
}
}