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;