viewport_lib/scene/
aabb.rs1#[derive(Debug, Clone, Copy)]
5pub struct Aabb {
6 pub min: glam::Vec3,
8 pub max: glam::Vec3,
10}
11
12impl Aabb {
13 pub fn from_positions(positions: &[[f32; 3]]) -> Self {
17 if positions.is_empty() {
18 return Self {
19 min: glam::Vec3::ZERO,
20 max: glam::Vec3::ZERO,
21 };
22 }
23 let mut min = glam::Vec3::splat(f32::INFINITY);
24 let mut max = glam::Vec3::splat(f32::NEG_INFINITY);
25 for p in positions {
26 let v = glam::Vec3::from(*p);
27 min = min.min(v);
28 max = max.max(v);
29 }
30 Self { min, max }
31 }
32
33 pub fn center(&self) -> glam::Vec3 {
35 (self.min + self.max) * 0.5
36 }
37
38 pub fn half_extents(&self) -> glam::Vec3 {
40 (self.max - self.min) * 0.5
41 }
42
43 pub fn transformed(&self, mat: &glam::Mat4) -> Self {
46 let corners = [
47 glam::Vec3::new(self.min.x, self.min.y, self.min.z),
48 glam::Vec3::new(self.max.x, self.min.y, self.min.z),
49 glam::Vec3::new(self.min.x, self.max.y, self.min.z),
50 glam::Vec3::new(self.max.x, self.max.y, self.min.z),
51 glam::Vec3::new(self.min.x, self.min.y, self.max.z),
52 glam::Vec3::new(self.max.x, self.min.y, self.max.z),
53 glam::Vec3::new(self.min.x, self.max.y, self.max.z),
54 glam::Vec3::new(self.max.x, self.max.y, self.max.z),
55 ];
56 let mut new_min = glam::Vec3::splat(f32::INFINITY);
57 let mut new_max = glam::Vec3::splat(f32::NEG_INFINITY);
58 for c in &corners {
59 let t = mat.transform_point3(*c);
60 new_min = new_min.min(t);
61 new_max = new_max.max(t);
62 }
63 Self {
64 min: new_min,
65 max: new_max,
66 }
67 }
68
69 pub fn intersects_plane(&self, normal: glam::Vec3, distance: f32) -> bool {
72 let center = self.center();
73 let extents = self.half_extents();
74 let r =
75 extents.x * normal.x.abs() + extents.y * normal.y.abs() + extents.z * normal.z.abs();
76 let d = normal.dot(center) + distance;
77 d.abs() <= r
78 }
79}
80
81impl Default for Aabb {
82 fn default() -> Self {
83 Self {
84 min: glam::Vec3::ZERO,
85 max: glam::Vec3::ZERO,
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 fn unit_cube_positions() -> Vec<[f32; 3]> {
95 vec![
96 [-0.5, -0.5, -0.5],
97 [0.5, -0.5, -0.5],
98 [0.5, 0.5, -0.5],
99 [-0.5, 0.5, -0.5],
100 [-0.5, -0.5, 0.5],
101 [0.5, -0.5, 0.5],
102 [0.5, 0.5, 0.5],
103 [-0.5, 0.5, 0.5],
104 ]
105 }
106
107 #[test]
108 fn test_aabb_from_unit_cube() {
109 let aabb = Aabb::from_positions(&unit_cube_positions());
110 assert!((aabb.min - glam::Vec3::splat(-0.5)).length() < 1e-5);
111 assert!((aabb.max - glam::Vec3::splat(0.5)).length() < 1e-5);
112 }
113
114 #[test]
115 fn test_aabb_transformed_identity() {
116 let aabb = Aabb::from_positions(&unit_cube_positions());
117 let transformed = aabb.transformed(&glam::Mat4::IDENTITY);
118 assert!((transformed.min - aabb.min).length() < 1e-5);
119 assert!((transformed.max - aabb.max).length() < 1e-5);
120 }
121
122 #[test]
123 fn test_aabb_transformed_translated() {
124 let aabb = Aabb::from_positions(&unit_cube_positions());
125 let mat = glam::Mat4::from_translation(glam::Vec3::new(10.0, 20.0, 30.0));
126 let transformed = aabb.transformed(&mat);
127 assert!((transformed.min - glam::Vec3::new(9.5, 19.5, 29.5)).length() < 1e-5);
128 assert!((transformed.max - glam::Vec3::new(10.5, 20.5, 30.5)).length() < 1e-5);
129 }
130
131 #[test]
132 fn test_aabb_center_and_extents() {
133 let aabb = Aabb::from_positions(&unit_cube_positions());
134 assert!(aabb.center().length() < 1e-5);
135 assert!((aabb.half_extents() - glam::Vec3::splat(0.5)).length() < 1e-5);
136 }
137
138 #[test]
139 fn test_aabb_from_empty() {
140 let aabb = Aabb::from_positions(&[]);
141 assert!((aabb.min - glam::Vec3::ZERO).length() < 1e-5);
142 assert!((aabb.max - glam::Vec3::ZERO).length() < 1e-5);
143 }
144
145 #[test]
146 fn test_intersects_plane_through_center() {
147 let aabb = Aabb::from_positions(&unit_cube_positions());
148 assert!(aabb.intersects_plane(glam::Vec3::X, 0.0));
150 }
151
152 #[test]
153 fn test_intersects_plane_outside() {
154 let aabb = Aabb::from_positions(&unit_cube_positions());
155 assert!(!aabb.intersects_plane(glam::Vec3::X, -2.0));
157 assert!(!aabb.intersects_plane(glam::Vec3::X, 2.0));
159 }
160
161 #[test]
162 fn test_intersects_plane_tangent() {
163 let aabb = Aabb::from_positions(&unit_cube_positions());
164 assert!(aabb.intersects_plane(glam::Vec3::X, -0.5));
166 }
167}