1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use crate::Error;
use crate::Error::{InvalidNumber, LowerBoundEqualsUpperBound, LowerBoundExceedsUpperBound};
use nalgebra::{Point3, Vector3};

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AxisAlignedBoundingBox {
    lower_bound: Point3<f64>,
    upper_bound: Point3<f64>,
}

impl AxisAlignedBoundingBox {
    pub fn new(lower_bound: Point3<f64>, upper_bound: Point3<f64>) -> Result<Self, Error> {
        if lower_bound > upper_bound {
            return Err(LowerBoundExceedsUpperBound);
        }
        if lower_bound == upper_bound {
            return Err(LowerBoundEqualsUpperBound);
        }

        Ok(Self {
            lower_bound,
            upper_bound,
        })
    }

    pub fn lower_bound(&self) -> Point3<f64> {
        self.lower_bound
    }

    pub fn upper_bound(&self) -> Point3<f64> {
        self.upper_bound
    }

    pub fn diagonal(&self) -> Vector3<f64> {
        self.upper_bound - self.lower_bound
    }

    pub fn get_center(&self) -> Point3<f64> {
        let diagonal = self.upper_bound - self.lower_bound;
        self.lower_bound + diagonal / 2.0
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AxisAlignedBoundingCube {
    center: Point3<f64>,
    edge_length: f64,
}

impl AxisAlignedBoundingCube {
    pub fn new(center: Point3<f64>, edge_length: f64) -> Result<Self, Error> {
        if edge_length <= 0.0 {
            return Err(InvalidNumber);
        }

        Ok(Self {
            center,
            edge_length,
        })
    }

    pub fn from_bounding_box(bounding_box: &AxisAlignedBoundingBox) -> Self {
        let center = bounding_box.get_center();
        let diagonal = bounding_box.diagonal();
        let edge_length = diagonal.x.max(diagonal.y).max(diagonal.z);

        Self {
            center,
            edge_length,
        }
    }

    pub fn center(&self) -> Point3<f64> {
        self.center
    }

    pub fn edge_length(&self) -> f64 {
        self.edge_length
    }
    pub fn half_edge_length(&self) -> f64 {
        self.edge_length / 2.0
    }

    pub fn get_lower_bound(&self) -> Point3<f64> {
        let half_edge_length = self.half_edge_length();
        self.center - Vector3::new(half_edge_length, half_edge_length, half_edge_length)
    }

    pub fn get_upper_bound(&self) -> Point3<f64> {
        let half_edge_length = self.half_edge_length();
        self.center + Vector3::new(half_edge_length, half_edge_length, half_edge_length)
    }

    pub fn diagonal(&self) -> Vector3<f64> {
        self.get_upper_bound() - self.get_lower_bound()
    }

    pub fn get_sub_cube(&self, x_half: bool, y_half: bool, z_half: bool) -> Self {
        let sub_cube_edge_length = self.half_edge_length();
        let x_sign = if x_half { 1.0 } else { -1.0 };
        let y_sign = if y_half { 1.0 } else { -1.0 };
        let z_sign = if z_half { 1.0 } else { -1.0 };

        let sub_cube_center = self.center
            + Vector3::new(
                x_sign * sub_cube_edge_length / 2.0,
                y_sign * sub_cube_edge_length / 2.0,
                z_sign * sub_cube_edge_length / 2.0,
            );

        Self::new(sub_cube_center, sub_cube_edge_length).expect("should work")
    }
}