214 lines
6.5 KiB
Rust
214 lines
6.5 KiB
Rust
//! Module containing the bounding box struct.
|
|
|
|
use math::Number;
|
|
|
|
macro_rules! make_bounding_box {
|
|
|
|
($name: ident, $vector: ident, $size: expr) => {
|
|
|
|
use math::vector::$vector;
|
|
|
|
/// Represents a bounding box.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct $name<T> {
|
|
min: $vector<T>,
|
|
max: $vector<T>,
|
|
}
|
|
|
|
impl<T: PartialOrd + Copy + Clone> $name<T> {
|
|
/// Creates a bounding box from its min and max coordinates.
|
|
pub fn new(min: $vector<T>, max: $vector<T>) -> $name<T> {
|
|
$name {
|
|
min: min,
|
|
max: max,
|
|
}
|
|
}
|
|
|
|
/// Enlarges the bounding box so it contains the point passed as parameter.
|
|
pub fn add_point(&mut self, point: &$vector<T>) {
|
|
for i in 0..$size {
|
|
if point[i] < self.min[i] {
|
|
self.min[i] = point[i];
|
|
}
|
|
if point[i] > self.max[i] {
|
|
self.max[i] = point[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the minimum value of the bounding box.
|
|
pub fn min(&self) -> $vector<T> {
|
|
self.min
|
|
}
|
|
|
|
/// Returns the maximum value of the bounding box.
|
|
pub fn max(&self) -> $vector<T> {
|
|
self.max
|
|
}
|
|
|
|
/// Returns a mut ref to the min.
|
|
pub fn min_mut(&mut self) -> &mut $vector<T> {
|
|
&mut self.min
|
|
}
|
|
|
|
/// Returns a mut ref to the max.
|
|
pub fn max_mut(&mut self) -> &mut $vector<T> {
|
|
&mut self.max
|
|
}
|
|
|
|
}
|
|
|
|
impl<T: Number> $name<T> {
|
|
/// Scales a bounding box from its center.
|
|
pub fn scale(&mut self, factor: T) {
|
|
let diag = self.max - self.min;
|
|
|
|
self.min -= diag * factor;
|
|
self.max += diag * factor;
|
|
}
|
|
|
|
/// Returns the intersection between two bounding boxes.
|
|
pub fn intersection(&self, other: &$name<T>) -> $name<T> {
|
|
let mut ret = self.clone();
|
|
for i in 0..$size {
|
|
if ret.min()[i] < other.min()[i] {
|
|
ret.min_mut()[i] = other.min()[i];
|
|
}
|
|
if ret.max()[i] > other.max()[i] {
|
|
ret.max_mut()[i] = other.max()[i];
|
|
}
|
|
}
|
|
ret
|
|
}
|
|
|
|
/// Returns the bounding box of the union of two bounding boxes.
|
|
pub fn union(&self, other: &$name<T>) -> $name<T> {
|
|
let mut ret = self.clone();
|
|
for i in 0..$size {
|
|
if ret.min()[i] > other.min()[i] {
|
|
ret.min_mut()[i] = other.min()[i];
|
|
}
|
|
if ret.max()[i] < other.max()[i] {
|
|
ret.max_mut()[i] = other.max()[i];
|
|
}
|
|
}
|
|
ret
|
|
}
|
|
|
|
/// Returns true if the point is inside the bounding box.
|
|
pub fn contains_point(&self, point: $vector<T>) -> bool {
|
|
for i in 0 .. $size {
|
|
if self.min()[i] > point[i] || point[i] > self.max()[i] {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
make_bounding_box!(BoundingBox2, Vector2, 2);
|
|
make_bounding_box!(BoundingBox3, Vector3, 3);
|
|
make_bounding_box!(BoundingBox4, Vector4, 4);
|
|
|
|
macro_rules! impl_center {
|
|
($name: ident, $vector: ident, $f: tt) => {
|
|
impl $name<$f> {
|
|
/// Returns the center of the bounding box.
|
|
pub fn center(&self) -> $vector<$f> {
|
|
(self.min + self.max) / 2.0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl_center!(BoundingBox2, Vector2, f32);
|
|
impl_center!(BoundingBox3, Vector3, f32);
|
|
impl_center!(BoundingBox4, Vector4, f32);
|
|
impl_center!(BoundingBox2, Vector2, f64);
|
|
impl_center!(BoundingBox3, Vector3, f64);
|
|
impl_center!(BoundingBox4, Vector4, f64);
|
|
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
macro_rules! assert_delta {
|
|
($x:expr, $y:expr, $d:expr) => {
|
|
if !($x - $y < $d || $y - $x < $d) { panic!(); }
|
|
}
|
|
}
|
|
|
|
use math::vector::Vector2;
|
|
use math::bounding_box::BoundingBox2;
|
|
|
|
use math::vector::Vector3;
|
|
use math::bounding_box::BoundingBox3;
|
|
|
|
#[test]
|
|
fn initialization() {
|
|
use std::f64::{MIN, MAX};
|
|
let mut bounding_box = BoundingBox2::new(
|
|
Vector2::new(MAX, MAX),
|
|
Vector2::new(MIN, MIN),
|
|
);
|
|
let v1 = Vector2::new(10.0, 20.0);
|
|
bounding_box.add_point(&v1);
|
|
assert_delta!(bounding_box.min().x(), v1.x(), 0.01);
|
|
assert_delta!(bounding_box.min().y(), v1.y(), 0.01);
|
|
assert_delta!(bounding_box.max().x(), v1.x(), 0.01);
|
|
assert_delta!(bounding_box.max().y(), v1.y(), 0.01);
|
|
|
|
let v2 = Vector2::new(20.0, 10.0);
|
|
assert_delta!(bounding_box.min().x(), v1.x(), 0.01);
|
|
assert_delta!(bounding_box.min().y(), v2.y(), 0.01);
|
|
assert_delta!(bounding_box.max().x(), v1.x(), 0.01);
|
|
assert_delta!(bounding_box.max().y(), v2.y(), 0.01);
|
|
}
|
|
|
|
#[test]
|
|
fn intersection() {
|
|
let b1 = BoundingBox3::new(
|
|
Vector3::new(0, 0, 0),
|
|
Vector3::new(2, 2, 2),
|
|
);
|
|
|
|
let b2 = BoundingBox3::new(
|
|
Vector3::new(1, 1, 1),
|
|
Vector3::new(3, 3, 3),
|
|
);
|
|
|
|
let bb1 = b1.intersection(&b2);
|
|
assert_eq!(bb1.min().x(), 1);
|
|
assert_eq!(bb1.min().y(), 1);
|
|
assert_eq!(bb1.min().z(), 1);
|
|
|
|
assert_eq!(bb1.max().x(), 2);
|
|
assert_eq!(bb1.max().y(), 2);
|
|
assert_eq!(bb1.max().z(), 2);
|
|
|
|
let bb2 = b2.intersection(&b1);
|
|
assert_eq!(bb1, bb2);
|
|
}
|
|
|
|
#[test]
|
|
fn contains_point() {
|
|
let b1 = BoundingBox3::new(
|
|
Vector3::new(0.0, 0.0, 0.0),
|
|
Vector3::new(2.0, 2.0, 2.0),
|
|
);
|
|
|
|
assert_eq!(b1.contains_point(Vector3::new(1.0, 1.0, 1.0)), true);
|
|
assert_eq!(b1.contains_point(Vector3::new(1.5, 0.5, 1.0)), true);
|
|
assert_eq!(b1.contains_point(Vector3::new(1.5, -0.5, 1.0)), false);
|
|
assert_eq!(b1.contains_point(Vector3::new(1.5, 0.5, -1.0)), false);
|
|
assert_eq!(b1.contains_point(Vector3::new(-0.5, 0.5, 1.0)), false);
|
|
assert_eq!(b1.contains_point(Vector3::new(2.5, 0.5, 1.0)), false);
|
|
assert_eq!(b1.contains_point(Vector3::new(0.5, 2.5, 1.0)), false);
|
|
|
|
}
|
|
}
|