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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Axis-aligned bounding box.
//!
//! Represented by a cuboid defined by two points. As long as the
//! `set_*` functions are used, the `lower` point will be less than or equal to the `upper` point
//! for any axis.


use cgmath::Point3;


// local min/max funcs for f32 since it isn't Ord and doesn't work with std::min/max
fn float_min(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn float_max(a: f32, b: f32) -> f32 { if a > b { a } else { b } }


/// An axis-aligned bounding box. Represented by a cuboid defined by two points. As long as the
/// `set_*` functions are used, the `lower` point will be less than or equal to the `upper` point
/// for any axis.
pub struct AABB {
    lower: Point3<f32>,
    upper: Point3<f32>,
}


#[allow(dead_code)]
impl AABB {
    /// Constructs a new AABB of size zero.
    pub fn new() -> AABB {
        AABB {
            lower: Point3::new(0.0, 0.0, 0.0),
            upper: Point3::new(0.0, 0.0, 0.0),
        }
    }

    /// Constructs a new AABB with the given points. This method does not ensure `lower` <= `upper`
    /// for all axes.
    pub fn from(lower: Point3<f32>, upper: Point3<f32>) -> AABB {
        AABB { lower, upper }
    }

    /// Returns the length of the AABB in the x dimension.
    pub fn size_x(&self) -> f32 { self.upper.x - self.lower.x }
    /// Returns the length of the AABB in the y dimension.
    pub fn size_y(&self) -> f32 { self.upper.y - self.lower.y }
    /// Returns the length of the AABB in the z dimension.
    pub fn size_z(&self) -> f32 { self.upper.z - self.lower.z }

    /// Returns the x coordinate of the lower point, representing the left side of the AABB.
    pub fn left(&self) -> f32 { self.lower.x }
    /// Returns the x coordinate of the upper point, representing the right side of the AABB.
    pub fn right(&self) -> f32 { self.upper.x }
    /// Returns the y coordinate of the lower point, representing the top side of the AABB.
    pub fn top(&self) -> f32 { self.lower.y }
    /// Returns the y coordinate of the upper point, representing the bottom side of the AABB.
    pub fn bottom(&self) -> f32 { self.upper.y }
    /// Returns the z coordinate of the lower point, representing the front side of the AABB.
    pub fn front(&self) -> f32 { self.lower.z }
    /// Returns the z coordinate of the upper point, representing the back side of the AABB.
    pub fn back(&self) -> f32 { self.upper.z }


    /// Updates the lower point. Rearranges the coordinates to assure that `lower` <= `upper` for
    /// all axes.
    pub fn set_lower(&mut self, lower: Point3<f32>) {
        let (x1, y1, z1) = lower.into();
        let (x2, y2, z2) = self.upper.into();
        self.lower = Point3::new(float_min(x1, x2), float_min(y1, y2), float_min(z1, z2));
        self.upper = Point3::new(float_max(x1, x2), float_max(y1, y2), float_max(z1, z2));
    }
    /// Updates the upper point. Rearranges the coordinates to assure that `lower` <= `upper` for
    /// all axes.
    pub fn set_upper(&mut self, upper: Point3<f32>) {
        let (x1, y1, z1) = self.lower.into();
        let (x2, y2, z2) = upper.into();
        self.lower = Point3::new(float_min(x1, x2), float_min(y1, y2), float_min(z1, z2));
        self.upper = Point3::new(float_max(x1, x2), float_max(y1, y2), float_max(z1, z2));
    }


    /// Updates the x coordinate of the lower point (the left side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_left(&mut self, left: f32) {
        let x1 = left;
        let x2 = self.upper.x;
        self.lower.x = float_min(x1, x2);
        self.upper.x = float_max(x1, x2);
    }
    /// Updates the x coordinate of the upper point (the right side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_right(&mut self, right: f32) {
        let x1 = self.lower.x;
        let x2 = right;
        self.lower.x = float_min(x1, x2);
        self.upper.x = float_max(x1, x2);
    }
    /// Updates the y coordinate of the lower point (the bottom side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_bottom(&mut self, bottom: f32) {
        let y1 = bottom;
        let y2 = self.upper.y;
        self.lower.y = float_min(y1, y2);
        self.upper.y = float_max(y1, y2);
    }
    /// Updates the y coordinate of the upper point (the top side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_top(&mut self, top: f32) {
        let y1 = self.lower.y;
        let y2 = top;
        self.lower.y = float_min(y1, y2);
        self.upper.y = float_max(y1, y2);
    }
    /// Updates the z coordinate of the lower point (the front side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_front(&mut self, front: f32) {
        let z1 = front;
        let z2 = self.upper.z;
        self.lower.z = float_min(z1, z2);
        self.upper.z = float_max(z1, z2);
    }
    /// Updates the z coordinate of the upper point (the back side of the AABB). Ensures that
    /// `lower` <= `upper` for all axes.
    pub fn set_back(&mut self, back: f32) {
        let z1 = self.lower.z;
        let z2 = back;
        self.lower.z = float_min(z1, z2);
        self.upper.z = float_max(z1, z2);
    }
}

impl Default for AABB {
    fn default() -> Self {
        AABB {
            lower: Point3::new(0.0, 0.0, 0.0),
            upper: Point3::new(0.0, 0.0, 0.0)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::AABB;
    use cgmath::Point3;

    macro_rules! assert_eq_float {
        ($a:expr, $b:expr) => { assert!((($a) - ($b)).abs() < std::f32::EPSILON) }
    }

    #[test]
    fn test_aabb_from() {
        let b = AABB::from(Point3::new(1.0, 3.0, 5.0), Point3::new(6.0, 4.0, 2.0));
        assert_eq!(b.lower, Point3::new(1.0, 3.0, 5.0));
        assert_eq!(b.upper, Point3::new(6.0, 4.0, 2.0));
        assert_eq_float!(b.left(), 1.0);
        assert_eq_float!(b.right(), 6.0);
        assert_eq_float!(b.top(), 3.0);
        assert_eq_float!(b.bottom(), 4.0);
        assert_eq_float!(b.front(), 5.0); // note that these are in the wrong order
        assert_eq_float!(b.back(), 2.0); // AABB::from() does not check point ordering
    }
}