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);