1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/math/box.d, _box.d) 6 * Documentation: 7 * Coverage: 8 */ 9 module liberty.math.box; 10 import liberty.math.vector : Vector; 11 /// 12 struct Box(T, int N) if (N >= 1 && N <= 3) { 13 /// 14 enum dim = N; 15 /// 16 alias type = T; 17 /// 18 alias BoundType = Vector!(T, N); 19 /// 20 BoundType min; 21 /// 22 BoundType max; 23 /// 24 this(BoundType min, BoundType max) pure nothrow @safe @nogc { 25 this.min = min; 26 this.max = max; 27 } 28 static if (N == 1) { 29 /// 30 this(T min, T max) pure nothrow @safe @nogc { 31 this.min.x = min; 32 this.max.x = max; 33 } 34 } 35 static if (N == 2) { 36 /// 37 this(T min_x, T min_y, T max_x, T max_y) pure nothrow @safe @nogc { 38 min = BoundType(min_x, min_y); 39 max = BoundType(max_x, max_y); 40 } 41 } 42 static if (N == 3) { 43 this(T min_x, T min_y, T min_z, T max_x, T max_y, T max_z) pure nothrow @safe @nogc { 44 min = BoundType(min_x, min_y, min_z); 45 max = BoundType(max_x, max_y, max_z); 46 } 47 } 48 /// Returns dimensions of the box. 49 BoundType size() pure nothrow const @safe @nogc @property { 50 return max - min; 51 } 52 /// Returns center of the box. 53 BoundType center() pure nothrow const @safe @nogc @property { 54 return (min + max) / 2; 55 } 56 /// Returns width of the box. 57 T width() pure nothrow const @safe @nogc @property { 58 return max.x - min.x; 59 } 60 static if (N >= 2) { 61 /// Returns height of the box. 62 T height() pure nothrow const @safe @nogc @property { 63 return max.y - min.y; 64 } 65 } 66 static if (N >= 3) { 67 /// Returns depth of the box. 68 T depth() pure nothrow const @safe @nogc @property { 69 return max.z - min.z; 70 } 71 } 72 /// Returns signed volume of the box. 73 T volume() pure nothrow const @safe @nogc @property { 74 T ret = 1; 75 BoundType size = size(); 76 static foreach (i; 0..N) { 77 ret *= size[i]; 78 } 79 return ret; 80 } 81 /// Returns true if empty. 82 bool empty() pure nothrow const @safe @nogc @property { 83 BoundType size = size(); 84 static foreach (i; 0..N) { 85 if (min[i] == max[i]) { 86 return true; 87 } 88 } 89 return false; 90 } 91 /// Returns true if it contains point. 92 bool contains(BoundType point) pure nothrow const @safe @nogc 93 in { 94 assert(isSorted()); 95 } do { 96 static foreach (i; 0..N) { 97 if (!(point[i] >= min[i] && point[i] < max[i])) { 98 return false; 99 } 100 } 101 return true; 102 } 103 /// 104 bool contains(Box other) pure nothrow const @safe @nogc 105 in { 106 assert(isSorted()); 107 assert(other.isSorted()); 108 } do { 109 static foreach (i; 0..N) { 110 if ((other.min[i] < min[i]) || (other.max[i] > max[i])) { 111 return false; 112 } 113 } 114 return true; 115 } 116 /// 117 real squaredDistance(BoundType point) pure nothrow const @safe @nogc 118 in { 119 assert(isSorted()); 120 } do { 121 real distanceSquared = 0; 122 static foreach (i; 0..N) { 123 if (point[i] < min[i]) { 124 distanceSquared += (point[i] - min[i]) ^^ 2; 125 } 126 if (point[i] > max[i]) { 127 distanceSquared += (point[i] - max[i]) ^^ 2; 128 } 129 } 130 return distanceSquared; 131 } 132 /// 133 real distance(BoundType point) pure nothrow const @safe @nogc { 134 import liberty.math.functions : sqrt; 135 return sqrt(squaredDistance(point)); 136 } 137 /// 138 real squaredDistance(Box o) pure nothrow const @safe @nogc 139 in { 140 assert(isSorted()); 141 assert(o.isSorted()); 142 } do { 143 real distanceSquared = 0; 144 static foreach (i; 0..N) { 145 if (o.max[i] < min[i]) { 146 distanceSquared += (o.max[i] - min[i]) ^^ 2; 147 } 148 if (o.min[i] > max[i]) { 149 distanceSquared += (o.min[i] - max[i]) ^^ 2; 150 } 151 } 152 return distanceSquared; 153 } 154 /// 155 real distance(Box o) pure nothrow const @safe @nogc { 156 import liberty.math.functions : sqrt; 157 return sqrt(squaredDistance(o)); 158 } 159 /// 160 Box intersection(Box o) pure nothrow const @safe @nogc 161 in { 162 assert(isSorted()); 163 assert(o.isSorted()); 164 } do { 165 if (empty()) { 166 return this; 167 } 168 if (o.empty()) { 169 return o; 170 } 171 Box ret; 172 T maxOfMins = 0; 173 T minOfMaxs = 0; 174 static foreach (i; 0..N) { 175 maxOfMins = (min.v[i] > o.min.v[i]) ? min.v[i] : o.min.v[i]; 176 minOfMaxs = (max.v[i] < o.max.v[i]) ? max.v[i] : o.max.v[i]; 177 ret.min.v[i] = maxOfMins; 178 ret.max.v[i] = minOfMaxs >= maxOfMins ? minOfMaxs : maxOfMins; 179 } 180 return ret; 181 } 182 /// 183 bool intersects(Box other) pure nothrow const @safe @nogc { 184 Box inter = this.intersection(other); 185 return inter.isSorted() && !inter.empty(); 186 } 187 /// 188 Box grow(BoundType space) pure nothrow const @safe @nogc { 189 Box ret = this; 190 ret.min -= space; 191 ret.max += space; 192 return ret; 193 } 194 /// 195 Box shrink(BoundType space) pure nothrow const @safe @nogc { 196 return grow(-space); 197 } 198 /// 199 Box grow(T space) pure nothrow const @safe @nogc { 200 return grow(BoundType(space)); 201 } 202 /// 203 Box translate(BoundType offset) pure nothrow const @safe @nogc { 204 return Box(min + offset, max + offset); 205 } 206 /// 207 Box shrink(T space) pure nothrow const @safe @nogc { 208 return shrink(BoundType(space)); 209 } 210 /// 211 Box expand(BoundType point) pure nothrow const @safe @nogc { 212 import liberty.math.vector : minByElem, maxByElem; 213 return Box(minByElem(min, point), maxByElem(max, point)); 214 } 215 /// 216 Box expand(Box other) pure nothrow const @safe @nogc 217 in { 218 assert(isSorted()); 219 assert(other.isSorted()); 220 } do { 221 if (empty()) { 222 return other; 223 } 224 if (other.empty()) { 225 return this; 226 } 227 Box ret; 228 T minOfMins = 0; 229 T maxOfMaxs = 0; 230 static foreach (i; 0..N) { 231 minOfMins = (min.v[i] < other.min.v[i]) ? min.v[i] : other.min.v[i]; 232 maxOfMaxs = (max.v[i] > other.max.v[i]) ? max.v[i] : other.max.v[i]; 233 ret.min.v[i] = minOfMins; 234 ret.max.v[i] = maxOfMaxs; 235 } 236 return ret; 237 } 238 /// 239 bool isSorted() pure nothrow const @safe @nogc { 240 static foreach (i; 0..N) { 241 if (min[i] > max[i]) { 242 return false; 243 } 244 } 245 return true; 246 } 247 /// 248 ref Box opAssign(U)(U x) nothrow @safe @nogc if (isBox!U) { 249 static if(is(U.type : T)) { 250 static if(U.dim == dim) { 251 min = x.min; 252 max = x.max; 253 } else { 254 static assert(false, "No conversion between boxes with different dimensions available!"); 255 } 256 } else { 257 static assert(false, "No conversion from " ~ U.type.stringof ~ " to " ~ type.stringof ~ " available!"); 258 } 259 return this; 260 } 261 /// 262 bool opEquals(U)(U other) pure nothrow const @safe @nogc if (is(U : Box)) { 263 return (min == other.min) && (max == other.max); 264 } 265 /// Cast to other box types. 266 U opCast(U)() pure nothrow const @safe @nogc if (isBox!U) { 267 U b = void; 268 static foreach (i; 0..N) { 269 b.min[i] = cast(U.type)(min[i]); 270 b.max[i] = cast(U.type)(max[i]); 271 } 272 return b; 273 } 274 static if (N == 2) { 275 /// 276 static Box rectangle(T x, T y, T width, T height) pure nothrow @safe @nogc { 277 return Box(x, y, x + width, y + height); 278 } 279 } 280 } 281 /// 282 alias DimensionType(T : Box!U, U...) = U[0]; 283 /// 284 unittest { 285 static assert (is(DimensionType!Box2F == float)); 286 static assert (is(DimensionType!Box3D == double)); 287 } 288 /// 289 enum isBox(T) = is(T : Box!U, U...); 290 /// 291 pure nothrow @safe @nogc unittest { 292 static assert (isBox!Box2F); 293 static assert (isBox!Box3I); 294 static assert (isBox!(Box!(double, 2))); 295 static assert (!isBox!(Vector!(float, 3))); 296 } 297 /// 298 template Box2(T) { 299 alias Box2 = Box!(T, 2); 300 } 301 /// 302 template Box3(T) { 303 alias Box3 = Box!(T, 3); 304 } 305 /// 306 alias Box2I = Box2!int; 307 /// 308 alias Box3I = Box3!int; 309 /// 310 alias Box2F = Box2!float; 311 /// 312 alias Box3F = Box3!float; 313 /// 314 alias Box2D = Box2!double; 315 /// 316 alias Box3D = Box3!double;