1use std::ops::{Add, Sub};
2
3use glam::DVec3;
4
5#[derive(Copy, Clone, PartialEq, Default, Debug)]
10pub struct Aabb {
11 min: DVec3,
12 max: DVec3,
13}
14
15impl Aabb {
16 pub const ZERO: Self = Self {
17 min: DVec3::ZERO,
18 max: DVec3::ZERO,
19 };
20
21 #[cfg_attr(debug_assertions, track_caller)]
28 pub fn new(min: DVec3, max: DVec3) -> Self {
29 debug_assert!(
30 min.x <= max.x && min.y <= max.y && min.z <= max.z,
31 "`min` must be less than or equal to `max` componentwise (min = {min}, max = {max})"
32 );
33
34 Self { min, max }
35 }
36
37 #[doc(hidden)]
39 pub const fn new_unchecked(min: DVec3, max: DVec3) -> Self {
40 Self { min, max }
41 }
42
43 pub fn new_point(p: DVec3) -> Self {
45 Self::new(p, p)
46 }
47
48 pub fn from_bottom_size(bottom: DVec3, size: DVec3) -> Self {
49 Self::new(
50 DVec3 {
51 x: bottom.x - size.x / 2.0,
52 y: bottom.y,
53 z: bottom.z - size.z / 2.0,
54 },
55 DVec3 {
56 x: bottom.x + size.x / 2.0,
57 y: bottom.y + size.y,
58 z: bottom.z + size.z / 2.0,
59 },
60 )
61 }
62
63 pub const fn min(self) -> DVec3 {
64 self.min
65 }
66
67 pub const fn max(self) -> DVec3 {
68 self.max
69 }
70
71 pub fn union(self, other: Self) -> Self {
72 Self::new(self.min.min(other.min), self.max.max(other.max))
73 }
74
75 pub fn intersects(self, other: Self) -> bool {
80 self.max.x >= other.min.x
81 && other.max.x >= self.min.x
82 && self.max.y >= other.min.y
83 && other.max.y >= self.min.y
84 && self.max.z >= other.min.z
85 && other.max.z >= self.min.z
86 }
87
88 pub fn intersects_strict(self, other: Self) -> bool {
93 self.max.x > other.min.x
94 && other.max.x > self.min.x
95 && self.max.y > other.min.y
96 && other.max.y > self.min.y
97 && self.max.z > other.min.z
98 && other.max.z > self.min.z
99 }
100
101 pub fn contains_point(self, p: DVec3) -> bool {
103 self.min.x <= p.x
104 && self.min.y <= p.y
105 && self.min.z <= p.z
106 && self.max.x >= p.x
107 && self.max.y >= p.y
108 && self.max.z >= p.z
109 }
110
111 pub fn projected_point(self, p: DVec3) -> DVec3 {
113 p.clamp(self.min, self.max)
114 }
115
116 pub fn distance_to_point(self, p: DVec3) -> f64 {
118 self.projected_point(p).distance(p)
119 }
120
121 pub fn ray_intersection(self, origin: DVec3, direction: DVec3) -> Option<[f64; 2]> {
132 let mut near: f64 = 0.0;
133 let mut far = f64::INFINITY;
134
135 for i in 0..3 {
136 let t0 = (self.min[i] - origin[i]) / direction[i];
139 let t1 = (self.max[i] - origin[i]) / direction[i];
140
141 near = near.max(t0.min(t1));
142 far = far.min(t0.max(t1));
143 }
144
145 (near <= far).then_some([near, far])
146 }
147}
148
149impl Add<DVec3> for Aabb {
150 type Output = Aabb;
151
152 fn add(self, rhs: DVec3) -> Self::Output {
153 Self::new(self.min + rhs, self.max + rhs)
154 }
155}
156
157impl Add<Aabb> for DVec3 {
158 type Output = Aabb;
159
160 fn add(self, rhs: Aabb) -> Self::Output {
161 rhs + self
162 }
163}
164
165impl Sub<DVec3> for Aabb {
166 type Output = Aabb;
167
168 fn sub(self, rhs: DVec3) -> Self::Output {
169 Self::new(self.min - rhs, self.max - rhs)
170 }
171}
172
173impl Sub<Aabb> for DVec3 {
174 type Output = Aabb;
175
176 fn sub(self, rhs: Aabb) -> Self::Output {
177 rhs - self
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn ray_intersect_edge_cases() {
187 let bb = Aabb::new([0.0, 0.0, 0.0].into(), [1.0, 1.0, 1.0].into());
188
189 let ros = [
190 DVec3::new(0.0, 0.0, 0.0),
192 DVec3::new(-0.5, 0.5, -0.5),
194 DVec3::new(0.5, 0.5, 0.5),
196 DVec3::new(0.0, 0.5, 0.0),
198 DVec3::new(0.0, 0.5, 0.5),
200 DVec3::new(-2.0, -2.0, -2.0),
202 ];
203
204 let rds = [
205 DVec3::new(1.0, 0.0, 0.0),
206 DVec3::new(-1.0, 0.0, 0.0),
207 DVec3::new(0.0, 1.0, 0.0),
208 DVec3::new(0.0, -1.0, 0.0),
209 DVec3::new(0.0, 0.0, 1.0),
210 DVec3::new(0.0, 0.0, -1.0),
211 ];
212
213 assert!(rds.iter().all(|d| d.is_normalized()));
214
215 for ro in ros {
216 for rd in rds {
217 if let Some([near, far]) = bb.ray_intersection(ro, rd) {
218 assert!(near.is_finite());
219 assert!(far.is_finite());
220 assert!(near <= far);
221 assert!(near >= 0.0);
222 assert!(far >= 0.0);
223 }
224 }
225 }
226 }
227}