use super::{Vector2, Vector3};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoundingBox2D {
pub min: Vector2,
pub max: Vector2,
}
impl BoundingBox2D {
pub fn new(min: Vector2, max: Vector2) -> Self {
BoundingBox2D { min, max }
}
pub fn from_point(point: Vector2) -> Self {
BoundingBox2D {
min: point,
max: point,
}
}
pub fn from_points(points: &[Vector2]) -> Option<Self> {
if points.is_empty() {
return None;
}
let mut min_x = points[0].x;
let mut min_y = points[0].y;
let mut max_x = points[0].x;
let mut max_y = points[0].y;
for point in points.iter().skip(1) {
min_x = min_x.min(point.x);
min_y = min_y.min(point.y);
max_x = max_x.max(point.x);
max_y = max_y.max(point.y);
}
Some(BoundingBox2D {
min: Vector2::new(min_x, min_y),
max: Vector2::new(max_x, max_y),
})
}
pub fn width(&self) -> f64 {
self.max.x - self.min.x
}
pub fn height(&self) -> f64 {
self.max.y - self.min.y
}
pub fn center(&self) -> Vector2 {
Vector2::new(
(self.min.x + self.max.x) / 2.0,
(self.min.y + self.max.y) / 2.0,
)
}
pub fn contains(&self, point: Vector2) -> bool {
point.x >= self.min.x
&& point.x <= self.max.x
&& point.y >= self.min.y
&& point.y <= self.max.y
}
pub fn expand_to_include(&mut self, point: Vector2) {
self.min.x = self.min.x.min(point.x);
self.min.y = self.min.y.min(point.y);
self.max.x = self.max.x.max(point.x);
self.max.y = self.max.y.max(point.y);
}
pub fn merge(&self, other: &BoundingBox2D) -> BoundingBox2D {
BoundingBox2D {
min: Vector2::new(
self.min.x.min(other.min.x),
self.min.y.min(other.min.y),
),
max: Vector2::new(
self.max.x.max(other.max.x),
self.max.y.max(other.max.y),
),
}
}
}
impl fmt::Display for BoundingBox2D {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BBox2D[{} -> {}]", self.min, self.max)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoundingBox3D {
pub min: Vector3,
pub max: Vector3,
}
impl Default for BoundingBox3D {
fn default() -> Self {
BoundingBox3D {
min: Vector3::new(0.0, 0.0, 0.0),
max: Vector3::new(0.0, 0.0, 0.0),
}
}
}
impl BoundingBox3D {
pub fn new(min: Vector3, max: Vector3) -> Self {
BoundingBox3D { min, max }
}
pub fn from_point(point: Vector3) -> Self {
BoundingBox3D {
min: point,
max: point,
}
}
pub fn from_points(points: &[Vector3]) -> Option<Self> {
if points.is_empty() {
return None;
}
let mut min_x = points[0].x;
let mut min_y = points[0].y;
let mut min_z = points[0].z;
let mut max_x = points[0].x;
let mut max_y = points[0].y;
let mut max_z = points[0].z;
for point in points.iter().skip(1) {
min_x = min_x.min(point.x);
min_y = min_y.min(point.y);
min_z = min_z.min(point.z);
max_x = max_x.max(point.x);
max_y = max_y.max(point.y);
max_z = max_z.max(point.z);
}
Some(BoundingBox3D {
min: Vector3::new(min_x, min_y, min_z),
max: Vector3::new(max_x, max_y, max_z),
})
}
pub fn width(&self) -> f64 {
self.max.x - self.min.x
}
pub fn height(&self) -> f64 {
self.max.y - self.min.y
}
pub fn depth(&self) -> f64 {
self.max.z - self.min.z
}
pub fn center(&self) -> Vector3 {
Vector3::new(
(self.min.x + self.max.x) / 2.0,
(self.min.y + self.max.y) / 2.0,
(self.min.z + self.max.z) / 2.0,
)
}
pub fn contains(&self, point: Vector3) -> bool {
point.x >= self.min.x
&& point.x <= self.max.x
&& point.y >= self.min.y
&& point.y <= self.max.y
&& point.z >= self.min.z
&& point.z <= self.max.z
}
pub fn expand_to_include(&mut self, point: Vector3) {
self.min.x = self.min.x.min(point.x);
self.min.y = self.min.y.min(point.y);
self.min.z = self.min.z.min(point.z);
self.max.x = self.max.x.max(point.x);
self.max.y = self.max.y.max(point.y);
self.max.z = self.max.z.max(point.z);
}
pub fn merge(&self, other: &BoundingBox3D) -> BoundingBox3D {
BoundingBox3D {
min: Vector3::new(
self.min.x.min(other.min.x),
self.min.y.min(other.min.y),
self.min.z.min(other.min.z),
),
max: Vector3::new(
self.max.x.max(other.max.x),
self.max.y.max(other.max.y),
self.max.z.max(other.max.z),
),
}
}
}
impl fmt::Display for BoundingBox3D {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BBox3D[{} -> {}]", self.min, self.max)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bbox2d_from_points() {
let points = vec![
Vector2::new(0.0, 0.0),
Vector2::new(10.0, 5.0),
Vector2::new(-5.0, 3.0),
];
let bbox = BoundingBox2D::from_points(&points).unwrap();
assert_eq!(bbox.min, Vector2::new(-5.0, 0.0));
assert_eq!(bbox.max, Vector2::new(10.0, 5.0));
}
#[test]
fn test_bbox2d_dimensions() {
let bbox = BoundingBox2D::new(Vector2::new(0.0, 0.0), Vector2::new(10.0, 5.0));
assert_eq!(bbox.width(), 10.0);
assert_eq!(bbox.height(), 5.0);
assert_eq!(bbox.center(), Vector2::new(5.0, 2.5));
}
#[test]
fn test_bbox2d_contains() {
let bbox = BoundingBox2D::new(Vector2::new(0.0, 0.0), Vector2::new(10.0, 10.0));
assert!(bbox.contains(Vector2::new(5.0, 5.0)));
assert!(!bbox.contains(Vector2::new(15.0, 5.0)));
}
#[test]
fn test_bbox3d_from_points() {
let points = vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 5.0, 3.0),
Vector3::new(-5.0, 3.0, -2.0),
];
let bbox = BoundingBox3D::from_points(&points).unwrap();
assert_eq!(bbox.min, Vector3::new(-5.0, 0.0, -2.0));
assert_eq!(bbox.max, Vector3::new(10.0, 5.0, 3.0));
}
#[test]
fn test_bbox3d_dimensions() {
let bbox = BoundingBox3D::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 5.0, 3.0),
);
assert_eq!(bbox.width(), 10.0);
assert_eq!(bbox.height(), 5.0);
assert_eq!(bbox.depth(), 3.0);
assert_eq!(bbox.center(), Vector3::new(5.0, 2.5, 1.5));
}
}