1 /* 2 * Copyright (c) 2017-2019 sel-project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 */ 23 /** 24 * Copyright: Copyright (c) 2017-2019 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/selery/source/selery/math/vector.d, selery/math/vector.d) 28 */ 29 module selery.math.vector; 30 31 import std.algorithm : reverse, canFind; 32 import std.array : join, split; 33 import std.conv : to, ConvException; 34 static import std.math; 35 import std.meta : staticIndexOf; 36 import std.range.primitives : ElementType; 37 import std..string : replace; 38 import std.traits : IntegralTypeOf, isNumeric, isArray, CommonType, isFloatingPointTrait = isFloatingPoint, isImplicitlyConvertible; 39 import std.typecons : isTuple; 40 import std.typetuple : TypeTuple; 41 42 static import std.typecons; 43 44 /** 45 * Vector for coordinates storing and operations. 46 */ 47 struct Vector(T, char[] c) if(c.length > 1 && areValidCoordinates(c)) { 48 49 public alias Type = T; 50 public alias coordinates = c; 51 52 mixin("alias Tuple = std.typecons.Tuple!(T, \"" ~ join(coordinates.idup.split(""), "\", T, \"") ~ "\");"); 53 54 mixin("public enum coords = TypeTuple!('" ~ join(coordinates.idup.split(""), "','") ~ "');"); 55 56 enum bool isFloatingPoint = isFloatingPointTrait!T; 57 58 private Tuple value; 59 60 mixin((){ 61 string ret; 62 foreach(immutable c ; coords) { 63 ret ~= "public pure nothrow @property @safe @nogc T " ~ c ~ "(){ return this.value." ~ c ~ "; }"; 64 } 65 return ret; 66 }()); 67 68 public pure nothrow @safe @nogc this(Tuple value) { 69 this.value = value; 70 } 71 72 public pure nothrow @safe @nogc this(T value) { 73 foreach(immutable c ; coords) { 74 mixin("this.value." ~ c ~ " = value;"); 75 } 76 } 77 78 public @safe this(F...)(F args) if(F.length == coordinates.length) { 79 foreach(i, immutable c; coords) { 80 mixin("this.value." ~ c ~ " = cast(T)args[" ~ to!string(i) ~ "];"); 81 } 82 } 83 84 public @safe @nogc this(T[coords.length] variables) { 85 foreach(i, immutable c; coords) { 86 mixin("this.value." ~ c ~ " = variables[i];"); 87 } 88 } 89 90 public @safe @nogc this(T[] variables) { 91 foreach(i, immutable c; coords) { 92 mixin("this.value." ~ c ~ " = variables[i];"); 93 } 94 } 95 96 /** 97 * Gets the vector as a constant tuple. 98 * Example: 99 * --- 100 * auto v = vector(0, 3, 4); 101 * assert(v.tuple == typeof(v).Tuple(0, 3, 4)); 102 * --- 103 */ 104 public pure nothrow @property @safe @nogc const Tuple tuple() { 105 return this.value; 106 } 107 108 /** 109 * Compares the vector with another vector of the same length or with 110 * a single number. 111 * Returns: true if all the values are equals, false otherwise 112 * Example: 113 * --- 114 * assert(Vector2!int(0, 10) == Vector2!int(0, 10)); 115 * assert(Vector3!ubyte(1, 1, 255) == Vector3!real(1, 1, 255)); 116 * assert(vector(0, 0, 0, 0) == 0); 117 * assert(vector(1, 2) == [1, 2]); 118 * assert(vector(float.nan, float.nan) != vector(float.nan, float.nan)); 119 * --- 120 */ 121 public bool opEquals(F)(F value) { 122 static if(isVector!F && coords == F.coords) return this.opEqualsImpl!"this.{c}==value.{c}"(value); 123 else static if(isArray!F) return value.length == coords.length && this.opEqualsImpl!"this.{c}==value[{i}]"(value); 124 else static if(__traits(compiles, T.init == F.init)) return this.opEqualsImpl!"this.{c}==value"(value); 125 else return false; 126 } 127 128 private bool opEqualsImpl(string op, F)(F value) { 129 mixin((){ 130 string[] ret; 131 foreach(i, immutable c; coords) { 132 ret ~= op.replace("{c}", to!string(c)).replace("{i}", to!string(i)); 133 } 134 return "return " ~ ret.join("&&") ~ ";"; 135 }()); 136 } 137 138 /** 139 * Performs an unary operation on the vector. 140 * Returns: the new vector 141 * Example: 142 * --- 143 * auto v = vector(-1, 0, 1); 144 * assert(-v == vector(1, 0, -1)); 145 * assert(++v == vector(0, 1, 2)); // this will change the original vector's values! 146 * assert(v-- == vector(0, 1, 2) && v == vector(-1, 0, 1)); 147 * --- 148 */ 149 public typeof(this) opUnary(string op)() if(__traits(compiles, { mixin("T t;t=" ~ op ~ "t;"); })) { 150 typeof(this) ret; 151 foreach(immutable c ; coords) { 152 mixin("ret." ~ c ~ "=" ~ op ~ "this.value." ~ c ~ ";"); 153 } 154 return ret; 155 } 156 157 /** 158 * Performs a binary operation on the vector. 159 * Params: 160 * value = a number, a vector or an array with the same size 161 * Returns: the new vector 162 * Example: 163 * --- 164 * assert(vector(1, 1) - 1 == vector(0, 0)); 165 * assert(vector(10, 10) * vector(0, 9) == vector(0, 90)); 166 * assert(vector(16, 15) & [15, 3] == vector(0, 3)); 167 * assert(1 - vector(100, 0, -100) == vector(-99, 1, 101)); 168 * --- 169 */ 170 public typeof(this) opBinary(string op, F)(F value) if(op != "in") { 171 return this.dup.opOpAssign!op(value); 172 } 173 174 public typeof(this) opBinaryRight(string op, F)(F value) if(op != "in" && __traits(compiles, typeof(this)(value))) { 175 return typeof(this)(value).opBinary!op(this); 176 } 177 178 /** 179 * Performs an assign operation on the vector, modifying it. 180 * Params: 181 * value = a number, a vector or an array with the same size 182 * Returns: 183 * Example: 184 * --- 185 * auto v = vector(1, 2); 186 * v += 4; 187 * v *= [0, 2]; 188 * assert(v == vector(0, 12)); 189 * --- 190 */ 191 public typeof(this) opOpAssign(string op, F)(F value) if(isVector!F && coordinates == F.coordinates) { 192 return this.opAssignImpl!("this.value.{c}" ~ op ~ "=value.{c}")(value); 193 } 194 195 /// ditto 196 public typeof(this) opOpAssign(string op, F)(F value) if(isArray!F) { 197 return this.opAssignImpl!("this.value.{c}" ~ op ~ "=value[{i}]")(value); 198 } 199 200 /// ditto 201 public typeof(this) opOpAssign(string op, F)(F value) if(isImplicitlyConvertible!(F, T)) { 202 return this.opAssignImpl!("this.value.{c}" ~ op ~ "=value")(value); 203 } 204 205 private typeof(this) opAssignImpl(string query, F)(F value) { 206 foreach(i, immutable c; coords) { 207 mixin(query.replace("{c}", to!string(c)).replace("{i}", to!string(i)) ~ ";"); 208 } 209 return this; 210 } 211 212 /** 213 * Converts the vector to the given one, mantaining the variables's 214 * value when possible. 215 * Example: 216 * --- 217 * assert(cast(Vector2!int)vector(.1, .1, 14) == vector(0, 14)); 218 * assert(cast(Vector4!real)vector(.5, 100) == vector(.5, 0, 100, 0)); 219 * // this will only return the vector 220 * assert(cast(Vector2!int 221 * --- 222 */ 223 public @safe auto opCast(F)() if(isVector!F) { 224 static if(is(T == F) && coordinates == F.coordinates) { 225 return this; 226 } else { 227 F ret; 228 foreach(immutable c; F.coords) { 229 static if(coordinates.canFind(c)) { 230 mixin("ret.value." ~ c ~ "=to!(F.Type)(this." ~ c ~ ");"); 231 } 232 } 233 return ret; 234 } 235 } 236 237 /** 238 * Converts the vector into an array of the same size. 239 * Example: 240 * --- 241 * assert(cast(int[])vector(1, 2) == [1, 2]); 242 * assert(cast(long[])vector(.1, 1.5, -.1) == [0L, 1L, 0L]); 243 * --- 244 */ 245 public @safe auto opCast(F)() if(isArray!F) { 246 F array; 247 foreach(immutable c ; coords) { 248 mixin("array ~= to!(typeof(T.init[0]))(this." ~ c ~ ");"); 249 } 250 return array; 251 } 252 253 /** 254 * Changes the vector's type. 255 */ 256 public auto type(F)() if(isImplicitlyConvertible!(F, T)) { 257 Vector!(F, coordinates) ret; 258 foreach(immutable c ; coords) { 259 mixin("ret.value." ~ c ~ "=this." ~ c ~ ";"); 260 } 261 return ret; 262 } 263 264 /** 265 * Duplicates the vector, mantaing the type, variables' 266 * names and their value. 267 * Example: 268 * --- 269 * assert(vector(1, 1).dup == vector(1, 1)); 270 * --- 271 */ 272 alias dup = type!T; 273 274 /** 275 * Gets the vector's length. 276 */ 277 public @property double length() { 278 double length = 0; 279 foreach(immutable c ; coords) { 280 mixin("length += this.value." ~ c ~ " * this.value." ~ c ~ ";"); 281 } 282 return std.math.sqrt(length); 283 } 284 285 /** 286 * Sets the vector's length. 287 */ 288 public @property double length(double length) { 289 double mult = length / this.length; 290 foreach(immutable c ; coords) { 291 mixin("this.value." ~ c ~ " = cast(T)(this.value." ~ c ~ " * mult);"); 292 } 293 return length; 294 } 295 296 /** 297 * Converts the vector into a string for logging and debugging purposes. 298 */ 299 public string toString() { 300 string[] cs; 301 foreach(i, coord; coords) { 302 mixin("cs ~= to!string(this." ~ coord ~ ");"); 303 } 304 return "Vector!(" ~ T.stringof ~ ", \"" ~ coordinates.idup ~ "\")(" ~ cs.join(", ") ~ ")"; 305 } 306 307 } 308 309 /// ditto 310 alias Vector(T, string coords) = Vector!(T, coords.dup); 311 312 /// ditto 313 alias Vector2(T) = Vector!(T, "xz"); 314 315 /// ditto 316 alias Vector3(T) = Vector!(T, "xyz"); 317 318 /// ditto 319 alias Vector4(T) = Vector!(T, "xyzw"); 320 321 private bool areValidCoordinates(char[] coords) { 322 foreach(i, char c; coords[0..$-1]) { 323 if(coords[i+1..$].canFind(c)) return false; 324 } 325 return true; 326 } 327 328 /** 329 * Automatically creates a vector if the number of the 330 * given arguments matches one of the default vectors. 331 * Example: 332 * --- 333 * assert(is(typeof(vector(1, 1)) == Vector2!int)); 334 * assert(is(typeof(vector(2Lu, 4)) == Vector2!ulong)); 335 * assert(is(typeof(vector(5, 5, 19.0)) == Vector3!double)); 336 * assert(is(typeof(vector(0, real.nan, double.nan, float.nan)) == Vector4!real)); 337 * --- 338 */ 339 public auto vector(E...)(E args) if(E.length > 1 && E.length <= 4 && !is(CommonType!E == void)) { 340 mixin("return Vector" ~ to!string(E.length) ~ "!(CommonType!E)(args);"); 341 } 342 343 /// Checks if the given type is a vector 344 enum bool isVector(T) = __traits(compiles, Vector!(T.Type, T.coordinates)(T.Type.init)); 345 346 public nothrow @safe T mathFunction(alias func, T)(T vector) if(isVector!T) { 347 T.Type[] values; 348 foreach(immutable c ; T.coords) { 349 mixin("values ~= cast(T.Type)func(vector." ~ c ~ ");"); 350 } 351 return T(values); 352 } 353 354 /** 355 * Rounds a vector to the nearest integer. 356 * Example: 357 * --- 358 * assert(round(vector(.25, .5, .75)) == vector(0, 1, 1)); 359 * --- 360 */ 361 public nothrow @safe T round(T)(T vector) if(isVector!T) { 362 return mathFunction!(std.math.round)(vector); 363 } 364 365 /** 366 * Floors a vector to the nearest integer. 367 * Example: 368 * --- 369 * assert(floor(vector(.25, .5, .75)) == vector(0, 0, 0)); 370 * --- 371 */ 372 public nothrow @safe T floor(T)(T vector) if(isVector!T) { 373 return mathFunction!(std.math.floor)(vector); 374 } 375 376 /** 377 * Ceils a vector to the nearest integer. 378 * Example: 379 * --- 380 * assert(ceil(vector(.25, .5, .75)) == vector(1, 1, 1)); 381 * --- 382 */ 383 public nothrow @safe T ceil(T)(T vector) if(isVector!T) { 384 return mathFunction!(std.math.ceil)(vector); 385 } 386 387 /** 388 * Calculate the absolute value of the array. 389 * Example: 390 * --- 391 * assert(abs(vector(-1, 0, 90)) == vector(1, 0, 90)); 392 * --- 393 */ 394 public nothrow @safe T abs(T)(T vector) if(isVector!T) { 395 return mathFunction!(std.math.abs)(vector); 396 } 397 398 /** 399 * Checks whether or not every member of the vector is finite 400 * (not infite, -inifite, nan). 401 * Example: 402 * --- 403 * assert(isFinite(vector(1, 2))); 404 * assert(isFinite(vector(float.min, float.max))); 405 * assert(!isFinite(vector(1, float.nan))); 406 * assert(!isFinite(vector(-float.infinity, 1f/0f))); 407 * --- 408 */ 409 public pure nothrow @safe @nogc bool isFinite(T)(T vector) if(isVector!T && T.isFloatingPoint) { 410 foreach(immutable c ; T.coords) { 411 mixin("if(!std.math.isFinite(vector." ~ c ~ ")) return false;"); 412 } 413 return true; 414 } 415 416 /** 417 * Checks whether or not at least one member of the vector 418 * is not a number (nan). 419 * Example: 420 * --- 421 * assert(!isNaN(vector(0, 2.1))); 422 * assert(isNaN(vector(float.init, -double.init))); 423 * assert(isNaN(vector(0, float.nan))); 424 * --- 425 */ 426 public pure nothrow @safe @nogc bool isNaN(T)(T vector) if(isVector!T && T.isFloatingPoint) { 427 foreach(immutable c ; T.coords) { 428 mixin("if(std.math.isNaN(vector." ~ c ~ ")) return true;"); 429 } 430 return false; 431 } 432 433 public @safe double distanceSquared(F, G)(F vector1, G vector2) if(isVector!F && isVector!G && F.coordinates == G.coordinates) { 434 double sum = 0; 435 foreach(immutable c ; F.coords) { 436 mixin("sum += std.math.pow(vector1." ~ c ~ " - vector2." ~ c ~ ", 2);"); 437 } 438 return sum; 439 } 440 441 /** 442 * Calculates the distance between to vectors of the 443 * same length. 444 * Params: 445 * vector1 = the first vector 446 * vector2 = the second vector 447 * Returns: the distance between the two vectors (always higher or equals than 0) 448 * Example: 449 * --- 450 * assert(distance(vector(0, 0), vector(1, 0)) == 1); 451 * assert(distance(vector(0, 0, 0) == vector(1, 1, 1)) == 3 ^^ .5); // 3 ^^ .5 is the squared root of 3 452 * --- 453 */ 454 public @safe double distance(T, char[] coords, E)(Vector!(T, coords) vector1, Vector!(E, coords) vector2) { 455 return std.math.sqrt(distanceSquared(vector1, vector2)); 456 } 457 458 public pure nothrow @safe double dot(T, char[] coords, E)(Vector!(T, coords) vector1, Vector!(E, coords) vector2) { 459 double dot = 0; 460 foreach(immutable c ; Vector!(T, coords).coords) { 461 mixin("dot += vector1." ~ c ~ " * vector2." ~ c ~ ";"); 462 } 463 return dot; 464 } 465 466 public pure nothrow @safe Vector!(CommonType!(A, B), coords) cross(A, B, char[] coords)(Vector!(A, coords) a, Vector!(B, coords) b) { 467 foreach(immutable exc ; Vector!(T, coords).coords) { 468 469 } 470 } 471 472 /** 473 * Example: 474 * --- 475 * alias DoublePosition = Vector3!float; 476 * 477 * --- 478 */ 479 deprecated("Use the vector's constructor instead") public V vector(V, T)(T tuple) if(isVector!V && isTuple!T) { 480 mixin((){ 481 string[] ret; 482 foreach(field ; T.fieldNames) { 483 ret ~= "conv!(V.Type)(tuple." ~ field ~ ")"; 484 } 485 return "return V(" ~ ret.join(",") ~ ");"; 486 }()); 487 } 488 489 deprecated("Use vector's .tuple property instead") public T tuple(T, V)(V vector) if(isTuple!T && isVector!V) { 490 T tup; 491 mixin((){ 492 string ret = ""; 493 foreach(i, immutable c; V.coords) { 494 ret ~= "tup." ~ c ~ "=conv!(T.Types[" ~ to!string(i) ~ "])(vector." ~ c ~ ");"; 495 } 496 return ret; 497 }()); 498 return tup; 499 } 500 501 To conv(To, From)(From from) { 502 static if(is(From == To)) return from; 503 else return cast(To)from; 504 } 505 506 unittest { 507 508 Vector3!int v3 = Vector3!int(-1, 0, 12); 509 510 // comparing 511 assert(v3.x == -1); 512 assert(v3.y == 0); 513 assert(v3.z == 12); 514 assert(v3 == Vector3!int(-1, 0, 12)); 515 assert(v3 == Vector3!float(-1, 0, 12)); 516 assert(v3 != Vector3!double(-1, 0, 12.00000001)); 517 518 // unary 519 assert(-v3 == Vector3!int(1, 0, -12)); 520 assert(++v3 == Vector3!int(0, 1, 13) && v3 == Vector3!int(0, 1, 13)); 521 assert(v3-- == Vector3!int(0, 1, 13) && v3 == Vector3!int(-1, 0, 12)); 522 523 // binary operator 524 assert(v3 + 3 == Vector3!int(2, 3, 15)); 525 assert(v3 * 100 == Vector3!int(-100, 0, 1200)); 526 assert(v3 - v3 == Vector3!int(0, 0, 0)); 527 assert(Vector3!double(.5, 0, 0) + v3 == Vector3!double(-.5, 0, 12)); 528 assert(v3 * [1, 2, 3] == Vector3!int(-1, 0, 36)); 529 assert((v3 & 1) == Vector3!int(1, 0, 0)); 530 assert(1 - v3 == Vector3!int(2, 1, -11)); 531 532 // assign operator 533 assert((v3 *= 3) == Vector3!int(-3, 0, 36)); 534 assert(v3 == Vector3!int(-3, 0, 36)); 535 v3 >>= 1; 536 assert(v3 == Vector3!int(-2, 0, 18)); 537 538 // cast 539 Vector3!float v3f = cast(Vector3!float)v3; 540 Vector2!int reduced = cast(Vector2!int)v3; 541 Vector4!long bigger = cast(Vector4!long)v3; 542 assert(v3f == Vector3!float(-2, 0, 18)); 543 assert(reduced == Vector2!float(-2, 18)); 544 assert(bigger == Vector4!long(-2, 0, 18, 0)); 545 546 // vector function 547 assert(vector(8, 19).Type.stringof == "int"); 548 assert(vector(1.0, 2, 99.9).Type.stringof == "double"); 549 assert(vector(1f, .01, 12L).Type.stringof == "double"); 550 551 // math functions 552 assert(round(vector(.2, .7)) == vector(0, 1)); 553 assert(floor(vector(.2, .7)) == vector(0, 0)); 554 assert(ceil(vector(.2, .7)) == vector(1, 1)); 555 assert(abs(vector(-.2, .7)) == vector(.2, .7)); 556 557 // distance 558 assert(distance(vector(0, 0), vector(0, 1)) == 1); 559 assert(distance(vector(0, 0, 0), vector(1, 1, 1)) == 3 ^^ .5); 560 561 } 562 563 /// Vectors usually used by Minecraft 564 alias ChunkPosition = Vector2!int; 565 566 /// ditto 567 alias EntityPosition = Vector3!double; 568 569 /// ditto 570 alias BlockPosition = Vector3!int; 571 572 /// Gets a ushort that indicates a position in a chunk. 573 public @property @safe ushort shortBlockPosition(BlockPosition position) { 574 return (position.y << 8 | position.z << 4 | position.x) & ushort.max; 575 } 576 577 /// Gets a BlockPosition from an ushort that indicates a position in a chunk. 578 public @property @safe BlockPosition blockPositionShort(ushort position) { 579 return BlockPosition(cast(int)(position & 255), cast(int)(position >> 8), cast(int)((position >> 4) & 255)); 580 } 581 582 /// Casts a BlockPosition to an EntityPosition using vector's opCast. 583 public @property @safe EntityPosition entityPosition(BlockPosition block) { 584 return cast(EntityPosition)block; 585 } 586 587 /// Casts an EntityPosition to a BlockPosition using vector's opCast. 588 public @property @safe BlockPosition blockPosition(EntityPosition entity) { 589 return cast(BlockPosition)entity; 590 } 591 592 enum Face : uint { 593 594 DOWN = 0, 595 UP = 1, 596 NORTH = 2, 597 SOUTH = 3, 598 WEST = 4, 599 EAST = 5 600 601 } 602 603 // Gets the block position from a tap. 604 public @property @safe BlockPosition face(BlockPosition from, uint face) { 605 switch(face) { 606 case Face.DOWN: return from - [0, 1, 0]; 607 case Face.UP: return from + [0, 1, 0]; 608 case Face.NORTH: return from - [0, 0, 1]; 609 case Face.SOUTH: return from + [0, 0, 1]; 610 case Face.WEST: return from - [1, 0, 0]; 611 case Face.EAST: return from + [1, 0, 0]; 612 default: return from; 613 } 614 } 615 616 abstract class Box(T) { 617 618 public abstract @safe bool intersects(Box!T box); 619 public abstract @safe bool intersects(Box!T box, bool deep); 620 621 public abstract @property @safe @nogc Vector3!T minimum(); 622 public abstract @property @safe @nogc Vector3!T maximum(); 623 624 public abstract @property @safe Box!T dup(); 625 public abstract override @safe string toString(); 626 627 } 628 629 abstract class ClassicBox(T) : Box!T { 630 631 public override @safe bool intersects(Box!T box) { 632 return this.intersects(box, true); 633 } 634 635 public override @safe bool intersects(Box!T box, bool deep) { 636 Vector3!T tmin = this.minimum; 637 Vector3!T tmax = this.maximum; 638 Vector3!T bmin = box.minimum; 639 Vector3!T bmax = box.maximum; 640 if(tmin.x > bmin.x && tmin.x < bmax.x || tmax.x > bmin.x && tmax.x < bmax.x) { 641 if(tmin.y >= bmin.y && tmin.y <= bmax.y || tmax.y >= bmin.y && tmax.y <= bmax.y) { 642 if(tmin.z > bmin.z && tmin.z < bmax.z || tmax.z > bmin.z && tmax.z < bmax.z) { 643 return true; 644 } 645 } 646 } 647 return deep && box.intersects(this, false); 648 } 649 650 public override @safe string toString() { 651 return "min: " ~ this.minimum.to!string ~ ", max: " ~ this.maximum.to!string; 652 } 653 654 } 655 656 class ClassicEntityBox(T) : ClassicBox!T { 657 658 private T width, height; 659 660 private Vector3!T position; 661 private Vector3!T min, max; 662 663 public @safe this(T width, T height, Vector3!T position) { 664 this.width = width; 665 this.height = height; 666 this.position = position; 667 this.update(); 668 } 669 670 public override @safe @nogc Vector3!T minimum() { 671 return this.min; 672 } 673 674 public override @safe @nogc Vector3!T maximum() { 675 return this.max; 676 } 677 678 public @safe void update(T width, T height) { 679 this.width = width; 680 this.height = height; 681 this.update(); 682 } 683 684 public @safe void update(Vector3!T position) { 685 this.position = position; 686 this.update(); 687 } 688 689 protected @safe void update() { 690 this.min = this.position - [this.width / 2f, 0, this.width / 2f]; 691 this.max = this.position + [this.width / 2f, this.height, this.width / 2f]; 692 } 693 694 public @safe ClassicEntityBox!T grow(T width, T height) { 695 return new ClassicEntityBox!T(width * 2, height * 2, this.position- [0, height, 0]); 696 } 697 698 public override @property @safe Box!T dup() { 699 return new ClassicEntityBox!T(this.width, this.height, this.position); 700 } 701 702 } 703 704 class ClassicBlockBox(T) : ClassicBox!T { 705 706 private Vector3!T size_start, size_end; 707 private Vector3!T start, end; 708 //private bool updated; 709 710 public @safe this(Vector3!T start, Vector3!T end) /*in { assert(start.x.one && start.y.one && start.z.one && end.x.one && end.y.one && end.z.one, "Blocks' sizes must be between 0 and 1"); } body*/ { 711 this.size_start = start; 712 this.size_end = end; 713 //this.updated = false; 714 } 715 716 public @safe this(T startx, T starty, T startz, T endx, T endy, T endz) { 717 this(Vector3!T(startx, starty, startz), Vector3!T(endx, endy, endz)); 718 } 719 720 public @safe void update(Vector3!T position)/* in { assert(!this.updated, "ClassicBlockBox can only be updated once"); } body*/ { 721 this.start = position + this.size_start; 722 this.end = position + this.size_end; 723 } 724 725 public override @property @safe @nogc Vector3!T minimum() { 726 return this.start; 727 } 728 729 public override @property @safe @nogc Vector3!T maximum() { 730 return this.end; 731 } 732 733 public override @property @safe Box!T dup() { 734 ClassicBlockBox!T ret = new ClassicBlockBox!T(this.start, this.end); 735 //ret.updated = this.updated; 736 return ret; 737 } 738 739 } 740 741 /*class ComplexBox(T, bool block, E...) : Box!(T, block) { 742 743 private Box!(T, block)[] boxes; 744 745 public this(E args) { 746 super(0, 0, null, null, null); 747 foreach(e ; args) { 748 if(typeid(e) == Box!(T, block)) { 749 this.boxes ~= cast(Box!(T, block))e; 750 } 751 } 752 } 753 754 public override void update(Vector3!T position) { 755 foreach(Box!(T, block) box ; this.boxes) { 756 box.update(position); 757 } 758 } 759 760 public override @property Vector3!T minimum() { 761 Vector3!T ret; 762 foreach(Box!(T, block) box ; this.boxes) { 763 Vector3!T min = box.minimum; 764 if(ret is null || (min.x < ret.x && min.y < ret.y && min.z < ret.z)) ret = min; 765 } 766 return ret; 767 } 768 769 public override @property Vector3!T maximum() { 770 Vector3!T ret; 771 foreach(Box!(T, block) box ; this.boxes) { 772 Vector3!T max = box.maximum; 773 if(ret is null || (max.x > ret.x && max.y > ret.y && max.z > ret.z)) ret = max; 774 } 775 return ret; 776 } 777 778 public override string toString() { 779 return this.boxes.to!string; 780 } 781 782 }*/ 783 784 alias EntityAxis = ClassicEntityBox!(EntityPosition.Type); 785 alias BlockAxis = ClassicBlockBox!(EntityPosition.Type);