use super::geometry::Aabb;
use std::ops::Add;
#[derive(Clone, Copy, Debug)]
pub struct Leaf {
pub aabb: Aabb,
pub idx: usize,
}
impl Leaf {
#[must_use]
#[inline]
pub const fn new(aabb: Aabb, idx: usize) -> Self {
Self { aabb, idx }
}
}
#[derive(Clone, Debug)]
pub struct Branch {
pub aabb: Aabb,
pub left: Box<Node>,
pub right: Box<Node>,
}
impl Branch {
#[must_use]
#[inline]
pub const fn new(aabb: Aabb, left: Box<Node>, right: Box<Node>) -> Self {
Self { aabb, left, right }
}
}
#[derive(Clone, Debug)]
pub enum Node {
Leaf(Leaf),
Branch(Branch),
}
impl Node {
#[must_use]
#[inline]
pub const fn leaf(aabb: Aabb, idx: usize) -> Self {
Self::Leaf(Leaf::new(aabb, idx))
}
#[must_use]
#[inline]
pub fn branch(right: Self, left: Self) -> Self {
Self::Branch(Branch::new(
right.aabb() + left.aabb(),
Box::new(left),
Box::new(right),
))
}
#[must_use]
#[inline]
pub const fn aabb(&self) -> Aabb {
match self {
Self::Leaf(leaf) => leaf.aabb,
Self::Branch(branch) => branch.aabb,
}
}
}
#[inline]
pub fn global_aabb(boxes: &[Aabb]) -> Aabb {
boxes.iter().skip(1).copied().fold(boxes[0], Add::add)
}
#[cfg(test)]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
mod test {
use super::*;
use glam::Vec3A;
const MIN_X: f32 = -4107.33;
const MIN_Y: f32 = -6000.0;
const MIN_Z: f32 = -13.2678;
const MAX_X: f32 = 4107.33;
const MAX_Y: f32 = 6000.0;
const MAX_Z: f32 = 2075.45;
#[test]
fn global_bounding_box() {
let bounding_boxes = vec![
Aabb::new(Vec3A::new(MIN_X, 0.0, 0.0), Vec3A::ZERO),
Aabb::new(Vec3A::new(0.0, MIN_Y, 0.0), Vec3A::ZERO),
Aabb::new(Vec3A::new(0.0, 0.0, MIN_Z), Vec3A::ZERO),
Aabb::new(Vec3A::ZERO, Vec3A::new(MAX_X, 0.0, 0.0)),
Aabb::new(Vec3A::ZERO, Vec3A::new(0.0, MAX_Y, 0.0)),
Aabb::new(Vec3A::ZERO, Vec3A::new(0.0, 0.0, MAX_Z)),
];
let global = global_aabb(&bounding_boxes);
assert!((global.min().x - MIN_X).abs() < f32::EPSILON);
assert!((global.min().y - MIN_Y).abs() < f32::EPSILON);
assert!((global.min().z - MIN_Z).abs() < f32::EPSILON);
assert!((global.max().x - MAX_X).abs() < f32::EPSILON);
assert!((global.max().y - MAX_Y).abs() < f32::EPSILON);
assert!((global.max().z - MAX_Z).abs() < f32::EPSILON);
}
#[test]
fn global_bounding_box_min() {
let bounding_boxes = vec![
Aabb::new(
Vec3A::new(MIN_X, MIN_Y, MIN_Z),
Vec3A::new(MIN_X, MIN_Y, MIN_Z),
),
Aabb::new(
Vec3A::new(MIN_X, MIN_Y, MIN_Z),
Vec3A::new(MIN_X, MIN_Y, MIN_Z),
),
];
let global = global_aabb(&bounding_boxes);
assert!((global.min().x - MIN_X).abs() < f32::EPSILON);
assert!((global.min().y - MIN_Y).abs() < f32::EPSILON);
assert!((global.min().z - MIN_Z).abs() < f32::EPSILON);
assert!((global.max().x - MIN_X).abs() < f32::EPSILON);
assert!((global.max().y - MIN_Y).abs() < f32::EPSILON);
assert!((global.max().z - MIN_Z).abs() < f32::EPSILON);
}
#[test]
fn global_bounding_box_max() {
let bounding_boxes = vec![
Aabb::new(
Vec3A::new(MAX_X, MAX_Y, MAX_Z),
Vec3A::new(MAX_X, MAX_Y, MAX_Z),
),
Aabb::new(
Vec3A::new(MAX_X, MAX_Y, MAX_Z),
Vec3A::new(MAX_X, MAX_Y, MAX_Z),
),
];
let global = global_aabb(&bounding_boxes);
assert!((global.min().x - MAX_X).abs() < f32::EPSILON);
assert!((global.min().y - MAX_Y).abs() < f32::EPSILON);
assert!((global.min().z - MAX_Z).abs() < f32::EPSILON);
assert!((global.max().x - MAX_X).abs() < f32::EPSILON);
assert!((global.max().y - MAX_Y).abs() < f32::EPSILON);
assert!((global.max().z - MAX_Z).abs() < f32::EPSILON);
}
}