1 module gl3n.aabb;
2 
3 private {
4     import gl3n.linalg : Vector, vec3;
5     import gl3n.math : almost_equal;
6 }
7 
8 
9 /// Base template for all AABB-types.
10 /// Params:
11 /// type = all values get stored as this type
12 struct AABBT(type) {
13     alias type at; /// Holds the internal type of the AABB.
14     alias Vector!(at, 3) vec3; /// Convenience alias to the corresponding vector type.
15 
16     vec3 min = vec3(0.0f, 0.0f, 0.0f); /// The minimum of the AABB (e.g. vec3(0, 0, 0)).
17     vec3 max = vec3(0.0f, 0.0f, 0.0f); /// The maximum of the AABB (e.g. vec3(1, 1, 1)).
18 
19     @safe pure nothrow:
20 
21     /// Constructs the AABB.
22     /// Params:
23     /// min = minimum of the AABB
24     /// max = maximum of the AABB
25     this(vec3 min, vec3 max) {
26         this.min = min;
27         this.max = max;
28     }
29 
30     /// Constructs the AABB around N points (all points will be part of the AABB).
31     static AABBT from_points(vec3[] points) {
32         AABBT res;
33 
34         if(points.length == 0) {
35             return res;
36         }
37 
38         res.min = points[0];
39         res.max = points[0];
40         foreach(v; points[1..$]) {
41             res.expand(v);
42         }
43         
44         return res;
45     }
46 
47     unittest {
48         AABB a = AABB(vec3(0.0f, 1.0f, 2.0f), vec3(1.0f, 2.0f, 3.0f));
49         assert(a.min == vec3(0.0f, 1.0f, 2.0f));
50         assert(a.max == vec3(1.0f, 2.0f, 3.0f));
51 
52         a = AABB.from_points([vec3(0.0f, 0.0f, 0.0f), vec3(-1.0f, 2.0f, 3.0f), vec3(0.0f, 0.0f, 4.0f)]);
53         assert(a.min == vec3(-1.0f, 0.0f, 0.0f));
54         assert(a.max == vec3(0.0f, 2.0f, 4.0f));
55         
56         a = AABB.from_points([vec3(1.0f, 1.0f, 1.0f), vec3(2.0f, 2.0f, 2.0f)]);
57         assert(a.min == vec3(1.0f, 1.0f, 1.0f));
58         assert(a.max == vec3(2.0f, 2.0f, 2.0f));
59     }
60 
61     /// Expands the AABB by another AABB. 
62     void expand(AABBT b) {
63         if (min.x > b.min.x) min.x = b.min.x;
64         if (min.y > b.min.y) min.y = b.min.y;
65         if (min.z > b.min.z) min.z = b.min.z;
66         if (max.x < b.max.x) max.x = b.max.x;
67         if (max.y < b.max.y) max.y = b.max.y;
68         if (max.z < b.max.z) max.z = b.max.z;
69     }
70 
71     /// Expands the AABB, so that $(I v) is part of the AABB.
72     void expand(vec3 v) {
73         if (v.x > max.x) max.x = v.x;
74         if (v.y > max.y) max.y = v.y;
75         if (v.z > max.z) max.z = v.z;
76         if (v.x < min.x) min.x = v.x;
77         if (v.y < min.y) min.y = v.y;
78         if (v.z < min.z) min.z = v.z;
79     }
80 
81     unittest {
82         AABB a = AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 4.0f, 1.0f));
83         AABB b = AABB(vec3(2.0f, -1.0f, 2.0f), vec3(3.0f, 3.0f, 3.0f));
84 
85         AABB c;
86         c.expand(a);
87         c.expand(b);
88         assert(c.min == vec3(0.0f, -1.0f, 0.0f));
89         assert(c.max == vec3(3.0f, 4.0f, 3.0f));
90 
91         c.expand(vec3(12.0f, -12.0f, 0.0f));
92         assert(c.min == vec3(0.0f, -12.0f, 0.0f));
93         assert(c.max == vec3(12.0f, 4.0f, 3.0f));
94     }
95 
96     /// Returns true if the AABBs intersect.
97     /// This also returns true if one AABB lies inside another.
98     bool intersects(AABBT box) const {
99         return (min.x < box.max.x && max.x > box.min.x) &&
100                (min.y < box.max.y && max.y > box.min.y) &&
101                (min.z < box.max.z && max.z > box.min.z);
102     }
103 
104     unittest {
105         assert(AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 1.0f, 1.0f)).intersects(
106                AABB(vec3(0.5f, 0.5f, 0.5f), vec3(3.0f, 3.0f, 3.0f))));
107 
108         assert(AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 1.0f, 1.0f)).intersects(
109                AABB(vec3(0.5f, 0.5f, 0.5f), vec3(0.7f, 0.7f, 0.7f))));
110 
111         assert(!AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 1.0f, 1.0f)).intersects(
112                 AABB(vec3(1.5f, 1.5f, 1.5f), vec3(3.0f, 3.0f, 3.0f))));
113     }
114 
115     /// Returns the extent of the AABB (also sometimes called size).
116     @property vec3 extent() const {
117         return max - min;
118     }
119 
120     /// Returns the half extent.
121     @property vec3 half_extent() const {
122         return 0.5 * (max - min);
123     }
124 
125     unittest {
126         AABB a = AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 1.0f, 1.0f));
127         assert(a.extent == vec3(1.0f, 1.0f, 1.0f));
128         assert(a.half_extent == 0.5 * a.extent);
129 
130         AABB b = AABB(vec3(0.2f, 0.2f, 0.2f), vec3(1.0f, 1.0f, 1.0f));
131         assert(b.extent == vec3(0.8f, 0.8f, 0.8f));
132         assert(b.half_extent == 0.5 * b.extent);
133         
134     }
135 
136     /// Returns the area of the AABB.
137     @property at area() const {
138         vec3 e = extent;
139         return 2.0 * (e.x * e.y + e.x * e.z + e.y * e.z);
140     }
141 
142     unittest {
143         AABB a = AABB(vec3(0.0f, 0.0f, 0.0f), vec3(1.0f, 1.0f, 1.0f));
144         assert(a.area == 6);
145 
146         AABB b = AABB(vec3(0.2f, 0.2f, 0.2f), vec3(1.0f, 1.0f, 1.0f));
147         assert(almost_equal(b.area, 3.84f));
148 
149         AABB c = AABB(vec3(0.2f, 0.4f, 0.6f), vec3(1.0f, 1.0f, 1.0f));
150         assert(almost_equal(c.area, 2.08f));
151     }
152 
153     /// Returns the center of the AABB.
154     @property vec3 center() const {
155         return 0.5 * (max + min);
156     }
157 
158     unittest {
159         AABB a = AABB(vec3(0.5f, 0.5f, 0.5f), vec3(1.0f, 1.0f, 1.0f));
160         assert(a.center == vec3(0.75f, 0.75f, 0.75f));
161     }
162 
163     /// Returns all vertices of the AABB, basically one vec3 per corner.
164     @property vec3[] vertices() const {
165         return [
166             vec3(min.x, min.y, min.z),
167             vec3(min.x, min.y, max.z),
168             vec3(min.x, max.y, min.z),
169             vec3(min.x, max.y, max.z),
170             vec3(max.x, min.y, min.z),
171             vec3(max.x, min.y, max.z),
172             vec3(max.x, max.y, min.z),
173             vec3(max.x, max.y, max.z),
174         ];
175     }
176 
177     bool opEquals(AABBT other) const {
178         return other.min == min && other.max == max;
179     }
180 
181     unittest {
182         assert(AABB(vec3(1.0f, 12.0f, 14.0f), vec3(33.0f, 222.0f, 342.0f)) ==
183                AABB(vec3(1.0f, 12.0f, 14.0f), vec3(33.0f, 222.0f, 342.0f)));
184     }
185 }
186 
187 alias AABBT!(float) AABB;