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) 6 * Documentation: 7 * Coverage: 8 **/ 9 module liberty.math.box; 10 11 import liberty.math.vector; 12 13 /** 14 * 15 **/ 16 struct Box(T, int N) if (N >= 1 && N <= 3) { 17 /** 18 * 19 **/ 20 enum dim = N; 21 22 /** 23 * 24 **/ 25 alias type = T; 26 27 /** 28 * 29 **/ 30 alias BoundType = Vector!(T, N); 31 32 /** 33 * 34 **/ 35 BoundType min; 36 37 /** 38 * 39 **/ 40 BoundType max; 41 42 /** 43 * 44 **/ 45 this(BoundType min, BoundType max) { 46 this.min = min; 47 this.max = max; 48 } 49 50 static if (N == 1) { 51 /** 52 * 53 **/ 54 this(T min, T max) { 55 this.min.x = min; 56 this.max.x = max; 57 } 58 } 59 60 static if (N == 2) { 61 /** 62 * 63 **/ 64 this(T min_x, T min_y, T max_x, T max_y) { 65 min = BoundType(min_x, min_y); 66 max = BoundType(max_x, max_y); 67 } 68 } 69 70 static if (N == 3) { 71 /** 72 * 73 **/ 74 this(T min_x, T min_y, T min_z, T max_x, T max_y, T max_z) { 75 min = BoundType(min_x, min_y, min_z); 76 max = BoundType(max_x, max_y, max_z); 77 } 78 } 79 80 /** 81 * Returns box dimensions. 82 **/ 83 BoundType getSize() const { 84 return max - min; 85 } 86 87 /** 88 * Returns box center. 89 **/ 90 BoundType getCenter() const { 91 return (min + max) / 2; 92 } 93 94 /** 95 * Returns box width . 96 **/ 97 T getWidth() const { 98 return max.x - min.x; 99 } 100 101 static if (N >= 2) { 102 /** 103 * Returns box height. 104 **/ 105 T getHeight() const { 106 return max.y - min.y; 107 } 108 } 109 110 111 static if (N >= 3) { 112 /** 113 * Returns depth of the box. 114 **/ 115 T getDepth() const { 116 return max.z - min.z; 117 } 118 } 119 120 /** 121 * Returns signed volume of the box. 122 **/ 123 T getVolume() const { 124 T ret = 1; 125 BoundType size = getSize(); 126 static foreach (i; 0..N) { 127 ret *= size[i]; 128 } 129 return ret; 130 } 131 132 /** 133 * Returns true if empty. 134 **/ 135 bool isEmpty() const { 136 //const BoundType size = getSize(); 137 static foreach (i; 0..N) { 138 if (min[i] == max[i]) { 139 return true; 140 } 141 } 142 return false; 143 } 144 145 /** 146 * Returns true if it contains point 147 **/ 148 bool contains(BoundType point) const 149 in { 150 assert(isSorted()); 151 } do { 152 static foreach (i; 0..N) { 153 if (!(point[i] >= min[i] && point[i] < max[i])) { 154 return false; 155 } 156 } 157 return true; 158 } 159 160 /** 161 * 162 **/ 163 bool contains(Box other) const 164 in { 165 assert(isSorted()); 166 assert(other.isSorted()); 167 } do { 168 static foreach (i; 0..N) { 169 if ((other.min[i] < min[i]) || (other.max[i] > max[i])) { 170 return false; 171 } 172 } 173 return true; 174 } 175 176 /** 177 * 178 **/ 179 real getSquaredDistance(BoundType point) const 180 in { 181 assert(isSorted()); 182 } do { 183 real distanceSquared = 0; 184 static foreach (i; 0..N) { 185 if (point[i] < min[i]) { 186 distanceSquared += (point[i] - min[i]) ^^ 2; 187 } 188 if (point[i] > max[i]) { 189 distanceSquared += (point[i] - max[i]) ^^ 2; 190 } 191 } 192 return distanceSquared; 193 } 194 195 /** 196 * 197 **/ 198 real getDistance(BoundType point) const { 199 import liberty.math.functions : sqrt; 200 return sqrt(getSquaredDistance(point)); 201 } 202 203 /** 204 * 205 **/ 206 real getSquaredDistance(Box o) const 207 in { 208 assert(isSorted()); 209 assert(o.isSorted()); 210 } do { 211 real distanceSquared = 0; 212 static foreach (i; 0..N) { 213 if (o.max[i] < min[i]) { 214 distanceSquared += (o.max[i] - min[i]) ^^ 2; 215 } 216 if (o.min[i] > max[i]) { 217 distanceSquared += (o.min[i] - max[i]) ^^ 2; 218 } 219 } 220 return distanceSquared; 221 } 222 223 /** 224 * 225 **/ 226 real getDistance(Box o) const { 227 import liberty.math.functions : sqrt; 228 return sqrt(getSquaredDistance(o)); 229 } 230 231 /** 232 * 233 **/ 234 Box getIntersection(Box o) const 235 in { 236 assert(isSorted()); 237 assert(o.isSorted()); 238 } do { 239 if (isEmpty()) { 240 return this; 241 } 242 if (o.isEmpty()) { 243 return o; 244 } 245 Box ret; 246 T maxOfMins = 0; 247 T minOfMaxs = 0; 248 static foreach (i; 0..N) { 249 maxOfMins = (min.v[i] > o.min.v[i]) ? min.v[i] : o.min.v[i]; 250 minOfMaxs = (max.v[i] < o.max.v[i]) ? max.v[i] : o.max.v[i]; 251 ret.min.v[i] = maxOfMins; 252 ret.max.v[i] = minOfMaxs >= maxOfMins ? minOfMaxs : maxOfMins; 253 } 254 return ret; 255 } 256 257 /** 258 * 259 **/ 260 bool intersects(Box other) const { 261 Box inter = this.getIntersection(other); 262 return inter.isSorted() && !inter.isEmpty(); 263 } 264 265 /** 266 * 267 **/ 268 Box grow(BoundType space) const { 269 Box ret = this; 270 ret.min -= space; 271 ret.max += space; 272 return ret; 273 } 274 275 /** 276 * 277 **/ 278 Box shrink(BoundType space) const { 279 return grow(-space); 280 } 281 282 /** 283 * 284 **/ 285 Box grow(T space) const { 286 return grow(BoundType(space)); 287 } 288 289 /** 290 * 291 **/ 292 Box translate(BoundType offset) const { 293 return Box(min + offset, max + offset); 294 } 295 296 /** 297 * 298 **/ 299 Box shrink(T space) const { 300 return shrink(BoundType(space)); 301 } 302 303 /** 304 * 305 **/ 306 Box expand(BoundType point) const { 307 import liberty.math.vector : minByElem, maxByElem; 308 return Box(minByElem(min, point), maxByElem(max, point)); 309 } 310 311 /** 312 * 313 **/ 314 Box expand(Box other) const 315 in { 316 assert(isSorted()); 317 assert(other.isSorted()); 318 } do { 319 if (isEmpty()) { 320 return other; 321 } 322 if (other.isEmpty()) { 323 return this; 324 } 325 Box ret; 326 T minOfMins = 0; 327 T maxOfMaxs = 0; 328 static foreach (i; 0..N) { 329 minOfMins = (min.v[i] < other.min.v[i]) ? min.v[i] : other.min.v[i]; 330 maxOfMaxs = (max.v[i] > other.max.v[i]) ? max.v[i] : other.max.v[i]; 331 ret.min.v[i] = minOfMins; 332 ret.max.v[i] = maxOfMaxs; 333 } 334 return ret; 335 } 336 337 /** 338 * 339 **/ 340 bool isSorted() const { 341 static foreach (i; 0..N) { 342 if (min[i] > max[i]) { 343 return false; 344 } 345 } 346 return true; 347 } 348 349 /** 350 * 351 **/ 352 ref Box opAssign(U)(U x) if (isBox!U) { 353 static if(is(U.type : T)) { 354 static if(U.dim == dim) { 355 min = x.min; 356 max = x.max; 357 } else { 358 static assert(false, "No conversion between boxes with different dimensions available!"); 359 } 360 } else { 361 static assert(false, "No conversion from " ~ U.type.stringof ~ " to " ~ type.stringof ~ " available!"); 362 } 363 return this; 364 } 365 366 /** 367 * 368 **/ 369 bool opEquals(U)(U other) const if (is(U : Box)) { 370 return (min == other.min) && (max == other.max); 371 } 372 373 /** 374 * 375 **/ 376 //size_t toHash() const { 377 // size_t hash = min.hashOf(); 378 // hash = max.hashOf(hash); 379 // return hash; 380 //} 381 382 /** 383 * Cast to other box types. 384 **/ 385 U opCast(U)() const if (isBox!U) { 386 U b = void; 387 static foreach (i; 0..N) { 388 b.min[i] = cast(U.type)(min[i]); 389 b.max[i] = cast(U.type)(max[i]); 390 } 391 return b; 392 } 393 394 static if (N == 2) { 395 /** 396 * 397 **/ 398 static Box rectangle(T x, T y, T width, T height) { 399 return Box(x, y, x + width, y + height); 400 } 401 } 402 } 403 404 /** 405 * 406 **/ 407 template Box2(T) { 408 alias Box2 = Box!(T, 2); 409 } 410 411 /** 412 * 413 **/ 414 template Box3(T) { 415 alias Box3 = Box!(T, 3); 416 } 417 418 /** 419 * 420 **/ 421 alias Box2I = Box2!int; 422 423 /** 424 * 425 **/ 426 alias Box3I = Box3!int; 427 428 /** 429 * 430 **/ 431 alias Box2F = Box2!float; 432 433 /** 434 * 435 **/ 436 alias Box3F = Box3!float; 437 438 /** 439 * 440 **/ 441 alias Box2D = Box2!double; 442 443 /** 444 * 445 **/ 446 alias Box3D = Box3!double;