mod curves;
mod surfaces;
use crate::{Point, Vector};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BoundingBox {
min: Point,
max: Point,
}
fn coordinates(p: Point) -> [f64; 3] {
[p.x, p.y, p.z]
}
fn point([x, y, z]: [f64; 3]) -> Point {
Point::new(x, y, z)
}
impl BoundingBox {
pub fn new(a: Point, b: Point) -> BoundingBox {
BoundingBox {
min: Point::new(a.x.min(b.x), a.y.min(b.y), a.z.min(b.z)),
max: Point::new(a.x.max(b.x), a.y.max(b.y), a.z.max(b.z)),
}
}
pub fn from_points(points: impl IntoIterator<Item = Point>) -> Option<BoundingBox> {
let mut points = points.into_iter();
let first = points.next()?;
Some(points.fold(BoundingBox::new(first, first), BoundingBox::including))
}
pub fn min(self) -> Point {
self.min
}
pub fn max(self) -> Point {
self.max
}
pub fn center(self) -> Point {
self.min + 0.5 * (self.max - self.min)
}
pub fn diagonal(self) -> Vector {
self.max - self.min
}
pub fn union(self, other: BoundingBox) -> BoundingBox {
self.including(other.min).including(other.max)
}
pub fn enlarged(self, distance: f64) -> BoundingBox {
let (min, max) = (coordinates(self.min), coordinates(self.max));
let (mut lo, mut hi) = (min, max);
for k in 0..3 {
lo[k] = min[k] - distance;
hi[k] = max[k] + distance;
if lo[k] > hi[k] {
let middle = 0.5 * (min[k] + max[k]);
(lo[k], hi[k]) = (middle, middle);
}
}
BoundingBox {
min: point(lo),
max: point(hi),
}
}
pub fn encloses(self, point: Point) -> bool {
let (p, min, max) = (
coordinates(point),
coordinates(self.min),
coordinates(self.max),
);
(0..3).all(|k| min[k] <= p[k] && p[k] <= max[k])
}
pub fn overlaps(self, other: BoundingBox) -> bool {
let (a_min, a_max) = (coordinates(self.min), coordinates(self.max));
let (b_min, b_max) = (coordinates(other.min), coordinates(other.max));
(0..3).all(|k| a_min[k] <= b_max[k] && b_min[k] <= a_max[k])
}
pub(crate) fn including(self, p: Point) -> BoundingBox {
BoundingBox {
min: Point::new(
self.min.x.min(p.x),
self.min.y.min(p.y),
self.min.z.min(p.z),
),
max: Point::new(
self.max.x.max(p.x),
self.max.y.max(p.y),
self.max.z.max(p.z),
),
}
}
}
pub trait HasBoundingBox {
fn bounding_box(&self) -> Option<BoundingBox>;
}
impl<T: HasBoundingBox + ?Sized> HasBoundingBox for &T {
fn bounding_box(&self) -> Option<BoundingBox> {
(**self).bounding_box()
}
}
pub(crate) mod sealed {
use super::BoundingBox;
pub trait BoundRange: crate::curves::ParametricCurve {
fn range_box(&self, first: f64, last: f64) -> BoundingBox;
}
}
#[deprecated(
since = "0.37.0",
note = "renamed `BoundingBox`: only 2D types carry a dimension suffix"
)]
pub type BoundingBox3D = BoundingBox;
#[doc(hidden)]
pub use HasBoundingBox as Bound3D;
#[cfg(test)]
mod tests {
use super::*;
fn unit() -> BoundingBox {
BoundingBox::new(Point::ORIGIN, Point::new(1.0, 1.0, 1.0))
}
#[test]
fn test_new_orders_the_corners() {
let b = BoundingBox::new(Point::new(1.0, -1.0, 2.0), Point::new(-3.0, 4.0, 2.0));
assert_eq!(b.min(), Point::new(-3.0, -1.0, 2.0));
assert_eq!(b.max(), Point::new(1.0, 4.0, 2.0));
}
#[test]
fn test_from_points_of_one_point_is_flat() {
let p = Point::new(1.0, 2.0, 3.0);
let b = BoundingBox::from_points([p]).unwrap();
assert_eq!((b.min(), b.max()), (p, p));
assert_eq!(b.diagonal(), Vector::ZERO);
assert!(b.encloses(p));
}
#[test]
fn test_union_is_symmetric_and_holds_both() {
let a = unit();
let b = BoundingBox::new(Point::new(-2.0, 0.5, 0.5), Point::new(-1.0, 3.0, 0.7));
assert_eq!(a.union(b), b.union(a));
let both = a.union(b);
for corner in [a.min(), a.max(), b.min(), b.max()] {
assert!(both.encloses(corner));
}
assert_eq!(a.union(a), a);
}
#[test]
fn test_enlarged_grows_and_shrinks() {
let b = unit().enlarged(0.25);
assert_eq!(b.min(), Point::new(-0.25, -0.25, -0.25));
assert_eq!(b.max(), Point::new(1.25, 1.25, 1.25));
assert_eq!(b.enlarged(-0.25), unit());
let collapsed = unit().enlarged(-2.0);
assert_eq!(collapsed.min(), Point::new(0.5, 0.5, 0.5));
assert_eq!(collapsed.max(), Point::new(0.5, 0.5, 0.5));
}
#[test]
fn test_encloses_counts_faces_and_rejects_each_axis() {
let b = unit();
assert!(b.encloses(Point::ORIGIN));
assert!(b.encloses(Point::new(1.0, 1.0, 1.0)));
for outside in [
Point::new(-1e-12, 0.5, 0.5),
Point::new(0.5, 1.0 + 1e-12, 0.5),
Point::new(0.5, 0.5, 2.0),
] {
assert!(!b.encloses(outside));
}
}
#[test]
fn test_overlaps_needs_every_axis() {
let b = unit();
assert!(b.overlaps(b));
let inner = BoundingBox::new(Point::new(0.2, 0.2, 0.2), Point::new(0.3, 0.3, 0.3));
assert!(b.overlaps(inner) && inner.overlaps(b));
let corner = BoundingBox::new(Point::new(1.0, 1.0, 1.0), Point::new(2.0, 2.0, 2.0));
assert!(b.overlaps(corner));
let above = BoundingBox::new(Point::new(0.5, 0.5, 1.5), Point::new(2.0, 2.0, 2.0));
assert!(!b.overlaps(above) && !above.overlaps(b));
}
#[test]
fn test_center_and_diagonal() {
let b = BoundingBox::new(Point::new(-1.0, 0.0, 2.0), Point::new(3.0, 2.0, 2.0));
assert_eq!(b.center(), Point::new(1.0, 1.0, 2.0));
assert_eq!(b.diagonal(), Vector::new(4.0, 2.0, 0.0));
}
}