2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // various vector and matrix operations
18 // matrix should be compatible with OpenGL, but mostly untested
19 module iv
.vmath
/*is aliced*/;
22 //version = aabbtree_many_asserts;
23 version = aabbtree_query_count
;
24 version = vmath_slow_normalize
;
27 // ////////////////////////////////////////////////////////////////////////// //
28 version(vmath_double
) {
29 alias VFloat
= double;
33 enum VFloatNum(long v
) = cast(VFloat
)v
;
34 enum VFloatNum(float v
) = cast(VFloat
)v
;
35 enum VFloatNum(double v
) = cast(VFloat
)v
;
36 enum VFloatNum(real v
) = cast(VFloat
)v
;
39 // ////////////////////////////////////////////////////////////////////////// //
40 /// is `v` finite (i.e. not a nan and not an infinity)?
41 public bool isFiniteDbl (in double v
) pure nothrow @trusted @nogc {
43 return (((*cast(const(ulong)*)&v
)&0x7ff0_0000_0000_0000UL) != 0x7ff0_0000_0000_0000UL);
47 /// is `v` finite (i.e. not a nan and not an infinity)?
48 public bool isFiniteFlt (in float v
) pure nothrow @trusted @nogc {
50 return (((*cast(const(uint)*)&v
)&0x7f80_0000U) != 0x7f80_0000U);
54 // ////////////////////////////////////////////////////////////////////////// //
55 private template isGoodSwizzling(string s
, string comps
, int minlen
, int maxlen
) {
56 private static template hasChar(string
str, char ch
, uint idx
=0) {
57 static if (idx
>= str.length
) enum hasChar
= false;
58 else static if (str[idx
] == ch
) enum hasChar
= true;
59 else enum hasChar
= hasChar
!(str, ch
, idx
+1);
61 private static template swz(string
str, string comps
, uint idx
=0) {
62 static if (idx
>= str.length
) enum swz
= true;
63 else static if (hasChar
!(comps
, str[idx
])) enum swz
= swz
!(str, comps
, idx
+1);
64 else enum swz
= false;
66 static if (s
.length
>= minlen
&& s
.length
<= maxlen
) enum isGoodSwizzling
= swz
!(s
, comps
);
67 else enum isGoodSwizzling
= false;
70 private template SwizzleCtor(string stn
, string s
) {
71 private static template buildCtor (string s
, uint idx
) {
72 static if (idx
>= s
.length
) enum buildCtor
= "";
73 else enum buildCtor
= s
[idx
]~","~buildCtor
!(s
, idx
+1);
75 enum SwizzleCtor
= stn
~"("~buildCtor
!(s
, 0)~")";
79 // ////////////////////////////////////////////////////////////////////////// //
80 // Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody.
81 // Wolfram Alpha LLC. 2011. Wolfram|Alpha. http://www.wolframalpha.com/input/?i=e+in+base+16 (access July 6, 2011).
82 enum real E_R
= 0x1.5bf0a
8b1457695355fb8ac
404e
7a
8p
+1L; /* e = 2.718281... */
83 enum real LOG2T_R
= 0x1.a934f0979a3715fc9257edfe9b5fbp
+1L; /* log2 10 = 3.321928... */
84 enum real LOG2E_R
= 0x1.71547652b82fe
1777d0ffda
0d23a
8p
+0L; /* log2 e = 1.442695... */
85 enum real LOG2_R
= 0x1.34413509f79fef
311f12b35816f92p
-2L; /* log10 2 = 0.301029... */
86 enum real LOG10E_R
= 0x1.bcb7b1526e50e32a6ab7555f5a67cp
-2L; /* log10 e = 0.434294... */
87 enum real LN2_R
= 0x1.62e42fefa39ef35793c7673007e5fp
-1L; /* ln 2 = 0.693147... */
88 enum real LN10_R
= 0x1.26bb1bbb5551582dd4adac
5705a
61p
+1L; /* ln 10 = 2.302585... */
89 enum real PI_R
= 0x1.921fb54442d18469898cc
51701b84p
+1L; /* PI = 3.141592... */
90 enum real PI_2_R
= PI_R
/2; /* PI / 2 = 1.570796... */
91 enum real PI_4_R
= PI_R
/4; /* PI / 4 = 0.785398... */
92 enum real M_1_PI_R
= 0x1.45f306dc
9c
882a
53f84eafa
3ea
69cp
-2L; /* 1 / PI = 0.318309... */
93 enum real M_2_PI_R
= 2*M_1_PI_R
; /* 2 / PI = 0.636619... */
94 enum real M_2_SQRTPI_R
= 0x1.20dd750429b6d11ae
3a
914fed
7fd8p
+0L; /* 2 / sqrt(PI) = 1.128379... */
95 enum real SQRT2_R
= 0x1.6a09e667f3bcc908b2fb1366ea958p
+0L; /* sqrt(2) = 1.414213... */
96 enum real SQRT1_2_R
= SQRT2_R
/2; /* sqrt(1/2) = 0.707106... */
98 enum double E_D
= cast(double)0x1.5bf0a
8b1457695355fb8ac
404e
7a
8p
+1L; /* e = 2.718281... */
99 enum double LOG2T_D
= cast(double)0x1.a934f0979a3715fc9257edfe9b5fbp
+1L; /* log2 10 = 3.321928... */
100 enum double LOG2E_D
= cast(double)0x1.71547652b82fe
1777d0ffda
0d23a
8p
+0L; /* log2 e = 1.442695... */
101 enum double LOG2_D
= cast(double)0x1.34413509f79fef
311f12b35816f92p
-2L; /* log10 2 = 0.301029... */
102 enum double LOG10E_D
= cast(double)0x1.bcb7b1526e50e32a6ab7555f5a67cp
-2L; /* log10 e = 0.434294... */
103 enum double LN2_D
= cast(double)0x1.62e42fefa39ef35793c7673007e5fp
-1L; /* ln 2 = 0.693147... */
104 enum double LN10_D
= cast(double)0x1.26bb1bbb5551582dd4adac
5705a
61p
+1L; /* ln 10 = 2.302585... */
105 enum double PI_D
= cast(double)0x1.921fb54442d18469898cc
51701b84p
+1L; /* PI = 3.141592... */
106 enum double PI_2_D
= cast(double)(PI_R
/2); /* PI / 2 = 1.570796... */
107 enum double PI_4_D
= cast(double)(PI_R
/4); /* PI / 4 = 0.785398... */
108 enum double M_1_PI_D
= cast(double)0x1.45f306dc
9c
882a
53f84eafa
3ea
69cp
-2L; /* 1 / PI = 0.318309... */
109 enum double M_2_PI_D
= cast(double)(2*M_1_PI_R
); /* 2 / PI = 0.636619... */
110 enum double M_2_SQRTPI_D
= cast(double)0x1.20dd750429b6d11ae
3a
914fed
7fd8p
+0L; /* 2 / sqrt(PI) = 1.128379... */
111 enum double SQRT2_D
= cast(double)0x1.6a09e667f3bcc908b2fb1366ea958p
+0L; /* sqrt(2) = 1.414213... */
112 enum double SQRT1_2_D
= cast(double)(SQRT2_R
/2); /* sqrt(1/2) = 0.707106... */
114 enum float E_F
= cast(float)0x1.5bf0a
8b1457695355fb8ac
404e
7a
8p
+1L; /* e = 2.718281... */
115 enum float LOG2T_F
= cast(float)0x1.a934f0979a3715fc9257edfe9b5fbp
+1L; /* log2 10 = 3.321928... */
116 enum float LOG2E_F
= cast(float)0x1.71547652b82fe
1777d0ffda
0d23a
8p
+0L; /* log2 e = 1.442695... */
117 enum float LOG2_F
= cast(float)0x1.34413509f79fef
311f12b35816f92p
-2L; /* log10 2 = 0.301029... */
118 enum float LOG10E_F
= cast(float)0x1.bcb7b1526e50e32a6ab7555f5a67cp
-2L; /* log10 e = 0.434294... */
119 enum float LN2_F
= cast(float)0x1.62e42fefa39ef35793c7673007e5fp
-1L; /* ln 2 = 0.693147... */
120 enum float LN10_F
= cast(float)0x1.26bb1bbb5551582dd4adac
5705a
61p
+1L; /* ln 10 = 2.302585... */
121 enum float PI_F
= cast(float)0x1.921fb54442d18469898cc
51701b84p
+1L; /* PI = 3.141592... */
122 enum float PI_2_F
= cast(float)(PI_R
/2); /* PI / 2 = 1.570796... */
123 enum float PI_4_F
= cast(float)(PI_R
/4); /* PI / 4 = 0.785398... */
124 enum float M_1_PI_F
= cast(float)0x1.45f306dc
9c
882a
53f84eafa
3ea
69cp
-2L; /* 1 / PI = 0.318309... */
125 enum float M_2_PI_F
= cast(float)(2*M_1_PI_R
); /* 2 / PI = 0.636619... */
126 enum float M_2_SQRTPI_F
= cast(float)0x1.20dd750429b6d11ae
3a
914fed
7fd8p
+0L; /* 2 / sqrt(PI) = 1.128379... */
127 enum float SQRT2_F
= cast(float)0x1.6a09e667f3bcc908b2fb1366ea958p
+0L; /* sqrt(2) = 1.414213... */
128 enum float SQRT1_2_F
= cast(float)(SQRT2_R
/2); /* sqrt(1/2) = 0.707106... */
131 // ////////////////////////////////////////////////////////////////////////// //
132 private template IsKnownVMathConstant(string name
) {
133 static if (name
== "E" || name
== "LOG2T" || name
== "LOG2E" || name
== "LOG2" || name
== "LOG10E" ||
134 name
== "LN2" || name
== "LN10" || name
== "PI" || name
== "PI_2" || name
== "PI_4" ||
135 name
== "M_1_PI" || name
== "M_2_PI" || name
== "M_2_SQRTPI" || name
== "SQRT2" || name
== "SQRT1_2")
137 enum IsKnownVMathConstant
= true;
139 enum IsKnownVMathConstant
= false;
143 template ImportCoreMath(FloatType
, T
...) {
145 (is(FloatType
== float) ||
is(FloatType
== const float) ||
is(FloatType
== immutable float)) ||
146 (is(FloatType
== double) ||
is(FloatType
== const double) ||
is(FloatType
== immutable double)),
147 "import type should be `float` or `double`");
148 private template InternalImport(T
...) {
149 static if (T
.length
== 0) enum InternalImport
= "";
150 else static if (is(typeof(T
[0]) == string
)) {
151 static if (T
[0] == "fabs" || T
[0] == "cos" || T
[0] == "sin" || T
[0] == "sqrt") {
152 enum InternalImport
= "import core.math : "~T
[0]~";"~InternalImport
!(T
[1..$]);
153 } else static if (T
[0] == "min" || T
[0] == "nmin") {
154 enum InternalImport
= "static T "~T
[0]~"(T) (in T a, in T b) { pragma(inline, true); return (a < b ? a : b); }"~InternalImport
!(T
[1..$]);
155 } else static if (T
[0] == "max" || T
[0] == "nmax") {
156 enum InternalImport
= "static T "~T
[0]~"(T) (in T a, in T b) { pragma(inline, true); return (a > b ? a : b); }"~InternalImport
!(T
[1..$]);
157 } else static if (IsKnownVMathConstant
!(T
[0])) {
158 static if (is(FloatType
== float) ||
is(FloatType
== const float) ||
is(FloatType
== immutable float)) {
159 enum InternalImport
= "enum "~T
[0]~"="~T
[0]~"_F;"~InternalImport
!(T
[1..$]);
161 enum InternalImport
= "enum "~T
[0]~"="~T
[0]~"_D;"~InternalImport
!(T
[1..$]);
163 } else static if (T
[0] == "isnan") {
164 enum InternalImport
= "import core.stdc.math : isnan;"~InternalImport
!(T
[1..$]);
165 } else static if (T
[0] == "isfinite") {
166 static if (is(FloatType
== float) ||
is(FloatType
== const float) ||
is(FloatType
== immutable float)) {
167 enum InternalImport
= "import iv.nanpay : isfinite = isFinite;"~InternalImport
!(T
[1..$]);
169 enum InternalImport
= "import iv.nanpay : isfinite = isFiniteD;"~InternalImport
!(T
[1..$]);
171 } else static if (is(FloatType
== float) ||
is(FloatType
== const float) ||
is(FloatType
== immutable float)) {
172 enum InternalImport
= "import core.stdc.math : "~T
[0]~"="~T
[0]~"f;"~InternalImport
!(T
[1..$]);
174 enum InternalImport
= "import core.stdc.math : "~T
[0]~";"~InternalImport
!(T
[1..$]);
177 else static assert(0, "string expected");
179 static if (T
.length
> 0) {
180 enum ImportCoreMath
= InternalImport
!T
;
182 enum ImportCoreMath
= "{}";
187 // ////////////////////////////////////////////////////////////////////////// //
189 enum DBLEPS
= 1.0e-18;
190 template EPSILON(T
) if (is(T
== float) ||
is(T
== double)) {
191 static if (is(T
== float)) enum EPSILON
= FLTEPS
;
192 else static if (is(T
== double)) enum EPSILON
= DBLEPS
;
193 else static assert(0, "wtf?!");
195 template SMALLEPSILON(T
) if (is(T
== float) ||
is(T
== double)) {
196 static if (is(T
== float)) enum SMALLEPSILON
= 1e-5f;
197 else static if (is(T
== double)) enum SMALLEPSILON
= 1.0e-9;
198 else static assert(0, "wtf?!");
201 auto deg2rad(T
:double) (T v
) pure nothrow @safe @nogc {
202 pragma(inline
, true);
203 static if (__traits(isFloating
, T
)) {
204 static if (is(T
== float)) alias PI
= PI_F
; else alias PI
= PI_D
;
205 return cast(T
)(v
*cast(T
)PI
/cast(T
)180);
207 static if (is(VFloat
== float)) alias PI
= PI_F
; else alias PI
= PI_D
;
208 return cast(VFloat
)(cast(VFloat
)v
*cast(VFloat
)PI
/cast(VFloat
)180);
212 auto rad2deg(T
:double) (T v
) pure nothrow @safe @nogc {
213 pragma(inline
, true);
214 static if (__traits(isFloating
, T
)) {
215 static if (is(T
== float)) alias PI
= PI_F
; else alias PI
= PI_D
;
216 return cast(T
)(v
*cast(T
)180/cast(T
)PI
);
218 static if (is(VFloat
== float)) alias PI
= PI_F
; else alias PI
= PI_D
;
219 return cast(VFloat
)(cast(VFloat
)v
*cast(VFloat
)180/cast(VFloat
)PI
);
224 // ////////////////////////////////////////////////////////////////////////// //
227 //alias AABB2 = AABBImpl!vec2;
230 // ////////////////////////////////////////////////////////////////////////// //
231 template IsVector(VT
) {
232 static if (is(VT
== VecN
!(D
, T
), ubyte D
, T
)) {
233 enum IsVector
= (D
== 2 || D
== 3);
235 enum IsVector
= false;
239 template IsVectorDim(VT
, ubyte dim
) {
240 static if (is(VT
== VecN
!(D
, T
), ubyte D
, T
)) {
241 enum IsVectorDim
= (D
== dim
);
243 enum IsVectorDim
= false;
247 template IsVector(VT
, FT
) {
248 static if (is(VT
== VecN
!(D
, T
), ubyte D
, T
)) {
249 enum IsVector
= ((D
== 2 || D
== 3) && is(T
== FT
));
251 enum IsVector
= false;
255 template IsVectorDim(VT
, FT
, ubyte dim
) {
256 static if (is(VT
== VecN
!(D
, T
), ubyte D
, T
)) {
257 enum IsVectorDim
= (D
== dim
&& is(T
== FT
));
259 enum IsVectorDim
= false;
263 template VectorDims(VT
) {
264 static if (is(VT
== VecN
!(D
, F
), ubyte D
, F
)) {
271 template VectorFloat(VT
) {
272 static if (is(VT
== VecN
!(D
, F
), ubyte D
, F
)) {
273 alias VectorFloat
= F
;
275 static assert(0, "not a vector");
280 // ////////////////////////////////////////////////////////////////////////// //
281 align(1) struct VecN(ubyte dims
, FloatType
=VFloat
) if (dims
>= 2 && dims
<= 3 && (is(FloatType
== float) ||
is(FloatType
== double))) {
284 static T
nmin(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
< b ? a
: b
); }
285 static T
nmax(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
> b ? a
: b
); }
288 enum isVector(VT
) = (is(VT
== VecN
!(2, FloatType
)) ||
is(VT
== VecN
!(3, FloatType
)));
289 enum isVector2(VT
) = is(VT
== VecN
!(2, FloatType
));
290 enum isVector3(VT
) = is(VT
== VecN
!(3, FloatType
));
291 enum isSameVector(VT
) = is(VT
== VecN
!(dims
, FloatType
));
293 alias v2
= VecN
!(2, FloatType
);
294 alias v3
= VecN
!(3, FloatType
);
296 alias Me
= typeof(this);
297 alias Float
= FloatType
;
299 enum Epsilon
= EPSILON
!Float
;
304 static if (dims
>= 3) Float z
= 0;
307 string
toString () const {
308 import std
.string
: format
;
310 static if (dims
== 2) return "(%s,%s)".format(x
, y
);
311 else static if (dims
== 3) return "(%s,%s,%s)".format(x
, y
, z
);
312 else static assert(0, "invalid dimension count for vector");
313 } catch (Exception
) {
319 inout(Float
)* unsafePtr () inout nothrow @trusted @nogc { pragma(inline
, true); return cast(typeof(return))&x
; }
322 this (in Float
[] c
) @trusted {
323 x
= (c
.length
>= 1 ? c
.ptr
[0] : 0);
324 y
= (c
.length
>= 2 ? c
.ptr
[1] : 0);
325 static if (dims
== 3) z
= (c
.length
>= 3 ? c
.ptr
[2] : 0);
328 this (in Float ax
, in Float ay
) pure {
329 pragma(inline
, true);
332 static if (dims
== 3) z
= 0;
335 this (in Float ax
, in Float ay
, in Float az
) pure {
336 //pragma(inline, true);
339 static if (dims
== 3) z
= az
;
342 this(VT
) (in auto ref VT v
) pure if (isVector
!VT
) {
343 //pragma(inline, true);
346 static if (dims
== 3) {
347 static if (isVector3
!VT
) z
= v
.z
; else z
= 0;
351 static Me
Zero () pure nothrow @safe @nogc { pragma(inline
, true); return Me(0, 0); }
352 static Me
Invalid () pure nothrow @safe @nogc { pragma(inline
, true); return Me(Float
.nan
, Float
.nan
); }
354 // infinites are invalid too
355 @property bool valid () const nothrow @safe @nogc {
357 pragma(inline, true);
358 import core.stdc.math : isnan;
359 static if (dims == 2) return !isnan(x) && !isnan(y);
360 else static if (dims == 3) return !isnan(x) && !isnan(y) && !isnan(z);
361 else static assert(0, "invalid dimension count for vector");
363 // this also reject nans
364 pragma(inline
, true);
365 import core
.stdc
.math
: isfinite
;
366 static if (dims
== 2) return isfinite(x
) && isfinite(y
);
367 else static if (dims
== 3) return isfinite(x
) && isfinite(y
) && isfinite(z
);
368 else static assert(0, "invalid dimension count for vector");
371 // this also reject nans
372 @property bool isFinite () const nothrow @safe @nogc {
373 pragma(inline
, true);
374 import core
.stdc
.math
: isfinite
;
375 static if (dims
== 2) return isfinite(x
) && isfinite(y
);
376 else static if (dims
== 3) return isfinite(x
) && isfinite(y
) && isfinite(z
);
377 else static assert(0, "invalid dimension count for vector");
380 @property bool isZero () const nothrow @safe @nogc {
381 pragma(inline
, true);
382 static if (dims
== 2) return (x
== 0 && y
== 0);
383 else static if (dims
== 3) return (x
== 0 && y
== 0 && z
== 0);
384 else static assert(0, "invalid dimension count for vector");
387 @property bool isNearZero () const nothrow @safe @nogc {
388 pragma(inline
, true);
389 mixin(ImportCoreMath
!(Float
, "fabs"));
390 static if (dims
== 2) return (fabs(x
) < EPSILON
!Float
&& fabs(y
) < EPSILON
!Float
);
391 else static if (dims
== 3) return (fabs(x
) < EPSILON
!Float
&& fabs(y
) < EPSILON
!Float
&& fabs(z
) < EPSILON
!Float
);
392 else static assert(0, "invalid dimension count for vector");
395 void set (in Float
[] c
...) @trusted {
396 x
= (c
.length
>= 1 ? c
.ptr
[0] : 0);
397 y
= (c
.length
>= 2 ? c
.ptr
[1] : 0);
398 static if (dims
== 3) z
= (c
.length
>= 3 ? c
.ptr
[2] : 0);
401 static if (dims
== 2)
402 void set (in Float ax
, in Float ay
) {
403 //pragma(inline, true);
408 static if (dims
== 3)
409 void set (in Float ax
, in Float ay
, in Float az
) {
410 //pragma(inline, true);
416 void opAssign(VT
) (in auto ref VT v
) if (isVector
!VT
) {
417 //pragma(inline, true);
420 static if (dims
== 3) {
421 static if (isVector3
!VT
) z
= v
.z
; else z
= 0;
425 Float
opIndex (usize idx
) const {
426 pragma(inline
, true);
427 static if (dims
== 2) return (idx
== 0 ? x
: idx
== 1 ? y
: Float
.nan
);
428 else static if (dims
== 3) return (idx
== 0 ? x
: idx
== 1 ? y
: idx
== 2 ? z
: Float
.nan
);
429 else static assert(0, "invalid dimension count for vector");
432 void opIndexAssign (Float v
, usize idx
) {
433 pragma(inline
, true);
434 static if (dims
== 2) { if (idx
== 0) x
= v
; else if (idx
== 1) y
= v
; }
435 else static if (dims
== 3) { if (idx
== 0) x
= v
; else if (idx
== 1) y
= v
; else if (idx
== 2) z
= v
; }
436 else static assert(0, "invalid dimension count for vector");
439 ref auto normalize () {
440 //pragma(inline, true);
441 mixin(ImportCoreMath
!(double, "sqrt"));
442 version(vmath_slow_normalize
) {
443 static if (dims
== 2) immutable double len
= sqrt(x
*x
+y
*y
);
444 else static if (dims
== 3) immutable double len
= sqrt(x
*x
+y
*y
+z
*z
);
445 else static assert(0, "invalid dimension count for vector");
448 static if (dims
== 3) z
/= len
;
450 static if (dims
== 2) immutable double invlen
= 1.0/sqrt(x
*x
+y
*y
);
451 else static if (dims
== 3) immutable double invlen
= 1.0/sqrt(x
*x
+y
*y
+z
*z
);
452 else static assert(0, "invalid dimension count for vector");
455 static if (dims
== 3) z
*= invlen
;
460 Float
normalizeRetLength () {
461 //pragma(inline, true);
462 mixin(ImportCoreMath
!(double, "sqrt"));
463 static if (dims
== 2) immutable double len
= sqrt(x
*x
+y
*y
);
464 else static if (dims
== 3) immutable double len
= sqrt(x
*x
+y
*y
+z
*z
);
465 else static assert(0, "invalid dimension count for vector");
466 version(vmath_slow_normalize
) {
469 static if (dims
== 3) z
/= len
;
471 immutable double invlen
= 1.0/len
;
474 static if (dims
== 3) z
*= invlen
;
476 return cast(Float
)len
;
480 ref auto safeNormalize () pure {
481 //pragma(inline, true);
482 import std.math : sqrt;
483 static if (dims == 2) Float invlength = sqrt(x*x+y*y);
484 else static if (dims == 3) Float invlength = sqrt(x*x+y*y+z*z);
485 else static assert(0, "invalid dimension count for vector");
486 if (invlength >= EPSILON!Float) {
487 invlength = cast(Float)1/invlength;
490 static if (dims == 3) z *= invlength;
494 static if (dims == 3) z = 0;
500 ref auto opOpAssign(string op
, VT
) (in auto ref VT a
) if (isVector
!VT
&& (op
== "+" || op
== "-" || op
== "*")) {
501 //pragma(inline, true);
502 mixin("x "~op
~"= a.x;");
503 mixin("y "~op
~"= a.y;");
504 static if (dims
== 3 && isVector3
!VT
) mixin("z "~op
~"= a.z;");
508 ref auto opOpAssign(string op
) (Float a
) if (op
== "+" || op
== "-" || op
== "*") {
509 //pragma(inline, true);
510 mixin("x "~op
~"= a;");
511 mixin("y "~op
~"= a;");
512 static if (dims
== 3) mixin("z "~op
~"= a;");
516 ref auto opOpAssign(string op
:"/") (Float a
) {
517 //import std.math : abs;
518 //pragma(inline, true);
519 //a = (abs(a) >= EPSILON!Float ? 1.0/a : Float.nan);
520 version(all
/*vmath_slow_normalize*/) {
523 static if (dims
== 3) z
/= a
;
525 immutable double aa
= 1.0/a
;
528 static if (dims
== 3) z
*= aa
;
533 static if (dims
== 2) {
535 ref auto rotate (Float angle
) {
536 pragma(inline
, true);
537 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
538 immutable Float c
= cos(angle
);
539 immutable Float s
= sin(angle
);
540 immutable Float nx
= x
*c
-y
*s
;
541 immutable Float ny
= x
*s
+y
*c
;
547 auto rotated (Float angle
) const {
548 pragma(inline
, true);
549 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
550 immutable Float c
= cos(angle
);
551 immutable Float s
= sin(angle
);
552 return v2(x
*c
-y
*s
, x
*s
+y
*c
);
557 // from `this` to `a`
558 auto lerp(VT
) (in auto ref VT a
, in Float t
) if (isVector
!VT
) {
559 pragma(inline
, true);
560 return this+(a
-this)*t
;
564 pragma(inline
, true);
565 static if (dims
== 2) return v2(x
, y
).normalize
; else return v3(x
, y
, z
).normalize
;
569 auto safeNormalized () {
570 pragma(inline, true);
571 static if (dims == 2) return v2(x, y).safeNormalize; else return v3(x, y, z).safeNormalize;
575 @property Float
length () {
576 pragma(inline
, true);
577 mixin(ImportCoreMath
!(Float
, "sqrt"));
578 static if (dims
== 2) return sqrt(x
*x
+y
*y
);
579 else static if (dims
== 3) return sqrt(x
*x
+y
*y
+z
*z
);
580 else static assert(0, "invalid dimension count for vector");
583 @property double dbllength () {
584 pragma(inline
, true);
585 mixin(ImportCoreMath
!(double, "sqrt"));
586 static if (dims
== 2) return sqrt(cast(double)x
*cast(double)x
+cast(double)y
*cast(double)y
);
587 else static if (dims
== 3) return sqrt(cast(double)x
*cast(double)x
+cast(double)y
*cast(double)y
+cast(double)z
*cast(double)z
);
588 else static assert(0, "invalid dimension count for vector");
591 @property Float
lengthSquared () {
592 pragma(inline
, true);
593 static if (dims
== 2) return x
*x
+y
*y
;
594 else static if (dims
== 3) return x
*x
+y
*y
+z
*z
;
595 else static assert(0, "invalid dimension count for vector");
598 @property double dbllengthSquared () {
599 pragma(inline
, true);
600 static if (dims
== 2) return cast(double)x
*cast(double)x
+cast(double)y
*cast(double)y
;
601 else static if (dims
== 3) return cast(double)x
*cast(double)x
+cast(double)y
*cast(double)y
+cast(double)z
*cast(double)z
;
602 else static assert(0, "invalid dimension count for vector");
606 Float
distance(VT
) (in auto ref VT a
) if (isVector
!VT
) {
607 pragma(inline
, true);
608 mixin(ImportCoreMath
!(Float
, "sqrt"));
609 static if (dims
== 2) {
610 static if (isVector2
!VT
) return sqrt((x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
));
611 else static if (isVector3
!VT
) return sqrt((x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+a
.z
*a
.z
);
612 else static assert(0, "invalid dimension count for vector");
613 } else static if (dims
== 3) {
614 static if (isVector2
!VT
) return sqrt((x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+z
*z
);
615 else static if (isVector3
!VT
) return sqrt((x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+(z
-a
.z
)*(z
-a
.z
));
616 else static assert(0, "invalid dimension count for vector");
618 static assert(0, "invalid dimension count for vector");
623 Float
distanceSquared(VT
) (in auto ref VT a
) if (isVector
!VT
) {
624 pragma(inline
, true);
625 static if (dims
== 2) {
626 static if (isVector2
!VT
) return (x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
);
627 else static if (isVector3
!VT
) return (x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+a
.z
*a
.z
;
628 else static assert(0, "invalid dimension count for vector");
629 } else static if (dims
== 3) {
630 static if (isVector2
!VT
) return (x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+z
*z
;
631 else static if (isVector3
!VT
) return (x
-a
.x
)*(x
-a
.x
)+(y
-a
.y
)*(y
-a
.y
)+(z
-a
.z
)*(z
-a
.z
);
632 else static assert(0, "invalid dimension count for vector");
634 static assert(0, "invalid dimension count for vector");
639 double dbldistance(VT
) (in auto ref VT a
) if (isVector
!VT
) {
640 pragma(inline
, true);
641 mixin(ImportCoreMath
!(double, "sqrt"));
642 return sqrt(dbldistanceSquared(a
));
646 double dbldistanceSquared(VT
) (in auto ref VT a
) if (isVector
!VT
) {
647 pragma(inline
, true);
648 static if (dims
== 2) {
649 static if (isVector2
!VT
) return cast(double)(x
-a
.x
)*cast(double)(x
-a
.x
)+cast(double)(y
-a
.y
)*cast(double)(y
-a
.y
);
650 else static if (isVector3
!VT
) return cast(double)(x
-a
.x
)*cast(double)(x
-a
.x
)+cast(double)(y
-a
.y
)*cast(double)(y
-a
.y
)+cast(double)a
.z
*cast(double)a
.z
;
651 else static assert(0, "invalid dimension count for vector");
652 } else static if (dims
== 3) {
653 static if (isVector2
!VT
) return cast(double)(x
-a
.x
)*cast(double)(x
-a
.x
)+cast(double)(y
-a
.y
)*cast(double)(y
-a
.y
)+cast(double)z
*cast(double)z
;
654 else static if (isVector3
!VT
) return cast(double)(x
-a
.x
)*cast(double)(x
-a
.x
)+cast(double)(y
-a
.y
)*cast(double)(y
-a
.y
)+cast(double)(z
-a
.z
)*cast(double)(z
-a
.z
);
655 else static assert(0, "invalid dimension count for vector");
657 static assert(0, "invalid dimension count for vector");
661 auto opBinary(string op
, VT
) (in auto ref VT a
) if (isVector
!VT
&& (op
== "+" || op
== "-")) {
662 pragma(inline
, true);
663 static if (dims
== 2 && isVector2
!VT
) mixin("return v2(x"~op
~"a.x, y"~op
~"a.y);");
664 else static if (dims
== 2 && isVector3
!VT
) mixin("return v3(x"~op
~"a.x, y"~op
~"a.y, 0);");
665 else static if (dims
== 3 && isVector2
!VT
) mixin("return v3(x"~op
~"a.x, y"~op
~"a.y, 0);");
666 else static if (dims
== 3 && isVector3
!VT
) mixin("return v3(x"~op
~"a.x, y"~op
~"a.y, z"~op
~"a.z);");
667 else static assert(0, "invalid dimension count for vector");
670 // vector elements operation
671 auto op(string opr
, VT
) (in auto ref VT a
) if (isVector
!VT
&& (opr
== "+" || opr
== "-" || opr
== "*" || opr
== "/")) {
672 pragma(inline
, true);
673 static if (dims
== 2) {
674 static if (isVector2
!VT
) mixin("return v2(x"~opr
~"a.x, y"~opr
~"a.y);");
675 else static if (isVector3
!VT
) mixin("return v2(x"~opr
~"a.x, y"~opr
~"a.y);");
676 else static assert(0, "invalid dimension count for vector");
677 } else static if (dims
== 3) {
678 static if (isVector2
!VT
) mixin("return v3(x"~opr
~"a.x, y"~opr
~"a.y, 0);");
679 else static if (isVector3
!VT
) mixin("return v3(x"~opr
~"a.x, y"~opr
~"a.y, z"~opr
~"a.z);");
680 else static assert(0, "invalid dimension count for vector");
682 static assert(0, "invalid dimension count for vector");
687 Float
opBinary(string op
:"*", VT
) (in auto ref VT a
) if (isVector
!VT
) { pragma(inline
, true); return dot(a
); }
690 auto opBinary(string op
:"%", VT
) (in auto ref VT a
) if (isVector
!VT
) { pragma(inline
, true); return cross(a
); }
691 auto opBinary(string op
:"^", VT
) (in auto ref VT a
) if (isVector
!VT
) { pragma(inline
, true); return cross(a
); }
693 static if (dims
== 2) {
694 auto opBinary(string op
:"%", VT
) (VT
.Float a
) if (isVector
!VT
) { pragma(inline
, true); return cross(a
); }
695 auto opBinary(string op
:"^", VT
) (VT
.Float a
) if (isVector
!VT
) { pragma(inline
, true); return cross(a
); }
698 auto opBinary(string op
) (Float a
) if (op
== "+" || op
== "-" || op
== "*") {
699 pragma(inline
, true);
700 static if (dims
== 2) mixin("return v2(x"~op
~"a, y"~op
~"a);");
701 else static if (dims
== 3) mixin("return v3(x"~op
~"a, y"~op
~"a, z"~op
~"a);");
702 else static assert(0, "invalid dimension count for vector");
705 auto opBinaryRight(string op
:"*") (Float a
) {
706 pragma(inline
, true);
707 static if (dims
== 2) mixin("return v2(x"~op
~"a, y"~op
~"a);");
708 else static if (dims
== 3) mixin("return v3(x"~op
~"a, y"~op
~"a, z"~op
~"a);");
709 else static assert(0, "invalid dimension count for vector");
712 auto opBinary(string op
:"/") (Float a
) {
713 pragma(inline
, true);
714 //import std.math : abs;
715 //immutable Float a = (abs(aa) >= EPSILON!Float ? 1.0/aa : Float.nan);
716 //immutable Float a = cast(Float)1/aa; // 1/0 == inf
717 version(all
/*vmath_slow_normalize*/) {
718 static if (dims
== 2) return v2(x
/a
, y
/a
);
719 else static if (dims
== 3) return v3(x
/a
, y
/a
, z
/a
);
720 else static assert(0, "invalid dimension count for vector");
722 a
= cast(Float
)1/a
; // 1/0 == inf
723 static if (dims
== 2) return v2(x
*a
, y
*a
);
724 else static if (dims
== 3) return v3(x
*a
, y
*a
, z
*a
);
725 else static assert(0, "invalid dimension count for vector");
729 auto opBinaryRight(string op
:"/") (Float aa
) {
730 pragma(inline
, true);
732 import std.math : abs;
733 static if (dims == 2) return v2((abs(x) >= EPSILON!Float ? aa/x : 0), (abs(y) >= EPSILON!Float ? aa/y : 0));
734 else static if (dims == 3) return v3((abs(x) >= EPSILON!Float ? aa/x : 0), (abs(y) >= EPSILON!Float ? aa/y : 0), (abs(z) >= EPSILON!Float ? aa/z : 0));
735 else static assert(0, "invalid dimension count for vector");
737 static if (dims
== 2) return v2(aa
/x
, aa
/y
);
738 else static if (dims
== 3) return v3(aa
/x
, aa
/y
, aa
/z
);
739 else static assert(0, "invalid dimension count for vector");
742 auto opUnary(string op
:"+") () { pragma(inline
, true); return this; }
744 auto opUnary(string op
:"-") () {
745 pragma(inline
, true);
746 static if (dims
== 2) return v2(-x
, -y
);
747 else static if (dims
== 3) return v3(-x
, -y
, -z
);
748 else static assert(0, "invalid dimension count for vector");
751 // this method performs the following triple product: (this x b) x c
752 Me
tripleProduct() (in auto ref Me b
, in auto ref Me c
) {
754 static if (dims
== 2) {
756 immutable Float ac
= a
.x
*c
.x
+a
.y
*c
.y
;
758 immutable Float bc
= b
.x
*c
.x
+b
.y
*c
.y
;
759 // perform b * a.dot(c) - a * b.dot(c)
766 immutable Float ac
= a
.x
*c
.x
+a
.y
*c
.y
+a
.z
*c
.z
;
768 immutable Float bc
= b
.x
*c
.x
+b
.y
*c
.y
+b
.z
*c
.z
;
769 // perform b * a.dot(c) - a * b.dot(c)
779 pragma(inline
, true);
780 mixin(ImportCoreMath
!(Float
, "fabs"));
781 static if (dims
== 2) return v2(fabs(x
), fabs(y
));
782 else static if (dims
== 3) return v3(fabs(x
), fabs(y
), fabs(z
));
783 else static assert(0, "invalid dimension count for vector");
787 pragma(inline
, true);
788 static if (dims
== 2) return v2((x
< 0 ?
-1 : x
> 0 ?
1 : 0), (y
< 0 ?
-1 : y
> 0 ?
1 : 0));
789 else static if (dims
== 3) return v3((x
< 0 ?
-1 : x
> 0 ?
1 : 0), (y
< 0 ?
-1 : y
> 0 ?
1 : 0), (z
< 0 ?
-1 : z
> 0 ?
1 : 0));
790 else static assert(0, "invalid dimension count for vector");
793 // `this` is edge; see glsl reference
794 auto step(VT
) (in auto ref VT val
) if (IsVector
!VT
) {
795 pragma(inline
, true);
796 static if (dims
== 2) return v2((val
.x
< this.x ?
0f : 1f), (val
.y
< this.y ?
0f : 1f));
797 else static if (dims
== 3) {
798 static if (VT
.Dims
== 3) {
799 return v3((val
.x
< this.x ?
0f : 1f), (val
.y
< this.y ?
0f : 1f), (val
.z
< this.z ?
0f : 1f));
801 return v3((val
.x
< this.x ?
0f : 1f), (val
.y
< this.y ?
0f : 1f), (0 < this.z ?
0f : 1f));
804 else static assert(0, "invalid dimension count for vector");
807 bool opEquals(VT
) (in auto ref VT a
) if (isVector
!VT
) {
808 pragma(inline
, true);
809 static if (dims
== 2 && isVector2
!VT
) return (x
== a
.x
&& y
== a
.y
);
810 else static if (dims
== 2 && isVector3
!VT
) return (x
== a
.x
&& y
== a
.y
&& a
.z
== 0);
811 else static if (dims
== 3 && isVector2
!VT
) return (x
== a
.x
&& y
== a
.y
&& z
== 0);
812 else static if (dims
== 3 && isVector3
!VT
) return (x
== a
.x
&& y
== a
.y
&& z
== a
.z
);
813 else static assert(0, "invalid dimension count for vector");
817 @property Float
dot(VT
) (in auto ref VT a
) if (isVector
!VT
) {
818 pragma(inline
, true);
819 static if (dims
== 2) {
821 } else static if (dims
== 3) {
822 static if (isVector2
!VT
) return x
*a
.x
+y
*a
.y
;
823 else static if (isVector3
!VT
) return x
*a
.x
+y
*a
.y
+z
*a
.z
;
824 else static assert(0, "invalid dimension count for vector");
826 static assert(0, "invalid dimension count for vector");
831 auto cross(VT
) (in auto ref VT a
) if (isVector
!VT
) {
832 pragma(inline
, true);
833 static if (dims
== 2 && isVector2
!VT
) return /*v3(0, 0, x*a.y-y*a.x)*/x
*a
.y
-y
*a
.x
;
834 else static if (dims
== 2 && isVector3
!VT
) return v3(y
*a
.z
, -x
*a
.z
, x
*a
.y
-y
*a
.x
);
835 else static if (dims
== 3 && isVector2
!VT
) return v3(-z
*a
.y
, z
*a
.x
, x
*a
.y
-y
*a
.x
);
836 else static if (dims
== 3 && isVector3
!VT
) return v3(y
*a
.z
-z
*a
.y
, z
*a
.x
-x
*a
.z
, x
*a
.y
-y
*a
.x
);
837 else static assert(0, "invalid dimension count for vector");
840 // this*s; if you want s*this, do cross(-s)
841 static if (dims
== 2) auto cross (Float s
) {
842 pragma(inline
, true);
843 return Me(s
*y
, -s
*x
);
846 // compute Euler angles from direction vector (this) (with zero roll)
848 auto tmp
= this.normalized
;
849 /*hpr.x = -atan2(tmp.x, tmp.y);
850 hpr.y = -atan2(tmp.z, sqrt(tmp.x*tmp.x+tmp.y*tmp.y));*/
851 static if (dims
== 2) {
852 mixin(ImportCoreMath
!(double, "atan2"));
854 cast(Float
)(atan2(cast(double)tmp
.x
, cast(Float
)0)),
855 cast(Float
)(-atan2(cast(double)tmp
.y
, cast(double)tmp
.x
)),
858 mixin(ImportCoreMath
!(double, "atan2", "sqrt"));
860 cast(Float
)(atan2(cast(double)tmp
.x
, cast(double)tmp
.z
)),
861 cast(Float
)(-atan2(cast(double)tmp
.y
, cast(double)sqrt(tmp
.x
*tmp
.x
+tmp
.z
*tmp
.z
))),
867 // some more supplementary functions to support various things
868 Float
vcos(VT
) (in auto ref VT v
) if (isVector
!VT
) {
869 immutable double len
= length
*v
.length
;
870 return cast(Float
)(len
> EPSILON
!Float ?
cast(Float
)(dot(v
)/len
) : cast(Float
)0);
873 static if (dims
== 2) Float
vsin(VT
) (in auto ref VT v
) if (isVector
!VT
) {
874 immutable double len
= length
*v
.length
;
875 return cast(Float
)(len
> EPSILON
!Float ?
cast(Float
)(cross(v
)/len
) : cast(Float
)0);
878 static if (dims
== 2) Float
angle180(VT
) (in auto ref VT v
) if (isVector
!VT
) {
879 import std
.math
: PI
;
880 mixin(ImportCoreMath
!(Float
, "atan"));
881 immutable Float cosv
= vcos(v
);
882 immutable Float sinv
= vsin(v
);
883 if (cosv
== 0) return (sinv
<= 0 ?
-90 : 90);
884 if (sinv
== 0) return (cosv
<= 0 ?
180 : 0);
885 Float angle
= (cast(Float
)180*atan(sinv
/cosv
))/cast(Float
)PI
;
886 if (cosv
< 0) { if (angle
> 0) angle
-= 180; else angle
+= 180; }
890 static if (dims
== 2) Float
angle360(VT
) (in auto ref VT v
) if (isVector
!VT
) {
891 import std
.math
: PI
;
892 mixin(ImportCoreMath
!(Float
, "atan"));
893 immutable Float cosv
= vcos(v
);
894 immutable Float sinv
= vsin(v
);
895 if (cosv
== 0) return (sinv
<= 0 ?
270 : 90);
896 if (sinv
== 0) return (cosv
<= 0 ?
180 : 0);
897 Float angle
= (cast(Float
)180*atan(sinv
/cosv
))/cast(Float
)PI
;
898 if (cosv
< 0) angle
+= 180;
899 if (angle
< 0) angle
+= 360;
903 Float
relativeAngle(VT
) (in auto ref VT v
) if (isVector
!VT
) {
904 import std
.math
: PI
;
905 mixin(ImportCoreMath
!(Float
, "acos"));
906 immutable Float cosv
= vcos(v
);
907 if (cosv
<= -1) return PI
;
908 if (cosv
>= 1) return 0;
912 bool touch(VT
) (in auto ref VT v
) if (isVector
!VT
) { pragma(inline
, true); return (distance(v
) < /*SMALL*/EPSILON
!Float
); }
913 bool touch(VT
) (in auto ref VT v
, in Float epsilon
) if (isVector
!VT
) { pragma(inline
, true); return (distance(v
) < epsilon
); }
915 // is `this` on left? (or on line)
916 bool onLeft(VT
) (in auto ref VT v0
, in auto ref VT v1
) if (isVector
!VT
) {
917 pragma(inline
, true);
918 return ((v1
-v0
).cross(this-v0
) <= 0);
921 static if (dims
== 2) {
923 // test if a point (`this`) is left/on/right of an infinite 2D line
928 Float
side(VT
) (in auto ref VT v0
, in auto ref VT v1
) const if (isVector2
!VT
) {
929 pragma(inline
, true);
930 return ((v1
.x
-v0
.x
)*(this.y
-v0
.y
)-(this.x
-v0
.x
)*(v1
.y
-v0
.y
));
935 bool inside(VT
) (in auto ref VT v0
, in auto ref VT v1
, in auto ref VT v2
) if (isVector
!VT
) {
936 if ((v1
-v0
).cross(this-v0
) <= 0) return false;
937 if ((v2
-v1
).cross(this-v1
) <= 0) return false;
938 return ((v0
-v2
).cross(this-v2
) > 0);
941 // box2dlite port support
942 static if (dims
== 2) {
943 // returns a perpendicular vector (90 degree rotation)
944 auto perp() () { pragma(inline
, true); return v2(-y
, x
); }
946 // returns a perpendicular vector (-90 degree rotation)
947 auto rperp() () { pragma(inline
, true); return v2(y
, -x
); }
949 // returns the vector projection of this onto v
950 auto projectTo() (in auto ref v2 v
) { pragma(inline
, true); return v
*(this.dot(v
)/v
.dot(v
)); }
952 // returns the unit length vector for the given angle (in radians)
953 auto forAngle (in Float a
) { pragma(inline
, true); mixin(ImportCoreMath
!(Float
, "cos", "sin")); return v2(cos(a
), sin(a
)); }
955 // returns the angular direction v is pointing in (in radians)
956 Float
toAngle() () { pragma(inline
, true); mixin(ImportCoreMath
!(Float
, "atan2")); return atan2(y
, x
); }
958 auto scross() (Float s
) { pragma(inline
, true); return v2(-s
*y
, s
*x
); }
960 // returns the closest point to `this` on the line segment from `a` to `b`, or invalid vector
961 // if `asseg` is false, "segment" is actually a line (infinite in both directions)
962 Me
projectToSeg(bool asseg
=true) (in auto ref Me a
, in auto ref Me b
) {
963 mixin(ImportCoreMath
!(Float
, "fabs"));
965 immutable ab
= b
-a
; // vector from a to b
966 // squared distance from a to b
967 immutable absq
= ab
.dot(ab
);
968 if (fabs(absq
) < Epsilon
) return a
; // a and b are the same point (roughly)
969 immutable ap
= p
-a
; // vector from a to p
970 immutable t
= ap
.dot(ab
)/absq
;
972 if (t
< 0) return a
; // "before" a on the line
973 if (t
> 1) return b
; // "after" b on the line
975 // projection lies "inbetween" a and b on the line
979 // returns the closest point to `this` on the line segment from `a` to `b`, or invalid vector
980 // if `asseg` is false, "segment" is actually a line (infinite in both directions)
981 Me
projectToSegT(bool asseg
=true) (in auto ref Me a
, in auto ref Me b
, ref Float rest
) {
982 mixin(ImportCoreMath
!(Float
, "fabs"));
984 immutable ab
= b
-a
; // vector from a to b
985 // squared distance from a to b
986 immutable absq
= ab
.dot(ab
);
987 if (fabs(absq
) < Epsilon
) { rest
= 0; return a
; } // a and b are the same point (roughly)
988 immutable ap
= p
-a
; // vector from a to p
989 immutable t
= ap
.dot(ab
)/absq
;
992 if (t
< 0) return a
; // "before" a on the line
993 if (t
> 1) return b
; // "after" b on the line
995 // projection lies "inbetween" a and b on the line
999 // returns `t` (normalized distance of projected point from `a`)
1000 Float
projectToLineT() (in auto ref Me a
, in auto ref Me b
) {
1001 mixin(ImportCoreMath
!(Float
, "fabs"));
1003 immutable ab
= b
-a
; // vector from a to b
1004 // squared distance from a to b
1005 immutable absq
= ab
.dot(ab
);
1006 if (fabs(absq
) < Epsilon
) return cast(Float
)0; // a and b are the same point (roughly)
1007 immutable ap
= p
-a
; // vector from a to p
1008 return cast(Float
)(ap
.dot(ab
)/absq
);
1012 bool equals() (in auto ref Me v
) {
1013 pragma(inline
, true);
1014 mixin(ImportCoreMath
!(Float
, "fabs"));
1015 static if (dims
== 2) return (fabs(x
-v
.x
) < EPSILON
!Float
&& fabs(y
-v
.y
) < EPSILON
!Float
);
1016 else static if (dims
== 3) return (fabs(x
-v
.x
) < EPSILON
!Float
&& fabs(y
-v
.y
) < EPSILON
!Float
&& fabs(z
-v
.z
) < EPSILON
!Float
);
1017 else static assert(0, "invalid dimension count for vector");
1022 auto opDispatch(string
fld) ()
1023 if ((dims
== 2 && isGoodSwizzling
!(fld, "xy", 2, 3)) ||
1024 (dims
== 3 && isGoodSwizzling
!(fld, "xyz", 2, 3)))
1026 static if (fld.length
== 2) {
1027 return mixin(SwizzleCtor
!("v2", fld));
1029 return mixin(SwizzleCtor
!("v3", fld));
1033 static if (dims
== 3) bool collinear() (in auto ref Me v1
, in auto ref Me v2
) {
1034 pragma(inline
, true);
1035 mixin(ImportCoreMath
!(Float
, "fabs"));
1037 immutable Float cx
= (v1
.y
-v0
.y
)*(v2
.z
-v0
.z
)-(v2
.y
-v0
.y
)*(v1
.z
-v0
.z
);
1038 immutable Float cy
= (v2
.x
-v0
.x
)*(v1
.z
-v0
.z
)-(v1
.x
-v0
.x
)*(v2
.z
-v0
.z
);
1039 immutable Float cz
= (v1
.x
-v0
.x
)*(v2
.y
-v0
.y
)-(v2
.x
-v0
.x
)*(v1
.y
-v0
.y
);
1040 return (fabs(cast(double)(cx
*cx
+cy
*cy
+cz
*cz
)) < EPSILON
!double);
1043 static if (dims
== 2) bool collinear() (in auto ref Me v1
, in auto ref Me v2
) {
1044 pragma(inline
, true);
1045 mixin(ImportCoreMath
!(double, "fabs"));
1047 immutable Float det
= cast(Float
)((v0
-v1
)*(v0
-v2
)-(v0
-v2
)*(v0
-v1
));
1048 return (fabs(det
) <= EPSILON
!Float
);
1051 static if (dims
== 2) {
1052 import std
.range
.primitives
: isInputRange
, ElementType
;
1054 static auto vertRange (const(Me
)[] varr
) {
1055 static struct VertRange
{
1058 nothrow @trusted @nogc:
1059 @property bool empty () const pure { pragma(inline
, true); return (pos
>= arr
.length
); }
1060 @property Me
front () const pure { pragma(inline
, true); return (pos
< arr
.length ? arr
.ptr
[pos
] : Me
.Invalid
); }
1061 void popFront () { pragma(inline
, true); if (pos
< arr
.length
) ++pos
; }
1062 auto save () const pure { pragma(inline
, true); return VertRange(arr
, pos
); }
1064 return VertRange(varr
, 0);
1067 bool insidePoly(VR
) (auto ref VR vr
) const if (isInputRange
!VR
&& is(ElementType
!VR
: const Me
)) {
1068 if (vr
.empty
) return false;
1071 if (vr
.empty
) return false;
1074 if (vr
.empty
) return false; //TODO: check if p is ON the edge?
1078 if (p
.y
> nmin(p1
.y
, p2
.y
)) {
1079 if (p
.y
<= nmax(p1
.y
, p2
.y
)) {
1080 if (p
.x
<= nmax(p1
.x
, p2
.x
)) {
1082 auto xinters
= (p
.y
-p1
.y
)*(p2
.x
-p1
.x
)/(p2
.y
-p1
.y
)+p1
.x
;
1083 if (p1
.x
== p2
.x || p
.x
<= xinters
) counter ^
= 1;
1088 if (vr
.empty
) break;
1093 return (counter
!= 0);
1096 bool insidePoly() (const(Me
)[] poly
) const { pragma(inline
, true); return insidePoly(vertRange(poly
)); }
1098 // gets the signed area
1099 // if the area is less than 0, it indicates that the polygon is clockwise winded
1100 Me
.Float
signedArea(VR
) (auto ref VR vr
) const if (isInputRange
!VR
&& is(ElementType
!VR
: const Me
)) {
1102 if (vr
.empty
) return area
;
1105 if (vr
.empty
) return area
;
1108 if (vr
.empty
) return area
;
1113 if (vr
.empty
) break;
1119 area
+= p2
.x
*pfirst
.y
;
1120 area
-= p2
.y
*pfirst
.x
;
1121 return area
/cast(VT
.Float
)2;
1124 Me
.Float
signedArea() (const(Me
)[] poly
) const { pragma(inline
, true); return signedArea(vertRange(poly
)); }
1126 // indicates if the vertices are in counter clockwise order
1127 // warning: If the area of the polygon is 0, it is unable to determine the winding
1128 bool isCCW(VR
) (auto ref VR vr
) const if (isInputRange
!VR
&& is(ElementType
!VR
: const Me
)) { pragma(inline
, true); return (signedArea(vr
) > 0); }
1129 bool isCCW() (const(Me
)[] poly
) const { pragma(inline
, true); return (signedArea(vertRange(poly
)) > 0); }
1131 // *signed* area; can be used to check on which side `b` is
1132 static auto triSignedArea() (in auto ref Me a
, in auto ref Me b
, in auto ref Me c
) {
1133 pragma(inline
, true);
1134 return (b
.x
-a
.x
)*(c
.y
-a
.y
)-(c
.x
-a
.x
)*(b
.y
-a
.y
);
1138 static if (dims
== 3) {
1139 // project this point to the given line
1140 Me
projectToLine() (in auto ref Me p0
, in auto ref Me p1
) const {
1141 immutable Me d
= p1
-p0
;
1142 immutable Me
.Float t
= d
.dot(this-p0
)/d
.dot(d
);
1143 if (tout
!is null) *tout
= t
;
1147 // return "time" of projection
1148 Me
.Float
projectToLineTime() (in auto ref Me p0
, in auto ref Me p1
) const {
1149 immutable Me d
= p1
-p0
;
1150 return d
.dot(this-p0
)/d
.dot(d
);
1153 // calculate triangle normal
1154 static Me
triangleNormal() (in auto ref Me v0
, in auto ref Me v1
, in auto ref Me v2
) {
1155 mixin(ImportCoreMath
!(Me
.Float
, "fabs"));
1156 immutable Me cp
= (v1
-v0
).cross(v2
-v1
);
1157 immutable Me
.Float m
= cp
.length
;
1158 return (fabs(m
) > Epsilon ? cp
*(cast(Me
.Float
)1/m
) : Me
.init
);
1161 // polygon must be convex, ccw, and without collinear vertices
1163 bool insideConvexPoly (const(Me
)[] ply
...) const @trusted {
1164 if (ply
.length
< 3) return false;
1165 immutable normal
= triangleNormal(ply
.ptr
[0], ply
.ptr
[1], ply
.ptr
[2]);
1166 auto pidx
= ply
.length
-1;
1167 for (typeof(pidx
) cidx
= 0; cidx
< ply
.length
; pidx
= cidx
, ++cidx
) {
1168 immutable pp1
= ply
.ptr
[pidx
];
1169 immutable pp2
= ply
.ptr
[cidx
];
1170 immutable side
= (pp2
-pp1
).cross(this-pp1
);
1171 if (normal
.dot(side
) < 0) return false;
1178 // linearly interpolate between v1 and v2
1180 VT lerp(VT) (in auto ref VT v1, in auto ref VT v2, const Float t) if (isSameVector!VT) {
1181 pragma(inline, true);
1182 return (v1*cast(Float)1.0f-t)+(v2*t);
1186 static if (dims
== 2) {
1187 Me
lineIntersect() (in auto ref Me p1
, in auto ref Me p2
, in auto ref Me q1
, in auto ref Me q2
) {
1188 pragma(inline
, true);
1189 mixin(ImportCoreMath
!(Me
.Float
, "fabs"));
1190 immutable Me
.Float a1
= p2
.y
-p1
.y
;
1191 immutable Me
.Float b1
= p1
.x
-p2
.x
;
1192 immutable Me
.Float c1
= a1
*p1
.x
+b1
*p1
.y
;
1193 immutable Me
.Float a2
= q2
.y
-q1
.y
;
1194 immutable Me
.Float b2
= q1
.x
-q2
.x
;
1195 immutable Me
.Float c2
= a2
*q1
.x
+b2
*q1
.y
;
1196 immutable Me
.Float det
= a1
*b2
-a2
*b1
;
1197 if (fabs(det
) > EPSILON
!(Me
.Float
)) {
1198 // lines are not parallel
1199 immutable Me
.Float invdet
= cast(Me
.Float
)1/det
;
1200 return Me((b2
*c1
-b1
*c2
)*invdet
, (a1
*c2
-a2
*c1
)*invdet
);
1205 Me
segIntersect(bool firstIsSeg
=true, bool secondIsSeg
=true) (in auto ref Me point0
, in auto ref Me point1
, in auto ref Me point2
, in auto ref Me point3
) {
1206 mixin(ImportCoreMath
!(Me
.Float
, "fabs"));
1207 static if (firstIsSeg
&& secondIsSeg
) {
1208 // fast aabb test for possible early exit
1209 if (nmax(point0
.x
, point1
.x
) < nmin(point2
.x
, point3
.x
) ||
nmax(point2
.x
, point3
.x
) < nmin(point0
.x
, point1
.x
)) return Me
.Invalid
;
1210 if (nmax(point0
.y
, point1
.y
) < nmin(point2
.y
, point3
.y
) ||
nmax(point2
.y
, point3
.y
) < nmin(point0
.y
, point1
.y
)) return Me
.Invalid
;
1212 immutable Me
.Float den
= ((point3
.y
-point2
.y
)*(point1
.x
-point0
.x
))-((point3
.x
-point2
.x
)*(point1
.y
-point0
.y
));
1213 if (fabs(den
) > EPSILON
!(Me
.Float
)) {
1214 immutable Me
.Float e
= point0
.y
-point2
.y
;
1215 immutable Me
.Float f
= point0
.x
-point2
.x
;
1216 immutable Me
.Float invden
= cast(Me
.Float
)1/den
;
1217 immutable Me
.Float ua
= (((point3
.x
-point2
.x
)*e
)-((point3
.y
-point2
.y
)*f
))*invden
;
1218 static if (firstIsSeg
) { if (ua
< 0 || ua
> 1) return Me
.Invalid
; }
1219 if (ua
>= 0 && ua
<= 1) {
1220 immutable Me
.Float ub
= (((point1
.x
-point0
.x
)*e
)-((point1
.y
-point0
.y
)*f
))*invden
;
1221 static if (secondIsSeg
) { if (ub
< 0 || ub
> 1) return Me
.Invalid
; }
1222 if (ua
!= 0 || ub
!= 0) return Me(point0
.x
+ua
*(point1
.x
-point0
.x
), point0
.y
+ua
*(point1
.y
-point0
.y
));
1228 // returns hittime; <0: no collision; 0: inside
1229 // WARNING! NOT REALLY TESTED, AND MAY BE INCORRECT!
1230 Me
.Float
sweepCircle() (in auto ref Me v0
, in auto ref Me v1
, in auto ref Me pos
, Me
.Float radii
, in auto ref Me vel
, ref Me hitp
) {
1231 mixin(ImportCoreMath
!(Me
.Float
, "fabs", "sqrt"));
1232 if (v0
.equals(v1
)) return (pos
.distanceSquared(v0
) <= radii
*radii ?
0 : -1); // v0 and v1 are the same point; do "point inside circle" check
1233 immutable Me normal
= (v1
-v0
).perp
.normalized
;
1234 immutable Me
.Float D
= -normal
*((v0
+v1
)/2);
1235 immutable d0
= normal
.dot(pos
)+D
;
1236 if (fabs(d0
) <= radii
) return cast(Me
.Float
)0; // inside
1238 immutable p1
= pos
+vel
;
1239 immutable Me
.Float d1
= normal
.dot(p1
)+D
;
1240 if (d0
> radii
&& d1
< radii
) {
1241 Me
.Float t
= (d0
-radii
)/(d0
-d1
); // normalized time
1243 // project hitp point to segment
1247 immutable ab
= b
-a
; // vector from a to b
1248 // squared distance from a to b
1249 immutable absq
= ab
.dot(ab
);
1250 //assert(absq != 0); // a and b are the same point
1251 if (fabs(absq
) < Me
.Epsilon
) return -1; // a and b are the same point (roughly) -- SOMETHING IS VERY WRONG
1252 // t1 is projection "time" of p; [0..1]
1253 Me
.Float t1
= (p
-a
).dot(ab
)/absq
; //a+t1*ab -- projected point
1254 // is "contact center" lies on the seg?
1255 if (t1
>= 0 && t1
<= 1) return t
; // yes: this is clear hit
1256 // because i'm teh idiot, i'll just check ray-circle intersection for edge's capsue endpoints
1257 // ('cause if we'll turn edge into the capsule (v0,v1) with radius radii, we can use raycasting)
1258 // this is not entirely valid for segments much shorter than radius, but meh
1260 immutable Me eco
= (t1
< 0 ? a
: b
);
1261 immutable Me rpj
= eco
.projectToSegT
!false(pos
, p1
, ct1
);
1262 immutable Me
.Float dsq
= eco
.distanceSquared(rpj
);
1263 if (dsq
>= radii
*radii
) return -1; // endpoint may be on sphere or out of it, this is not interesting
1264 immutable Me
.Float
dt = sqrt(radii
*radii
-dsq
)/sqrt(vel
.x
*vel
.x
+vel
.y
*vel
.y
);
1276 // ////////////////////////////////////////////////////////////////////////// //
1277 // 2x2 matrix for fast 2D rotations and such
1278 alias mat22
= Mat22
!vec2
;
1280 align(1) struct Mat22(VT
) if (IsVectorDim
!(VT
, 2)) {
1283 alias Float
= VT
.Float
;
1284 alias mat22
= typeof(this);
1285 alias vec2
= VecN
!(3, Float
);
1286 alias Me
= typeof(this);
1288 nothrow @safe @nogc:
1290 // default is "not rotated"
1291 vec2 col1
= vec2(1, 0);
1292 vec2 col2
= vec2(0, 1);
1295 this (Float angle
) { pragma(inline
, true); set(angle
); }
1297 void set (Float angle
) {
1298 pragma(inline
, true);
1299 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
1300 immutable Float c
= cos(angle
), s
= sin(angle
);
1301 col1
.x
= c
; col1
.y
= s
;
1302 col2
.x
= -s
; col2
.y
= c
;
1306 this() (in auto ref vec2 acol1
, in auto ref vec2 acol2
) { pragma(inline
, true); col1
= acol1
; col2
= acol2
; }
1308 static Me
Identity () { pragma(inline
, true); Me res
; return res
; }
1310 Me
transpose () const { pragma(inline
, true); return Me(vec2(col1
.x
, col2
.x
), vec2(col1
.y
, col2
.y
)); }
1312 Me
invert () const @trusted {
1313 pragma(inline
, true);
1314 immutable Float a
= col1
.x
, b
= col2
.x
, c
= col1
.y
, d
= col2
.y
;
1316 Float det
= a
*d
-b
*c
;
1317 assert(det
!= cast(Float
)0);
1318 det
= cast(Float
)1/det
;
1326 Me
opUnary(string op
:"+") () const { pragma(inline
, true); return this; }
1327 Me
opUnary(string op
:"-") () const { pragma(inline
, true); return Me(-col1
, -col2
); }
1329 vec2
opBinary(string op
:"*") (in auto ref vec2 v
) const { pragma(inline
, true); return vec2(col1
.x
*v
.x
+col2
.x
*v
.y
, col1
.y
*v
.x
+col2
.y
*v
.y
); }
1330 vec2
opBinaryRight(string op
:"*") (in auto ref vec2 v
) const { pragma(inline
, true); return vec2(col1
.x
*v
.x
+col2
.x
*v
.y
, col1
.y
*v
.x
+col2
.y
*v
.y
); }
1332 Me
opBinary(string op
:"*") (Float s
) const { pragma(inline
, true); return Me(vec2(col1
*s
, col2
*s
)); }
1333 Me
opBinaryRight(string op
:"*") (Float s
) const { pragma(inline
, true); return Me(vec2(col1
*s
, col2
*s
)); }
1335 Me
opBinary(string op
) (in auto ref Me bm
) const if (op
== "+" || op
== "-") { pragma(inline
, true); mixin("return Me(col1"~op
~"bm.col1, col2"~op
~"bm.col2);"); }
1336 Me
opBinary(string op
:"*") (in auto ref Me bm
) const { pragma(inline
, true); return Me(this*bm
.col1
, this*bm
.col2
); }
1338 ref Me
opOpAssign(string op
) (in auto ref Me bm
) if (op
== "+" || op
== "-") { pragma(inline
, true); mixin("col1 "~op
~"= bm.col1;"); mixin("col2 "~op
~"= bm.col2;"); return this; }
1340 ref Me
opOpAssign(string op
:"*") (Float s
) const { pragma(inline
, true); col1
*= s
; col2
*= s
; }
1342 Me
abs() () { pragma(inline
, true); return Me(col1
.abs
, col2
.abs
); }
1344 // solves the system of linear equations: Ax = b
1345 vec2
solve() (in auto ref vec2 v
) const {
1346 immutable Float a
= col1
.x
, b
= col2
.x
, c
= col1
.y
, d
= col2
.y
;
1347 Float det
= a
*d
-b
*c
;
1348 assert(det
!= cast(Float
)0);
1349 det
= cast(Float
)1/det
;
1350 return vec2((d
*v
.x
-b
*v
.y
)*det
, (a
*v
.y
-c
*v
.x
)*det
);
1355 // ////////////////////////////////////////////////////////////////////////// //
1356 alias mat3
= Mat3
!vec2
; /// for 2D
1357 alias mat33
= Mat3
!vec3
; /// for 3D
1359 /// very simple (and mostly not tested) 3x3 matrix, for 2D/3D (depenting of parameterizing vector type)
1360 /// for 3D, 3x3 matrix cannot do translation
1361 align(1) struct Mat3(VT
) if (IsVectorDim
!(VT
, 2) || IsVectorDim
!(VT
, 3)) {
1364 alias Float
= VT
.Float
;
1365 alias m3
= typeof(this);
1366 static if (VT
.Dims
== 2) {
1369 enum ThreeD
= false;
1370 enum isVector2(VT
) = is(VT
== VecN
!(2, Float
));
1375 enum isVector3(VT
) = is(VT
== VecN
!(3, Float
));
1379 // 3x3 matrix components
1387 string
toString () const nothrow @trusted {
1388 import std
.string
: format
;
1390 return "0:[%g,%g,%g]\n3:[%g,%g,%g]\n6:[%g,%g,%g]".format(
1391 m
.ptr
[0], m
.ptr
[1], m
.ptr
[2],
1392 m
.ptr
[3], m
.ptr
[4], m
.ptr
[5],
1393 m
.ptr
[6], m
.ptr
[7], m
.ptr
[8],
1395 } catch (Exception
) {
1401 nothrow @trusted @nogc:
1402 this (const(Float
)[] vals
...) {
1403 pragma(inline
, true);
1404 if (vals
.length
>= 3*3) {
1405 m
.ptr
[0..9] = vals
.ptr
[0..9];
1407 // so `m3(1)`, for example, will create matrix filled with `1`
1408 if (vals
.length
== 1) {
1409 m
.ptr
[0..9] = vals
.ptr
[0];
1411 // still clear the matrix
1413 m
.ptr
[0..vals
.length
] = vals
[];
1418 this() (in auto ref m3 mt
) {
1419 pragma(inline
, true);
1423 Float
opIndex (usize x
, usize y
) const {
1424 pragma(inline
, true);
1425 return (x
< 3 && y
< 3 ? m
.ptr
[y
*3+x
] : Float
.nan
);
1428 void opIndexAssign (Float v
, usize x
, usize y
) {
1429 pragma(inline
, true);
1430 if (x
< 3 && y
< 3) m
.ptr
[y
*3+x
] = v
;
1433 @property bool isIdentity () const {
1434 pragma(inline
, true); // can we?
1436 m
.ptr
[0] == 1 && m
.ptr
[1] == 0 && m
.ptr
[2] == 0 &&
1437 m
.ptr
[3] == 0 && m
.ptr
[4] == 1 && m
.ptr
[5] == 0 &&
1438 m
.ptr
[6] == 0 && m
.ptr
[7] == 0 && m
.ptr
[8] == 1;
1441 auto opUnary(string op
:"+") () const { pragma(inline
, true); return this; }
1443 auto opUnary(string op
:"-") () const {
1444 pragma(inline
, true);
1446 -m
.ptr
[0], -m
.ptr
[1], -m
.ptr
[2],
1447 -m
.ptr
[3], -m
.ptr
[4], -m
.ptr
[5],
1448 -m
.ptr
[6], -m
.ptr
[7], -m
.ptr
[8],
1452 auto opBinary(string op
) (in auto ref m3 b
) const if (op
== "+" || op
== "-") {
1453 pragma(inline
, true);
1455 mixin("res.m.ptr[0] = m.ptr[0]"~op
~"b.m.ptr[0];");
1456 mixin("res.m.ptr[1] = m.ptr[1]"~op
~"b.m.ptr[1];");
1457 mixin("res.m.ptr[2] = m.ptr[2]"~op
~"b.m.ptr[2];");
1458 mixin("res.m.ptr[3] = m.ptr[3]"~op
~"b.m.ptr[3];");
1459 mixin("res.m.ptr[4] = m.ptr[4]"~op
~"b.m.ptr[4];");
1460 mixin("res.m.ptr[5] = m.ptr[5]"~op
~"b.m.ptr[5];");
1461 mixin("res.m.ptr[6] = m.ptr[6]"~op
~"b.m.ptr[6];");
1462 mixin("res.m.ptr[7] = m.ptr[7]"~op
~"b.m.ptr[7];");
1463 mixin("res.m.ptr[8] = m.ptr[8]"~op
~"b.m.ptr[8];");
1467 ref auto opOpAssign(string op
) (in auto ref m3 b
) if (op
== "+" || op
== "-") {
1468 pragma(inline
, true);
1469 mixin("m.ptr[0]"~op
~"=b.m.ptr[0]; m.ptr[1]"~op
~"=b.m.ptr[1]; m.ptr[2]"~op
~"=b.m.ptr[2];");
1470 mixin("m.ptr[3]"~op
~"=b.m.ptr[3]; m.ptr[4]"~op
~"=b.m.ptr[4]; m.ptr[5]"~op
~"=b.m.ptr[5];");
1471 mixin("m.ptr[6]"~op
~"=b.m.ptr[6]; m.ptr[7]"~op
~"=b.m.ptr[7]; m.ptr[8]"~op
~"=b.m.ptr[8];");
1475 auto opBinary(string op
) (in Float b
) const if (op
== "*" || op
== "/") {
1476 pragma(inline
, true);
1478 mixin("res.m.ptr[0] = m.ptr[0]"~op
~"b;");
1479 mixin("res.m.ptr[1] = m.ptr[1]"~op
~"b;");
1480 mixin("res.m.ptr[2] = m.ptr[2]"~op
~"b;");
1481 mixin("res.m.ptr[3] = m.ptr[3]"~op
~"b;");
1482 mixin("res.m.ptr[4] = m.ptr[4]"~op
~"b;");
1483 mixin("res.m.ptr[5] = m.ptr[5]"~op
~"b;");
1484 mixin("res.m.ptr[6] = m.ptr[6]"~op
~"b;");
1485 mixin("res.m.ptr[7] = m.ptr[7]"~op
~"b;");
1486 mixin("res.m.ptr[8] = m.ptr[8]"~op
~"b;");
1490 auto opBinaryRight(string op
) (in Float b
) const if (op
== "*" || op
== "/") {
1491 pragma(inline
, true);
1493 mixin("res.m.ptr[0] = m.ptr[0]"~op
~"b;");
1494 mixin("res.m.ptr[1] = m.ptr[1]"~op
~"b;");
1495 mixin("res.m.ptr[2] = m.ptr[2]"~op
~"b;");
1496 mixin("res.m.ptr[3] = m.ptr[3]"~op
~"b;");
1497 mixin("res.m.ptr[4] = m.ptr[4]"~op
~"b;");
1498 mixin("res.m.ptr[5] = m.ptr[5]"~op
~"b;");
1499 mixin("res.m.ptr[6] = m.ptr[6]"~op
~"b;");
1500 mixin("res.m.ptr[7] = m.ptr[7]"~op
~"b;");
1501 mixin("res.m.ptr[8] = m.ptr[8]"~op
~"b;");
1505 ref auto opOpAssign(string op
) (in Float b
) if (op
== "*" || op
== "/") {
1506 pragma(inline
, true);
1507 mixin("m.ptr[0]"~op
~"=b; m.ptr[1]"~op
~"=b; m.ptr[2]"~op
~"=b;");
1508 mixin("m.ptr[3]"~op
~"=b; m.ptr[4]"~op
~"=b; m.ptr[5]"~op
~"=b;");
1509 mixin("m.ptr[6]"~op
~"=b; m.ptr[7]"~op
~"=b; m.ptr[8]"~op
~"=b;");
1513 static if (TwoD
) auto opBinary(string op
:"*") (in auto ref v2 v
) const {
1514 pragma(inline
, true);
1516 v
.x
*m
.ptr
[3*0+0]+v
.y
*m
.ptr
[3*1+0]+m
.ptr
[3*2+0],
1517 v
.x
*m
.ptr
[3*0+1]+v
.y
*m
.ptr
[3*1+1]+m
.ptr
[3*2+1],
1521 static if (ThreeD
) auto opBinary(string op
:"*") (in auto ref v3 v
) const {
1522 pragma(inline
, true);
1524 v
.x
*m
.ptr
[3*0+0]+v
.y
*m
.ptr
[3*1+0]+v
.z
*m
.ptr
[3*2+0],
1525 v
.x
*m
.ptr
[3*0+1]+v
.y
*m
.ptr
[3*1+1]+v
.z
*m
.ptr
[3*2+1],
1526 v
.x
*m
.ptr
[3*0+2]+v
.y
*m
.ptr
[3*1+2]+v
.z
*m
.ptr
[3*2+2],
1530 static if (TwoD
) auto opBinaryRight(string op
:"*") (in auto ref v2 v
) const { pragma(inline
, true); return this*v
; }
1531 static if (ThreeD
) auto opBinaryRight(string op
:"*") (in auto ref v3 v
) const { pragma(inline
, true); return this*v
; }
1533 auto opBinary(string op
:"*") (in auto ref m3 b
) const {
1534 //pragma(inline, true);
1536 res
.m
.ptr
[3*0+0] = m
.ptr
[3*0+0]*b
.m
.ptr
[3*0+0]+m
.ptr
[3*0+1]*b
.m
.ptr
[3*1+0]+m
.ptr
[3*0+2]*b
.m
.ptr
[3*2+0];
1537 res
.m
.ptr
[3*0+1] = m
.ptr
[3*0+0]*b
.m
.ptr
[3*0+1]+m
.ptr
[3*0+1]*b
.m
.ptr
[3*1+1]+m
.ptr
[3*0+2]*b
.m
.ptr
[3*2+1];
1538 res
.m
.ptr
[3*0+2] = m
.ptr
[3*0+0]*b
.m
.ptr
[3*0+2]+m
.ptr
[3*0+1]*b
.m
.ptr
[3*1+2]+m
.ptr
[3*0+2]*b
.m
.ptr
[3*2+2];
1539 res
.m
.ptr
[3*1+0] = m
.ptr
[3*1+0]*b
.m
.ptr
[3*0+0]+m
.ptr
[3*1+1]*b
.m
.ptr
[3*1+0]+m
.ptr
[3*1+2]*b
.m
.ptr
[3*2+0];
1540 res
.m
.ptr
[3*1+1] = m
.ptr
[3*1+0]*b
.m
.ptr
[3*0+1]+m
.ptr
[3*1+1]*b
.m
.ptr
[3*1+1]+m
.ptr
[3*1+2]*b
.m
.ptr
[3*2+1];
1541 res
.m
.ptr
[3*1+2] = m
.ptr
[3*1+0]*b
.m
.ptr
[3*0+2]+m
.ptr
[3*1+1]*b
.m
.ptr
[3*1+2]+m
.ptr
[3*1+2]*b
.m
.ptr
[3*2+2];
1542 res
.m
.ptr
[3*2+0] = m
.ptr
[3*2+0]*b
.m
.ptr
[3*0+0]+m
.ptr
[3*2+1]*b
.m
.ptr
[3*1+0]+m
.ptr
[3*2+2]*b
.m
.ptr
[3*2+0];
1543 res
.m
.ptr
[3*2+1] = m
.ptr
[3*2+0]*b
.m
.ptr
[3*0+1]+m
.ptr
[3*2+1]*b
.m
.ptr
[3*1+1]+m
.ptr
[3*2+2]*b
.m
.ptr
[3*2+1];
1544 res
.m
.ptr
[3*2+2] = m
.ptr
[3*2+0]*b
.m
.ptr
[3*0+2]+m
.ptr
[3*2+1]*b
.m
.ptr
[3*1+2]+m
.ptr
[3*2+2]*b
.m
.ptr
[3*2+2];
1548 // multiply vector by transposed matrix (TESTME!)
1549 static if (ThreeD
) auto transmul() (in auto ref v3 v
) const {
1550 pragma(inline
, true);
1552 v
.x
*m
.ptr
[3*0+0]+v
.y
*m
.ptr
[3*0+1]+v
.z
*m
.ptr
[3*0+2],
1553 v
.x
*m
.ptr
[3*1+0]+v
.y
*m
.ptr
[3*1+1]+v
.z
*m
.ptr
[3*1+2],
1554 v
.x
*m
.ptr
[3*2+0]+v
.y
*m
.ptr
[3*2+1]+v
.z
*m
.ptr
[3*2+2],
1558 // sum of the diagonal components
1559 Float
trace () const { pragma(inline
, true); return m
.ptr
[3*0+0]+m
.ptr
[3*1+1]+m
.ptr
[3*2+2]; }
1562 Float
det () const {
1563 pragma(inline
, true);
1565 res
+= m
.ptr
[3*0+0]*(m
.ptr
[3*1+1]*m
.ptr
[3*2+2]-m
.ptr
[3*2+1]*m
.ptr
[3*1+2]);
1566 res
-= m
.ptr
[3*0+1]*(m
.ptr
[3*1+0]*m
.ptr
[3*2+2]-m
.ptr
[3*2+0]*m
.ptr
[3*1+2]);
1567 res
+= m
.ptr
[3*0+2]*(m
.ptr
[3*1+0]*m
.ptr
[3*2+1]-m
.ptr
[3*2+0]*m
.ptr
[3*1+1]);
1571 auto transposed () const {
1572 pragma(inline
, true);
1574 res
.m
.ptr
[3*0+0] = m
.ptr
[3*0+0];
1575 res
.m
.ptr
[3*0+1] = m
.ptr
[3*1+0];
1576 res
.m
.ptr
[3*0+2] = m
.ptr
[3*2+0];
1577 res
.m
.ptr
[3*1+0] = m
.ptr
[3*0+1];
1578 res
.m
.ptr
[3*1+1] = m
.ptr
[3*1+1];
1579 res
.m
.ptr
[3*1+2] = m
.ptr
[3*2+1];
1580 res
.m
.ptr
[3*2+0] = m
.ptr
[3*0+2];
1581 res
.m
.ptr
[3*2+1] = m
.ptr
[3*1+2];
1582 res
.m
.ptr
[3*2+2] = m
.ptr
[3*2+2];
1587 //pragma(inline, true);
1588 immutable mtp
= this.transposed
;
1591 res
.m
.ptr
[3*0+0] = mtp
.m
.ptr
[3*1+1]*mtp
.m
.ptr
[3*2+2]-mtp
.m
.ptr
[3*2+1]*mtp
.m
.ptr
[3*1+2];
1592 res
.m
.ptr
[3*0+1] = mtp
.m
.ptr
[3*1+0]*mtp
.m
.ptr
[3*2+2]-mtp
.m
.ptr
[3*2+0]*mtp
.m
.ptr
[3*1+2];
1593 res
.m
.ptr
[3*0+2] = mtp
.m
.ptr
[3*1+0]*mtp
.m
.ptr
[3*2+1]-mtp
.m
.ptr
[3*2+0]*mtp
.m
.ptr
[3*1+1];
1594 res
.m
.ptr
[3*1+0] = mtp
.m
.ptr
[3*0+1]*mtp
.m
.ptr
[3*2+2]-mtp
.m
.ptr
[3*2+1]*mtp
.m
.ptr
[3*0+2];
1595 res
.m
.ptr
[3*1+1] = mtp
.m
.ptr
[3*0+0]*mtp
.m
.ptr
[3*2+2]-mtp
.m
.ptr
[3*2+0]*mtp
.m
.ptr
[3*0+2];
1596 res
.m
.ptr
[3*1+2] = mtp
.m
.ptr
[3*0+0]*mtp
.m
.ptr
[3*2+1]-mtp
.m
.ptr
[3*2+0]*mtp
.m
.ptr
[3*0+1];
1597 res
.m
.ptr
[3*2+0] = mtp
.m
.ptr
[3*0+1]*mtp
.m
.ptr
[3*1+2]-mtp
.m
.ptr
[3*1+1]*mtp
.m
.ptr
[3*0+2];
1598 res
.m
.ptr
[3*2+1] = mtp
.m
.ptr
[3*0+0]*mtp
.m
.ptr
[3*1+2]-mtp
.m
.ptr
[3*1+0]*mtp
.m
.ptr
[3*0+2];
1599 res
.m
.ptr
[3*2+2] = mtp
.m
.ptr
[3*0+0]*mtp
.m
.ptr
[3*1+1]-mtp
.m
.ptr
[3*1+0]*mtp
.m
.ptr
[3*0+1];
1601 res
.m
.ptr
[3*0+1] *= -1;
1602 res
.m
.ptr
[3*1+0] *= -1;
1603 res
.m
.ptr
[3*1+2] *= -1;
1604 res
.m
.ptr
[3*2+1] *= -1;
1606 return res
/this.det
;
1609 static if (ThreeD
) {
1610 ref m3
rotateX (Float angle
) {
1611 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
1614 // get the sine and cosine of the rotation angle
1615 immutable Float s
= sin(angle
);
1616 immutable Float c
= cos(angle
);
1618 // calculate the new values of the six affected matrix entries
1619 immutable Float temp01
= c
*A
[0, 1]+s
*A
[0, 2];
1620 immutable Float temp11
= c
*A
[1, 1]+s
*A
[1, 2];
1621 immutable Float temp21
= c
*A
[2, 1]+s
*A
[2, 2];
1622 immutable Float temp02
= c
*A
[0, 2]-s
*A
[0, 1];
1623 immutable Float temp12
= c
*A
[1, 2]-s
*A
[1, 1];
1624 immutable Float temp22
= c
*A
[2, 2]-s
*A
[2, 1];
1626 // put the results back into A
1627 A
[0, 1] = temp01
; A
[0, 2] = temp02
;
1628 A
[1, 1] = temp11
; A
[1, 2] = temp12
;
1629 A
[2, 1] = temp21
; A
[2, 2] = temp22
;
1634 ref m3
rotateY (Float angle
) {
1635 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
1638 // get the sine and cosine of the rotation angle
1639 immutable Float s
= sin(angle
);
1640 immutable Float c
= cos(angle
);
1642 // calculate the new values of the six affected matrix entries
1643 immutable Float temp00
= c
*A
[0, 0]+s
*A
[0, 2];
1644 immutable Float temp10
= c
*A
[1, 0]+s
*A
[1, 2];
1645 immutable Float temp20
= c
*A
[2, 0]+s
*A
[2, 2];
1646 immutable Float temp02
= c
*A
[0, 2]-s
*A
[0, 0];
1647 immutable Float temp12
= c
*A
[1, 2]-s
*A
[1, 0];
1648 immutable Float temp22
= c
*A
[2, 2]-s
*A
[2, 0];
1650 // put the results back into XformToChange
1651 A
[0, 0] = temp00
; A
[0, 2] = temp02
;
1652 A
[1, 0] = temp10
; A
[1, 2] = temp12
;
1653 A
[2, 0] = temp20
; A
[2, 2] = temp22
;
1658 ref m3
rotateZ (Float angle
) {
1659 import core
.stdc
.math
: cos
, sin
;
1662 // get the sine and cosine of the rotation angle
1663 immutable Float s
= sin(angle
);
1664 immutable Float c
= cos(angle
);
1666 // calculate the new values of the six affected matrix entries
1667 immutable Float temp00
= c
*A
[0, 0]+s
*A
[0, 1];
1668 immutable Float temp10
= c
*A
[1, 0]+s
*A
[1, 1];
1669 immutable Float temp20
= c
*A
[2, 0]+s
*A
[2, 1];
1670 immutable Float temp01
= c
*A
[0, 1]-s
*A
[0, 0];
1671 immutable Float temp11
= c
*A
[1, 1]-s
*A
[1, 0];
1672 immutable Float temp21
= c
*A
[2, 1]-s
*A
[2, 0];
1674 // put the results back into XformToChange
1675 A
[0, 0] = temp00
; A
[0, 1] = temp01
;
1676 A
[1, 0] = temp10
; A
[1, 1] = temp11
;
1677 A
[2, 0] = temp20
; A
[2, 1] = temp21
;
1684 auto Identity () { pragma(inline
, true); return m3(); }
1685 auto Zero () { pragma(inline
, true); return m3(0); }
1688 auto Rotate (in Float angle
) {
1689 pragma(inline
, true);
1690 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
1691 immutable Float c
= cos(angle
);
1692 immutable Float s
= sin(angle
);
1694 res
.m
.ptr
[3*0+0] = c
; res
.m
.ptr
[3*0+1] = s
;
1695 res
.m
.ptr
[3*1+0] = -s
; res
.m
.ptr
[3*1+1] = c
;
1699 auto Scale (in Float sx
, in Float sy
) {
1700 pragma(inline
, true);
1702 res
.m
.ptr
[3*0+0] = sx
;
1703 res
.m
.ptr
[3*1+1] = sy
;
1707 auto Scale() (in auto ref v2 sc
) {
1708 pragma(inline
, true);
1710 res
.m
.ptr
[3*0+0] = sc
.x
;
1711 res
.m
.ptr
[3*1+1] = sc
.y
;
1715 auto Translate (in Float dx
, in Float dy
) {
1716 pragma(inline
, true);
1718 res
.m
.ptr
[3*2+0] = dx
;
1719 res
.m
.ptr
[3*2+1] = dy
;
1723 auto Translate() (in auto ref v2 v
) {
1724 pragma(inline
, true);
1726 res
.m
.ptr
[3*2+0] = v
.x
;
1727 res
.m
.ptr
[3*2+1] = v
.y
;
1732 static if (ThreeD
) {
1733 // make rotation matrix from given angles
1734 static auto Rotate() (in auto ref v3 angles
) {
1735 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
1737 immutable Float cos_b
= cos(angles
[0]);
1738 immutable Float sin_b
= sin(angles
[0]);
1739 immutable Float cos_c
= cos(angles
[1]);
1740 immutable Float sin_c
= sin(angles
[1]);
1741 immutable Float cos_a
= cos(angles
[2]);
1742 immutable Float sin_a
= sin(angles
[2]);
1747 M
[0, 0] = cos_a
*cos_c
-sin_a
*sin_b
*sin_c
;
1748 M
[0, 1] = sin_a
*cos_c
+cos_a
*sin_b
*sin_c
;
1749 M
[0, 2] = cos_b
*sin_c
;
1751 // second matrix row
1752 M
[1, 0] = -sin_a
*cos_b
;
1753 M
[1, 1] = cos_a
*cos_b
;
1757 M
[2, 0] = -cos_a
*sin_c
-sin_a
*sin_b
*cos_c
;
1758 M
[2, 1] = -sin_a
*sin_c
+cos_a
*sin_b
*cos_c
;
1759 M
[2, 2] = cos_b
*cos_c
;
1764 static auto RotateX() (Float angle
) {
1770 static auto RotateY() (Float angle
) {
1776 static auto RotateZ() (Float angle
) {
1785 // ////////////////////////////////////////////////////////////////////////// //
1786 alias mat4
= Mat4
!vec3
;
1788 align(1) struct Mat4(VT
) if (IsVectorDim
!(VT
, 3)) {
1791 alias Float
= VT
.Float
;
1792 alias mat4
= typeof(this);
1793 alias vec3
= VecN
!(3, Float
);
1796 // OpenGL-compatible, row by row
1805 string
toString () const @trusted {
1806 import std
.string
: format
;
1808 return "0:[%g,%g,%g,%g]\n4:[%g,%g,%g,%g]\n8:[%g,%g,%g,%g]\nc:[%g,%g,%g,%g]".format(
1809 mt
.ptr
[ 0], mt
.ptr
[ 1], mt
.ptr
[ 2], mt
.ptr
[ 3],
1810 mt
.ptr
[ 4], mt
.ptr
[ 5], mt
.ptr
[ 6], mt
.ptr
[ 7],
1811 mt
.ptr
[ 8], mt
.ptr
[ 9], mt
.ptr
[10], mt
.ptr
[11],
1812 mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14], mt
.ptr
[15],
1814 } catch (Exception
) {
1819 Float
opIndex (usize x
, usize y
) const @trusted @nogc { pragma(inline
, true); return (x
< 4 && y
< 4 ? mt
.ptr
[y
*4+x
] : Float
.nan
); }
1820 void opIndexAssign (Float v
, usize x
, usize y
) @trusted @nogc { pragma(inline
, true); if (x
< 4 && y
< 4) mt
.ptr
[y
*4+x
] = v
; }
1823 this() (in Float
[] vals
...) { pragma(inline
, true); if (vals
.length
>= 16) mt
.ptr
[0..16] = vals
.ptr
[0..16]; else { mt
.ptr
[0..16] = 0; mt
.ptr
[0..vals
.length
] = vals
.ptr
[0..vals
.length
]; } }
1824 this() (in auto ref mat4 m
) { pragma(inline
, true); mt
.ptr
[0..16] = m
.mt
.ptr
[0..16]; }
1825 this() (in auto ref vec3 v
) {
1826 //mt.ptr[0..16] = 0;
1827 mt
.ptr
[0*4+0] = v
.x
;
1828 mt
.ptr
[1*4+1] = v
.y
;
1829 mt
.ptr
[2*4+2] = v
.z
;
1830 //mt.ptr[3*4+3] = 1; // just in case
1833 static mat4
Zero () { pragma(inline
, true); return mat4(0); }
1834 static mat4
Identity () { pragma(inline
, true); /*mat4 res = Zero; res.mt.ptr[0*4+0] = res.mt.ptr[1*4+1] = res.mt.ptr[2*4+2] = res.mt.ptr[3*4+3] = 1; return res;*/ return mat4(); }
1836 // multiply current OpenGL matrix with this one
1837 // templated, to not compile it if we won't use it
1838 void glMultiply() () const {
1839 pragma(inline
, true);
1841 static if (is(Float
== float)) {
1842 glMultMatrixf(mt
.ptr
);
1844 glMultMatrixd(mt
.ptr
);
1848 static mat4
GLRetrieveAny() (uint mode
) nothrow @trusted @nogc {
1849 pragma(inline
, true);
1852 static if (is(Float
== float)) {
1853 glGetFloatv(mode
, res
.mt
.ptr
);
1855 glGetDoublev(mode
, res
.mt
.ptr
);
1860 static mat4
GLRetrieveModelView() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; return GLRetrieveAny(GL_MODELVIEW_MATRIX
); }
1861 static mat4
GLRetrieveProjection() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; return GLRetrieveAny(GL_PROJECTION_MATRIX
); }
1862 static mat4
GLRetrieveTexture() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; return GLRetrieveAny(GL_TEXTURE_MATRIX
); }
1863 static mat4
GLRetrieveColor() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; return GLRetrieveAny(GL_COLOR_MATRIX
); }
1865 void glRetrieveAny() (uint mode
) nothrow @trusted @nogc {
1866 pragma(inline
, true);
1868 static if (is(Float
== float)) {
1869 glGetFloatv(mode
, mt
.ptr
);
1871 glGetDoublev(mode
, mt
.ptr
);
1875 void glRetrieveModelView() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; glRetrieveAny(GL_MODELVIEW_MATRIX
); }
1876 void glRetrieveProjection() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; glRetrieveAny(GL_PROJECTION_MATRIX
); }
1877 void glRetrieveTexture() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; glRetrieveAny(GL_TEXTURE_MATRIX
); }
1878 void glRetrieveColor() () nothrow @trusted @nogc { pragma(inline
, true); import iv
.glbinds
; glRetrieveAny(GL_COLOR_MATRIX
); }
1880 @property bool isIdentity () const {
1881 pragma(inline
, true); // can we?
1883 mt
.ptr
[0] == 1 && mt
.ptr
[1] == 0 && mt
.ptr
[2] == 0 && mt
.ptr
[3] == 0 &&
1884 mt
.ptr
[4] == 0 && mt
.ptr
[5] == 1 && mt
.ptr
[6] == 0 && mt
.ptr
[7] == 0 &&
1885 mt
.ptr
[8] == 0 && mt
.ptr
[9] == 0 && mt
.ptr
[10] == 1 && mt
.ptr
[11] == 0 &&
1886 mt
.ptr
[12] == 0 && mt
.ptr
[13] == 0 && mt
.ptr
[14] == 0 && mt
.ptr
[15] == 1;
1889 @property inout(Float
)* glGetVUnsafe () inout nothrow @trusted @nogc { pragma(inline
, true); return cast(typeof(return))mt
.ptr
; }
1891 Float
[4] getRow (int idx
) const {
1892 Float
[4] res
= Float
.nan
;
1895 res
.ptr
[0] = mt
.ptr
[0];
1896 res
.ptr
[1] = mt
.ptr
[4];
1897 res
.ptr
[2] = mt
.ptr
[8];
1898 res
.ptr
[3] = mt
.ptr
[12];
1901 res
.ptr
[0] = mt
.ptr
[1];
1902 res
.ptr
[1] = mt
.ptr
[5];
1903 res
.ptr
[2] = mt
.ptr
[9];
1904 res
.ptr
[3] = mt
.ptr
[13];
1907 res
.ptr
[0] = mt
.ptr
[2];
1908 res
.ptr
[1] = mt
.ptr
[6];
1909 res
.ptr
[2] = mt
.ptr
[10];
1910 res
.ptr
[3] = mt
.ptr
[14];
1913 res
.ptr
[0] = mt
.ptr
[3];
1914 res
.ptr
[1] = mt
.ptr
[7];
1915 res
.ptr
[2] = mt
.ptr
[11];
1916 res
.ptr
[3] = mt
.ptr
[15];
1923 Float
[4] getCol (int idx
) const {
1924 Float
[4] res
= Float
.nan
;
1925 if (idx
>= 0 && idx
<= 3) res
= mt
.ptr
[idx
*4..idx
*5];
1929 // this is for camera matrices
1930 vec3
upVector () const { pragma(inline
, true); return vec3(mt
.ptr
[1], mt
.ptr
[5], mt
.ptr
[9]); }
1931 vec3
rightVector () const { pragma(inline
, true); return vec3(mt
.ptr
[0], mt
.ptr
[4], mt
.ptr
[8]); }
1932 vec3
forwardVector () const { pragma(inline
, true); return vec3(mt
.ptr
[2], mt
.ptr
[6], mt
.ptr
[10]); }
1934 private enum SinCosImportMixin
= q
{
1935 static if (is(Float
== float)) {
1936 import core
.stdc
.math
: cos
=cosf
, sin
=sinf
;
1938 import core
.stdc
.math
: cos
, sin
;
1942 static mat4
RotateX() (Float angle
) {
1943 mixin(SinCosImportMixin
);
1945 res
.mt
.ptr
[0*4+0] = cast(Float
)1;
1946 res
.mt
.ptr
[1*4+1] = cos(angle
);
1947 res
.mt
.ptr
[2*4+1] = -sin(angle
);
1948 res
.mt
.ptr
[1*4+2] = sin(angle
);
1949 res
.mt
.ptr
[2*4+2] = cos(angle
);
1950 res
.mt
.ptr
[3*4+3] = cast(Float
)1;
1954 static mat4
RotateY() (Float angle
) {
1955 mixin(SinCosImportMixin
);
1957 res
.mt
.ptr
[0*4+0] = cos(angle
);
1958 res
.mt
.ptr
[2*4+0] = sin(angle
);
1959 res
.mt
.ptr
[1*4+1] = cast(Float
)1;
1960 res
.mt
.ptr
[0*4+2] = -sin(angle
);
1961 res
.mt
.ptr
[2*4+2] = cos(angle
);
1962 res
.mt
.ptr
[3*4+3] = cast(Float
)1;
1966 static mat4
RotateZ() (Float angle
) {
1967 mixin(SinCosImportMixin
);
1969 res
.mt
.ptr
[0*4+0] = cos(angle
);
1970 res
.mt
.ptr
[1*4+0] = -sin(angle
);
1971 res
.mt
.ptr
[0*4+1] = sin(angle
);
1972 res
.mt
.ptr
[1*4+1] = cos(angle
);
1973 res
.mt
.ptr
[2*4+2] = cast(Float
)1;
1974 res
.mt
.ptr
[3*4+3] = cast(Float
)1;
1978 static mat4
Translate() (in auto ref vec3 v
) {
1980 res
.mt
.ptr
[0*4+0] = res
.mt
.ptr
[1*4+1] = res
.mt
.ptr
[2*4+2] = 1;
1981 res
.mt
.ptr
[3*4+0] = v
.x
;
1982 res
.mt
.ptr
[3*4+1] = v
.y
;
1983 res
.mt
.ptr
[3*4+2] = v
.z
;
1984 res
.mt
.ptr
[3*4+3] = 1;
1988 static mat4
TranslateNeg() (in auto ref vec3 v
) {
1990 res
.mt
.ptr
[0*4+0] = res
.mt
.ptr
[1*4+1] = res
.mt
.ptr
[2*4+2] = 1;
1991 res
.mt
.ptr
[3*4+0] = -v
.x
;
1992 res
.mt
.ptr
[3*4+1] = -v
.y
;
1993 res
.mt
.ptr
[3*4+2] = -v
.z
;
1994 res
.mt
.ptr
[3*4+3] = 1;
1998 static mat4
Scale() (in auto ref vec3 v
) {
2000 res
.mt
.ptr
[0*4+0] = v
.x
;
2001 res
.mt
.ptr
[1*4+1] = v
.y
;
2002 res
.mt
.ptr
[2*4+2] = v
.z
;
2003 res
.mt
.ptr
[3*4+3] = 1;
2007 static mat4
Rotate() (in auto ref vec3 v
) {
2008 auto mx
= mat4
.RotateX(v
.x
);
2009 auto my
= mat4
.RotateY(v
.y
);
2010 auto mz
= mat4
.RotateZ(v
.z
);
2014 static mat4
RotateDeg() (in auto ref vec3 v
) {
2015 auto mx
= mat4
.RotateX(v
.x
.deg2rad
);
2016 auto my
= mat4
.RotateY(v
.y
.deg2rad
);
2017 auto mz
= mat4
.RotateZ(v
.z
.deg2rad
);
2021 // for camera; x is pitch (up/down); y is yaw (left/right); z is roll (tilt)
2022 static mat4
RotateZXY() (in auto ref vec3 v
) {
2023 auto mx
= mat4
.RotateX(v
.x
);
2024 auto my
= mat4
.RotateY(v
.y
);
2025 auto mz
= mat4
.RotateZ(v
.z
);
2029 // for camera; x is pitch (up/down); y is yaw (left/right); z is roll (tilt)
2030 static mat4
RotateZXYDeg() (in auto ref vec3 v
) {
2031 auto mx
= mat4
.RotateX(v
.x
.deg2rad
);
2032 auto my
= mat4
.RotateY(v
.y
.deg2rad
);
2033 auto mz
= mat4
.RotateZ(v
.z
.deg2rad
);
2037 // same as `glFrustum()`
2038 static mat4
Frustum() (Float left
, Float right
, Float bottom
, Float top
, Float nearVal
, Float farVal
) nothrow @trusted @nogc {
2040 res
.mt
.ptr
[0] = 2*nearVal
/(right
-left
);
2041 res
.mt
.ptr
[5] = 2*nearVal
/(top
-bottom
);
2042 res
.mt
.ptr
[8] = (right
+left
)/(right
-left
);
2043 res
.mt
.ptr
[9] = (top
+bottom
)/(top
-bottom
);
2044 res
.mt
.ptr
[10] = -(farVal
+nearVal
)/(farVal
-nearVal
);
2045 res
.mt
.ptr
[11] = -1;
2046 res
.mt
.ptr
[14] = -(2*farVal
*nearVal
)/(farVal
-nearVal
);
2051 // same as `glOrtho()`
2052 static mat4
Ortho() (Float left
, Float right
, Float bottom
, Float top
, Float nearVal
, Float farVal
) nothrow @trusted @nogc {
2054 res
.mt
.ptr
[0] = 2/(right
-left
);
2055 res
.mt
.ptr
[5] = 2/(top
-bottom
);
2056 res
.mt
.ptr
[10] = -2/(farVal
-nearVal
);
2057 res
.mt
.ptr
[12] = -(right
+left
)/(right
-left
);
2058 res
.mt
.ptr
[13] = -(top
+bottom
)/(top
-bottom
);
2059 res
.mt
.ptr
[14] = -(farVal
+nearVal
)/(farVal
-nearVal
);
2063 // same as `gluPerspective()`
2064 // sets the frustum to perspective mode
2065 // fovY - Field of vision in degrees in the y direction
2066 // aspect - Aspect ratio of the viewport
2067 // zNear - The near clipping distance
2068 // zFar - The far clipping distance
2069 static mat4
Perspective() (Float fovY
, Float aspect
, Float zNear
, Float zFar
) nothrow @trusted @nogc {
2070 import std
.math
: PI
;
2071 mixin(ImportCoreMath
!(Float
, "tan"));
2072 immutable Float fH
= cast(Float
)(tan(fovY
/360*PI
)*zNear
);
2073 immutable Float fW
= cast(Float
)(fH
*aspect
);
2074 return Frustum(-fW
, fW
, -fH
, fH
, zNear
, zFar
);
2078 static mat4
LookAtFucked() (in auto ref vec3 eye
, in auto ref vec3 center
, in auto ref vec3 up
) {
2079 // compute vector `N = EP-VRP` and normalize `N`
2080 vec3 n
= eye
-center
;
2083 // compute vector `V = UP-VRP`
2084 // make vector `V` orthogonal to `N` and normalize `V`
2086 immutable dp
= v
.dot(n
); //dp = (float)V3Dot(&v,&n);
2087 //v.x -= dp * n.x; v.y -= dp * n.y; v.z -= dp * n.z;
2091 // compute vector `U = V x N` (cross product)
2092 immutable vec3 u
= v
.cross(n
);
2094 // write the vectors `U`, `V`, and `N` as the first three rows of first, second, and third columns of transformation matrix
2097 m
.mt
.ptr
[0*4+0] = u
.x
;
2098 m
.mt
.ptr
[1*4+0] = u
.y
;
2099 m
.mt
.ptr
[2*4+0] = u
.z
;
2100 //m.mt.ptr[3*4+0] = 0;
2102 m
.mt
.ptr
[0*4+1] = v
.x
;
2103 m
.mt
.ptr
[1*4+1] = v
.y
;
2104 m
.mt
.ptr
[2*4+1] = v
.z
;
2105 //m.mt.ptr[3*4+1] = 0;
2107 m
.mt
.ptr
[0*4+2] = n
.x
;
2108 m
.mt
.ptr
[1*4+2] = n
.y
;
2109 m
.mt
.ptr
[2*4+2] = n
.z
;
2110 //m.mt.ptr[3*4+2] = 0;
2112 // compute the fourth row of transformation matrix to include the translation of `VRP` to the origin
2113 m
.mt
.ptr
[3*4+0] = -u
.x
*center
.x
-u
.y
*center
.y
-u
.z
*center
.z
;
2114 m
.mt
.ptr
[3*4+1] = -v
.x
*center
.x
-v
.y
*center
.y
-v
.z
*center
.z
;
2115 m
.mt
.ptr
[3*4+2] = -n
.x
*center
.x
-n
.y
*center
.y
-n
.z
*center
.z
;
2116 m
.mt
.ptr
[3*4+3] = 1;
2118 foreach (ref Float f
; m
.mt
[]) {
2119 mixin(ImportCoreMath
!(Float
, "fabs"));
2120 if (fabs(f
) < EPSILON
!Float
) f
= 0;
2126 // does `gluLookAt()`
2127 mat4
lookAt() (in auto ref vec3 eye
, in auto ref vec3 center
, in auto ref vec3 up
) const {
2128 mixin(ImportCoreMath
!(Float
, "sqrt"));
2131 Float
[3] x
= void, y
= void, z
= void;
2133 // make rotation matrix
2135 z
.ptr
[0] = eye
.x
-center
.x
;
2136 z
.ptr
[1] = eye
.y
-center
.y
;
2137 z
.ptr
[2] = eye
.z
-center
.z
;
2138 mag
= sqrt(z
.ptr
[0]*z
.ptr
[0]+z
.ptr
[1]*z
.ptr
[1]+z
.ptr
[2]*z
.ptr
[2]);
2148 // X vector = Y cross Z
2149 x
.ptr
[0] = y
.ptr
[1]*z
.ptr
[2]-y
.ptr
[2]*z
.ptr
[1];
2150 x
.ptr
[1] = -y
.ptr
[0]*z
.ptr
[2]+y
.ptr
[2]*z
.ptr
[0];
2151 x
.ptr
[2] = y
.ptr
[0]*z
.ptr
[1]-y
.ptr
[1]*z
.ptr
[0];
2152 // Recompute Y = Z cross X
2153 y
.ptr
[0] = z
.ptr
[1]*x
.ptr
[2]-z
.ptr
[2]*x
.ptr
[1];
2154 y
.ptr
[1] = -z
.ptr
[0]*x
.ptr
[2]+z
.ptr
[2]*x
.ptr
[0];
2155 y
.ptr
[2] = z
.ptr
[0]*x
.ptr
[1]-z
.ptr
[1]*x
.ptr
[0];
2157 /* cross product gives area of parallelogram, which is < 1.0 for
2158 * non-perpendicular unit-length vectors; so normalize x, y here
2160 mag
= sqrt(x
.ptr
[0]*x
.ptr
[0]+x
.ptr
[1]*x
.ptr
[1]+x
.ptr
[2]*x
.ptr
[2]);
2167 mag
= sqrt(y
.ptr
[0]*y
.ptr
[0]+y
.ptr
[1]*y
.ptr
[1]+y
.ptr
[2]*y
.ptr
[2]);
2174 m
.mt
.ptr
[0*4+0] = x
.ptr
[0];
2175 m
.mt
.ptr
[1*4+0] = x
.ptr
[1];
2176 m
.mt
.ptr
[2*4+0] = x
.ptr
[2];
2177 m
.mt
.ptr
[3*4+0] = 0;
2178 m
.mt
.ptr
[0*4+1] = y
.ptr
[0];
2179 m
.mt
.ptr
[1*4+1] = y
.ptr
[1];
2180 m
.mt
.ptr
[2*4+1] = y
.ptr
[2];
2181 m
.mt
.ptr
[3*4+1] = 0;
2182 m
.mt
.ptr
[0*4+2] = z
.ptr
[0];
2183 m
.mt
.ptr
[1*4+2] = z
.ptr
[1];
2184 m
.mt
.ptr
[2*4+2] = z
.ptr
[2];
2185 m
.mt
.ptr
[3*4+2] = 0;
2186 m
.mt
.ptr
[0*4+3] = 0;
2187 m
.mt
.ptr
[1*4+3] = 0;
2188 m
.mt
.ptr
[2*4+3] = 0;
2189 m
.mt
.ptr
[3*4+3] = 1;
2191 // move, and translate Eye to Origin
2192 return this*m
*Translate(-eye
);
2195 // rotate matrix to face along the target direction
2196 // this function will clear the previous rotation and scale, but it will keep the previous translation
2197 // it is for rotating object to look at the target, NOT for camera
2198 ref mat4
lookingAt() (in auto ref vec3 target
) {
2199 mixin(ImportCoreMath
!(Float
, "fabs"));
2200 vec3 position
= vec3(mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2201 vec3 forward
= (target
-position
).normalized
;
2203 if (fabs(forward
.x
) < EPSILON
!Float
&& fabs(forward
.z
) < EPSILON
!Float
) {
2204 up
.z
= (forward
.y
> 0 ?
-1 : 1);
2208 vec3 left
= up
.cross(forward
).normalized
;
2209 up
= forward
.cross(left
).normalized
; //k8: `normalized` was commented out; why?
2210 mt
.ptr
[0*4+0] = left
.x
;
2211 mt
.ptr
[0*4+1] = left
.y
;
2212 mt
.ptr
[0*4+2] = left
.z
;
2213 mt
.ptr
[1*4+0] = up
.x
;
2214 mt
.ptr
[1*4+1] = up
.y
;
2215 mt
.ptr
[1*4+2] = up
.z
;
2216 mt
.ptr
[2*4+0] = forward
.x
;
2217 mt
.ptr
[2*4+1] = forward
.y
;
2218 mt
.ptr
[2*4+2] = forward
.z
;
2222 ref mat4
lookingAt() (in auto ref vec3 target
, in auto ref vec3 upVec
) {
2223 vec3 position
= vec3(mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2224 vec3 forward
= (target
-position
).normalized
;
2225 vec3 left
= upVec
.cross(forward
).normalized
;
2226 vec3 up
= forward
.cross(left
).normalized
;
2227 mt
.ptr
[0*4+0] = left
.x
;
2228 mt
.ptr
[0*4+1] = left
.y
;
2229 mt
.ptr
[0*4+2] = left
.z
;
2230 mt
.ptr
[1*4+0] = up
.x
;
2231 mt
.ptr
[1*4+1] = up
.y
;
2232 mt
.ptr
[1*4+2] = up
.z
;
2233 mt
.ptr
[2*4+0] = forward
.x
;
2234 mt
.ptr
[2*4+1] = forward
.y
;
2235 mt
.ptr
[2*4+2] = forward
.z
;
2239 mat4
lookAt() (in auto ref vec3 target
) { pragma(inline
, true); auto res
= mat4(this); return this.lookingAt(target
); }
2240 mat4
lookAt() (in auto ref vec3 target
, in auto ref vec3 upVec
) { pragma(inline
, true); auto res
= mat4(this); return this.lookingAt(target
, upVec
); }
2242 ref mat4
rotate() (Float angle
, in auto ref vec3 axis
) {
2243 mixin(SinCosImportMixin
);
2244 angle
= deg2rad(angle
);
2245 immutable Float c
= cos(angle
);
2246 immutable Float s
= sin(angle
);
2247 immutable Float c1
= 1-c
;
2248 immutable Float m0
= mt
.ptr
[0], m4
= mt
.ptr
[4], m8
= mt
.ptr
[8], m12
= mt
.ptr
[12];
2249 immutable Float m1
= mt
.ptr
[1], m5
= mt
.ptr
[5], m9
= mt
.ptr
[9], m13
= mt
.ptr
[13];
2250 immutable Float m2
= mt
.ptr
[2], m6
= mt
.ptr
[6], m10
= mt
.ptr
[10], m14
= mt
.ptr
[14];
2252 // build rotation matrix
2253 immutable Float r0
= axis
.x
*axis
.x
*c1
+c
;
2254 immutable Float r1
= axis
.x
*axis
.y
*c1
+axis
.z
*s
;
2255 immutable Float r2
= axis
.x
*axis
.z
*c1
-axis
.y
*s
;
2256 immutable Float r4
= axis
.x
*axis
.y
*c1
-axis
.z
*s
;
2257 immutable Float r5
= axis
.y
*axis
.y
*c1
+c
;
2258 immutable Float r6
= axis
.y
*axis
.z
*c1
+axis
.x
*s
;
2259 immutable Float r8
= axis
.x
*axis
.z
*c1
+axis
.y
*s
;
2260 immutable Float r9
= axis
.y
*axis
.z
*c1
-axis
.x
*s
;
2261 immutable Float r10
= axis
.z
*axis
.z
*c1
+c
;
2263 // multiply rotation matrix
2264 mt
.ptr
[0] = r0
*m0
+r4
*m1
+r8
*m2
;
2265 mt
.ptr
[1] = r1
*m0
+r5
*m1
+r9
*m2
;
2266 mt
.ptr
[2] = r2
*m0
+r6
*m1
+r10
*m2
;
2267 mt
.ptr
[4] = r0
*m4
+r4
*m5
+r8
*m6
;
2268 mt
.ptr
[5] = r1
*m4
+r5
*m5
+r9
*m6
;
2269 mt
.ptr
[6] = r2
*m4
+r6
*m5
+r10
*m6
;
2270 mt
.ptr
[8] = r0
*m8
+r4
*m9
+r8
*m10
;
2271 mt
.ptr
[9] = r1
*m8
+r5
*m9
+r9
*m10
;
2272 mt
.ptr
[10] = r2
*m8
+r6
*m9
+r10
*m10
;
2273 mt
.ptr
[12] = r0
*m12
+r4
*m13
+r8
*m14
;
2274 mt
.ptr
[13] = r1
*m12
+r5
*m13
+r9
*m14
;
2275 mt
.ptr
[14] = r2
*m12
+r6
*m13
+r10
*m14
;
2280 ref mat4
rotateX() (Float angle
) {
2281 mixin(SinCosImportMixin
);
2282 angle
= deg2rad(angle
);
2283 immutable Float c
= cos(angle
);
2284 immutable Float s
= sin(angle
);
2285 immutable Float m1
= mt
.ptr
[1], m2
= mt
.ptr
[2];
2286 immutable Float m5
= mt
.ptr
[5], m6
= mt
.ptr
[6];
2287 immutable Float m9
= mt
.ptr
[9], m10
= mt
.ptr
[10];
2288 immutable Float m13
= mt
.ptr
[13], m14
= mt
.ptr
[14];
2290 mt
.ptr
[1] = m1
*c
+m2
*-s
;
2291 mt
.ptr
[2] = m1
*s
+m2
*c
;
2292 mt
.ptr
[5] = m5
*c
+m6
*-s
;
2293 mt
.ptr
[6] = m5
*s
+m6
*c
;
2294 mt
.ptr
[9] = m9
*c
+m10
*-s
;
2295 mt
.ptr
[10]= m9
*s
+m10
*c
;
2296 mt
.ptr
[13]= m13
*c
+m14
*-s
;
2297 mt
.ptr
[14]= m13
*s
+m14
*c
;
2302 ref mat4
rotateY() (Float angle
) {
2303 mixin(SinCosImportMixin
);
2304 angle
= deg2rad(angle
);
2305 immutable Float c
= cos(angle
);
2306 immutable Float s
= sin(angle
);
2307 immutable Float m0
= mt
.ptr
[0], m2
= mt
.ptr
[2];
2308 immutable Float m4
= mt
.ptr
[4], m6
= mt
.ptr
[6];
2309 immutable Float m8
= mt
.ptr
[8], m10
= mt
.ptr
[10];
2310 immutable Float m12
= mt
.ptr
[12], m14
= mt
.ptr
[14];
2312 mt
.ptr
[0] = m0
*c
+m2
*s
;
2313 mt
.ptr
[2] = m0
*-s
+m2
*c
;
2314 mt
.ptr
[4] = m4
*c
+m6
*s
;
2315 mt
.ptr
[6] = m4
*-s
+m6
*c
;
2316 mt
.ptr
[8] = m8
*c
+m10
*s
;
2317 mt
.ptr
[10]= m8
*-s
+m10
*c
;
2318 mt
.ptr
[12]= m12
*c
+m14
*s
;
2319 mt
.ptr
[14]= m12
*-s
+m14
*c
;
2324 ref mat4
rotateZ() (Float angle
) {
2325 mixin(SinCosImportMixin
);
2326 angle
= deg2rad(angle
);
2327 immutable Float c
= cos(angle
);
2328 immutable Float s
= sin(angle
);
2329 immutable Float m0
= mt
.ptr
[0], m1
= mt
.ptr
[1];
2330 immutable Float m4
= mt
.ptr
[4], m5
= mt
.ptr
[5];
2331 immutable Float m8
= mt
.ptr
[8], m9
= mt
.ptr
[9];
2332 immutable Float m12
= mt
.ptr
[12], m13
= mt
.ptr
[13];
2334 mt
.ptr
[0] = m0
*c
+m1
*-s
;
2335 mt
.ptr
[1] = m0
*s
+m1
*c
;
2336 mt
.ptr
[4] = m4
*c
+m5
*-s
;
2337 mt
.ptr
[5] = m4
*s
+m5
*c
;
2338 mt
.ptr
[8] = m8
*c
+m9
*-s
;
2339 mt
.ptr
[9] = m8
*s
+m9
*c
;
2340 mt
.ptr
[12]= m12
*c
+m13
*-s
;
2341 mt
.ptr
[13]= m12
*s
+m13
*c
;
2347 ref mat4
translate() (in auto ref vec3 v
) {
2348 mt
.ptr
[0] += mt
.ptr
[3]*v
.x
; mt
.ptr
[4] += mt
.ptr
[7]*v
.x
; mt
.ptr
[8] += mt
.ptr
[11]*v
.x
; mt
.ptr
[12] += mt
.ptr
[15]*v
.x
;
2349 mt
.ptr
[1] += mt
.ptr
[3]*v
.y
; mt
.ptr
[5] += mt
.ptr
[7]*v
.y
; mt
.ptr
[9] += mt
.ptr
[11]*v
.y
; mt
.ptr
[13] += mt
.ptr
[15]*v
.y
;
2350 mt
.ptr
[2] += mt
.ptr
[3]*v
.z
; mt
.ptr
[6] += mt
.ptr
[7]*v
.z
; mt
.ptr
[10] += mt
.ptr
[11]*v
.z
; mt
.ptr
[14] += mt
.ptr
[15]*v
.z
;
2354 ref mat4
translateNeg() (in auto ref vec3 v
) {
2355 mt
.ptr
[0] -= mt
.ptr
[3]*v
.x
; mt
.ptr
[4] -= mt
.ptr
[7]*v
.x
; mt
.ptr
[8] -= mt
.ptr
[11]*v
.x
; mt
.ptr
[12] -= mt
.ptr
[15]*v
.x
;
2356 mt
.ptr
[1] -= mt
.ptr
[3]*v
.y
; mt
.ptr
[5] -= mt
.ptr
[7]*v
.y
; mt
.ptr
[9] -= mt
.ptr
[11]*v
.y
; mt
.ptr
[13] -= mt
.ptr
[15]*v
.y
;
2357 mt
.ptr
[2] -= mt
.ptr
[3]*v
.z
; mt
.ptr
[6] -= mt
.ptr
[7]*v
.z
; mt
.ptr
[10] -= mt
.ptr
[11]*v
.z
; mt
.ptr
[14] -= mt
.ptr
[15]*v
.z
;
2362 ref mat4 translate() (in auto ref vec3 v) {
2369 ref mat4 translateNeg() (in auto ref vec3 v) {
2378 ref mat4
scale() (in auto ref vec3 v
) {
2379 mt
.ptr
[0] *= v
.x
; mt
.ptr
[4] *= v
.x
; mt
.ptr
[8] *= v
.x
; mt
.ptr
[12] *= v
.x
;
2380 mt
.ptr
[1] *= v
.y
; mt
.ptr
[5] *= v
.y
; mt
.ptr
[9] *= v
.y
; mt
.ptr
[13] *= v
.y
;
2381 mt
.ptr
[2] *= v
.z
; mt
.ptr
[6] *= v
.z
; mt
.ptr
[10] *= v
.z
; mt
.ptr
[14] *= v
.z
;
2385 mat4
rotated() (Float angle
, in auto ref vec3 axis
) const { pragma(inline
, true); auto res
= mat4(this); return res
.rotate(angle
, axis
); }
2386 mat4
rotatedX() (Float angle
) const { pragma(inline
, true); auto res
= mat4(this); return res
.rotateX(angle
); }
2387 mat4
rotatedY() (Float angle
) const { pragma(inline
, true); auto res
= mat4(this); return res
.rotateY(angle
); }
2388 mat4
rotatedZ() (Float angle
) const { pragma(inline
, true); auto res
= mat4(this); return res
.rotateZ(angle
); }
2389 mat4
translated() (in auto ref vec3 v
) const { pragma(inline
, true); auto res
= mat4(this); return res
.translate(v
); }
2390 mat4
translatedNeg() (in auto ref vec3 v
) const { pragma(inline
, true); auto res
= mat4(this); return res
.translateNeg(v
); }
2391 mat4
scaled() (in auto ref vec3 v
) const { pragma(inline
, true); auto res
= mat4(this); return res
.scale(v
); }
2393 // retrieve angles in degree from rotation matrix, M = Rx*Ry*Rz, in degrees
2394 // Rx: rotation about X-axis, pitch
2395 // Ry: rotation about Y-axis, yaw (heading)
2396 // Rz: rotation about Z-axis, roll
2397 vec3
getAnglesDeg () const {
2398 mixin(ImportCoreMath
!(Float
, "asin", "atan2"));
2399 Float pitch
= void, roll
= void;
2400 Float yaw
= rad2deg(asin(mt
.ptr
[8]));
2401 if (mt
.ptr
[10] < 0) {
2402 if (yaw
>= 0) yaw
= 180-yaw
; else yaw
= -180-yaw
;
2404 if (mt
.ptr
[0] > -EPSILON
!Float
&& mt
.ptr
[0] < EPSILON
!Float
) {
2406 pitch
= rad2deg(atan2(mt
.ptr
[1], mt
.ptr
[5]));
2408 roll
= rad2deg(atan2(-mt
.ptr
[4], mt
.ptr
[0]));
2409 pitch
= rad2deg(atan2(-mt
.ptr
[9], mt
.ptr
[10]));
2411 return vec3(pitch
, yaw
, roll
);
2414 // retrieve angles in degree from rotation matrix, M = Rx*Ry*Rz, in radians
2415 // Rx: rotation about X-axis, pitch
2416 // Ry: rotation about Y-axis, yaw (heading)
2417 // Rz: rotation about Z-axis, roll
2418 vec3
getAngles () const {
2419 mixin(ImportCoreMath
!(Float
, "asin", "atan2"));
2420 Float pitch
= void, roll
= void;
2421 Float yaw
= asin(mt
.ptr
[8]);
2422 if (mt
.ptr
[10] < 0) {
2423 if (yaw
>= 0) yaw
= 180-yaw
; else yaw
= -180-yaw
;
2425 if (mt
.ptr
[0] > -EPSILON
!Float
&& mt
.ptr
[0] < EPSILON
!Float
) {
2427 pitch
= atan2(mt
.ptr
[1], mt
.ptr
[5]);
2429 roll
= atan2(-mt
.ptr
[4], mt
.ptr
[0]);
2430 pitch
= atan2(-mt
.ptr
[9], mt
.ptr
[10]);
2432 return vec3(pitch
, yaw
, roll
);
2435 vec3
opBinary(string op
:"*") (in auto ref vec3 v
) const {
2436 //pragma(inline, true);
2438 mt
.ptr
[0*4+0]*v
.x
+mt
.ptr
[1*4+0]*v
.y
+mt
.ptr
[2*4+0]*v
.z
+mt
.ptr
[3*4+0],
2439 mt
.ptr
[0*4+1]*v
.x
+mt
.ptr
[1*4+1]*v
.y
+mt
.ptr
[2*4+1]*v
.z
+mt
.ptr
[3*4+1],
2440 mt
.ptr
[0*4+2]*v
.x
+mt
.ptr
[1*4+2]*v
.y
+mt
.ptr
[2*4+2]*v
.z
+mt
.ptr
[3*4+2]);
2443 vec3
opBinaryRight(string op
:"*") (in auto ref vec3 v
) const {
2444 //pragma(inline, true);
2446 mt
.ptr
[0*4+0]*v
.x
+mt
.ptr
[0*4+1]*v
.y
+mt
.ptr
[0*4+2]*v
.z
+mt
.ptr
[0*4+3],
2447 mt
.ptr
[1*4+0]*v
.x
+mt
.ptr
[1*4+1]*v
.y
+mt
.ptr
[1*4+2]*v
.z
+mt
.ptr
[1*4+3],
2448 mt
.ptr
[2*4+0]*v
.x
+mt
.ptr
[2*4+1]*v
.y
+mt
.ptr
[2*4+2]*v
.z
+mt
.ptr
[2*4+3]);
2451 mat4
opBinary(string op
:"*") (in auto ref mat4 m
) const {
2452 //pragma(inline, true);
2454 mt
.ptr
[0]*m
.mt
.ptr
[0] +mt
.ptr
[4]*m
.mt
.ptr
[1] +mt
.ptr
[8]*m
.mt
.ptr
[2] +mt
.ptr
[12]*m
.mt
.ptr
[3], mt
.ptr
[1]*m
.mt
.ptr
[0] +mt
.ptr
[5]*m
.mt
.ptr
[1] +mt
.ptr
[9]*m
.mt
.ptr
[2] +mt
.ptr
[13]*m
.mt
.ptr
[3], mt
.ptr
[2]*m
.mt
.ptr
[0] +mt
.ptr
[6]*m
.mt
.ptr
[1] +mt
.ptr
[10]*m
.mt
.ptr
[2] +mt
.ptr
[14]*m
.mt
.ptr
[3], mt
.ptr
[3]*m
.mt
.ptr
[0] +mt
.ptr
[7]*m
.mt
.ptr
[1] +mt
.ptr
[11]*m
.mt
.ptr
[2] +mt
.ptr
[15]*m
.mt
.ptr
[3],
2455 mt
.ptr
[0]*m
.mt
.ptr
[4] +mt
.ptr
[4]*m
.mt
.ptr
[5] +mt
.ptr
[8]*m
.mt
.ptr
[6] +mt
.ptr
[12]*m
.mt
.ptr
[7], mt
.ptr
[1]*m
.mt
.ptr
[4] +mt
.ptr
[5]*m
.mt
.ptr
[5] +mt
.ptr
[9]*m
.mt
.ptr
[6] +mt
.ptr
[13]*m
.mt
.ptr
[7], mt
.ptr
[2]*m
.mt
.ptr
[4] +mt
.ptr
[6]*m
.mt
.ptr
[5] +mt
.ptr
[10]*m
.mt
.ptr
[6] +mt
.ptr
[14]*m
.mt
.ptr
[7], mt
.ptr
[3]*m
.mt
.ptr
[4] +mt
.ptr
[7]*m
.mt
.ptr
[5] +mt
.ptr
[11]*m
.mt
.ptr
[6] +mt
.ptr
[15]*m
.mt
.ptr
[7],
2456 mt
.ptr
[0]*m
.mt
.ptr
[8] +mt
.ptr
[4]*m
.mt
.ptr
[9] +mt
.ptr
[8]*m
.mt
.ptr
[10]+mt
.ptr
[12]*m
.mt
.ptr
[11],mt
.ptr
[1]*m
.mt
.ptr
[8] +mt
.ptr
[5]*m
.mt
.ptr
[9] +mt
.ptr
[9]*m
.mt
.ptr
[10]+mt
.ptr
[13]*m
.mt
.ptr
[11],mt
.ptr
[2]*m
.mt
.ptr
[8] +mt
.ptr
[6]*m
.mt
.ptr
[9] +mt
.ptr
[10]*m
.mt
.ptr
[10]+mt
.ptr
[14]*m
.mt
.ptr
[11],mt
.ptr
[3]*m
.mt
.ptr
[8] +mt
.ptr
[7]*m
.mt
.ptr
[9] +mt
.ptr
[11]*m
.mt
.ptr
[10]+mt
.ptr
[15]*m
.mt
.ptr
[11],
2457 mt
.ptr
[0]*m
.mt
.ptr
[12]+mt
.ptr
[4]*m
.mt
.ptr
[13]+mt
.ptr
[8]*m
.mt
.ptr
[14]+mt
.ptr
[12]*m
.mt
.ptr
[15],mt
.ptr
[1]*m
.mt
.ptr
[12]+mt
.ptr
[5]*m
.mt
.ptr
[13]+mt
.ptr
[9]*m
.mt
.ptr
[14]+mt
.ptr
[13]*m
.mt
.ptr
[15],mt
.ptr
[2]*m
.mt
.ptr
[12]+mt
.ptr
[6]*m
.mt
.ptr
[13]+mt
.ptr
[10]*m
.mt
.ptr
[14]+mt
.ptr
[14]*m
.mt
.ptr
[15],mt
.ptr
[3]*m
.mt
.ptr
[12]+mt
.ptr
[7]*m
.mt
.ptr
[13]+mt
.ptr
[11]*m
.mt
.ptr
[14]+mt
.ptr
[15]*m
.mt
.ptr
[15],
2461 mat4
opBinary(string op
:"+") (in auto ref mat4 m
) const {
2462 auto res
= mat4(this);
2467 mat4
opBinary(string op
:"*") (Float a
) const {
2468 auto res
= mat4(this);
2473 mat4
opBinary(string op
:"/") (Float a
) const {
2474 mixin(ImportCoreMath
!(Float
, "fabs"));
2475 auto res
= mat4(this);
2476 if (fabs(a
) >= FLTEPS
) {
2485 ref vec2
opOpAssign(string op
:"*") (in auto ref mat4 m
) {
2487 foreach (immutable i
; 0..4) {
2488 foreach (immutable j
; 0..4) {
2489 foreach (immutable k
; 0..4) {
2490 res
.mt
.ptr
[i
+j
*4] += mt
.ptr
[i
+k
*4]*m
.mt
.ptr
[k
+j
*4];
2498 mat4
transposed () const {
2501 foreach (immutable i; 0..4) {
2502 foreach (immutable j; 0..4) {
2503 res.mt.ptr[i+j*4] = mt.ptr[j+i*4];
2509 mt
.ptr
[0], mt
.ptr
[4], mt
.ptr
[8], mt
.ptr
[12],
2510 mt
.ptr
[1], mt
.ptr
[5], mt
.ptr
[9], mt
.ptr
[13],
2511 mt
.ptr
[2], mt
.ptr
[6], mt
.ptr
[10], mt
.ptr
[14],
2512 mt
.ptr
[3], mt
.ptr
[7], mt
.ptr
[11], mt
.ptr
[15],
2516 void negate () { foreach (ref v
; mt
) v
= -v
; }
2518 mat4
opUnary(string op
:"-") () const { pragma(inline
, true); return this; }
2520 mat4
opUnary(string op
:"-") () const {
2522 -mt
.ptr
[0], -mt
.ptr
[1], -mt
.ptr
[2], -mt
.ptr
[3],
2523 -mt
.ptr
[4], -mt
.ptr
[5], -mt
.ptr
[6], -mt
.ptr
[7],
2524 -mt
.ptr
[8], -mt
.ptr
[9], -mt
.ptr
[10], -mt
.ptr
[11],
2525 -mt
.ptr
[12], -mt
.ptr
[13], -mt
.ptr
[14], -mt
.ptr
[15],
2529 // blends two matrices together, at a given percentage (range is [0..1]), blend==0: m2 is ignored
2530 // WARNING! won't sanitize `blend`
2531 mat4
blended() (in auto ref mat4 m2
, Float blend
) const {
2532 immutable Float ib
= cast(Float
)1-blend
;
2534 res
.mt
.ptr
[0] = mt
.ptr
[0]*ib
+m2
.mt
.ptr
[0]*blend
;
2535 res
.mt
.ptr
[1] = mt
.ptr
[1]*ib
+m2
.mt
.ptr
[1]*blend
;
2536 res
.mt
.ptr
[2] = mt
.ptr
[2]*ib
+m2
.mt
.ptr
[2]*blend
;
2537 res
.mt
.ptr
[3] = mt
.ptr
[3]*ib
+m2
.mt
.ptr
[3]*blend
;
2538 res
.mt
.ptr
[4] = mt
.ptr
[4]*ib
+m2
.mt
.ptr
[4]*blend
;
2539 res
.mt
.ptr
[5] = mt
.ptr
[5]*ib
+m2
.mt
.ptr
[5]*blend
;
2540 res
.mt
.ptr
[6] = mt
.ptr
[6]*ib
+m2
.mt
.ptr
[6]*blend
;
2541 res
.mt
.ptr
[7] = mt
.ptr
[7]*ib
+m2
.mt
.ptr
[7]*blend
;
2542 res
.mt
.ptr
[8] = mt
.ptr
[8]*ib
+m2
.mt
.ptr
[8]*blend
;
2543 res
.mt
.ptr
[9] = mt
.ptr
[9]*ib
+m2
.mt
.ptr
[9]*blend
;
2544 res
.mt
.ptr
[10] = mt
.ptr
[10]*ib
+m2
.mt
.ptr
[10]*blend
;
2545 res
.mt
.ptr
[11] = mt
.ptr
[11]*ib
+m2
.mt
.ptr
[11]*blend
;
2546 res
.mt
.ptr
[12] = mt
.ptr
[12]*ib
+m2
.mt
.ptr
[12]*blend
;
2547 res
.mt
.ptr
[13] = mt
.ptr
[13]*ib
+m2
.mt
.ptr
[13]*blend
;
2548 res
.mt
.ptr
[14] = mt
.ptr
[14]*ib
+m2
.mt
.ptr
[14]*blend
;
2549 res
.mt
.ptr
[15] = mt
.ptr
[15]*ib
+m2
.mt
.ptr
[15]*blend
;
2553 Float
determinant() () const {
2554 return mt
.ptr
[0]*getCofactor(mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[13], mt
.ptr
[14], mt
.ptr
[15])-
2555 mt
.ptr
[1]*getCofactor(mt
.ptr
[4], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[14], mt
.ptr
[15])+
2556 mt
.ptr
[2]*getCofactor(mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[15])-
2557 mt
.ptr
[3]*getCofactor(mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2560 //WARNING: this must be tested for row/col
2561 // partially ;-) taken from DarkPlaces
2562 // this assumes uniform scaling
2563 mat4
invertedSimple () const {
2564 // we only support uniform scaling, so assume the first row is enough
2565 // (note the lack of sqrt here, because we're trying to undo the scaling,
2566 // this means multiplying by the inverse scale twice - squaring it, which
2567 // makes the sqrt a waste of time)
2569 immutable Float scale
= cast(Float
)1/(mt
.ptr
[0*4+0]*mt
.ptr
[0*4+0]+mt
.ptr
[1*4+0]*mt
.ptr
[1*4+0]+mt
.ptr
[2*4+0]*mt
.ptr
[2*4+0]);
2571 mixin(ImportCoreMath
!(Float
, "sqrt"));
2572 Float scale
= cast(Float
)3/sqrt(
2573 mt
.ptr
[0*4+0]*mt
.ptr
[0*4+0]+mt
.ptr
[1*4+0]*mt
.ptr
[1*4+0]+mt
.ptr
[2*4+0]*mt
.ptr
[2*4+0]+
2574 mt
.ptr
[0*4+1]*mt
.ptr
[0*4+1]+mt
.ptr
[1*4+1]*mt
.ptr
[1*4+1]+mt
.ptr
[2*4+1]*mt
.ptr
[2*4+1]+
2575 mt
.ptr
[0*4+2]*mt
.ptr
[0*4+2]+mt
.ptr
[1*4+2]*mt
.ptr
[1*4+2]+mt
.ptr
[2*4+2]*mt
.ptr
[2*4+2]
2582 // invert the rotation by transposing and multiplying by the squared recipricol of the input matrix scale as described above
2583 res
.mt
.ptr
[0*4+0] = mt
.ptr
[0*4+0]*scale
;
2584 res
.mt
.ptr
[1*4+0] = mt
.ptr
[0*4+1]*scale
;
2585 res
.mt
.ptr
[2*4+0] = mt
.ptr
[0*4+2]*scale
;
2586 res
.mt
.ptr
[0*4+1] = mt
.ptr
[1*4+0]*scale
;
2587 res
.mt
.ptr
[1*4+1] = mt
.ptr
[1*4+1]*scale
;
2588 res
.mt
.ptr
[2*4+1] = mt
.ptr
[1*4+2]*scale
;
2589 res
.mt
.ptr
[0*4+2] = mt
.ptr
[2*4+0]*scale
;
2590 res
.mt
.ptr
[1*4+2] = mt
.ptr
[2*4+1]*scale
;
2591 res
.mt
.ptr
[2*4+2] = mt
.ptr
[2*4+2]*scale
;
2593 // invert the translate
2594 res
.mt
.ptr
[3*4+0] = -(mt
.ptr
[3*4+0]*res
.mt
.ptr
[0*4+0]+mt
.ptr
[3*4+1]*res
.mt
.ptr
[1*4+0]+mt
.ptr
[3*4+2]*res
.mt
.ptr
[2*4+0]);
2595 res
.mt
.ptr
[3*4+1] = -(mt
.ptr
[3*4+0]*res
.mt
.ptr
[0*4+1]+mt
.ptr
[3*4+1]*res
.mt
.ptr
[1*4+1]+mt
.ptr
[3*4+2]*res
.mt
.ptr
[2*4+1]);
2596 res
.mt
.ptr
[3*4+2] = -(mt
.ptr
[3*4+0]*res
.mt
.ptr
[0*4+2]+mt
.ptr
[3*4+1]*res
.mt
.ptr
[1*4+2]+mt
.ptr
[3*4+2]*res
.mt
.ptr
[2*4+2]);
2598 // don't know if there's anything worth doing here
2599 res
.mt
.ptr
[0*4+3] = cast(Float
)0;
2600 res
.mt
.ptr
[1*4+3] = cast(Float
)0;
2601 res
.mt
.ptr
[2*4+3] = cast(Float
)0;
2602 res
.mt
.ptr
[3*4+3] = cast(Float
)1;
2607 //FIXME: make this fast pasta!
2608 ref mat4
invertSimple () {
2609 mt
[] = invertedSimple().mt
[];
2613 // ////////////////////////////////////////////////////////////////////////////
2614 // compute the inverse of 4x4 Euclidean transformation matrix
2616 // Euclidean transformation is translation, rotation, and reflection.
2617 // With Euclidean transform, only the position and orientation of the object
2618 // will be changed. Euclidean transform does not change the shape of an object
2619 // (no scaling). Length and angle are reserved.
2621 // Use inverseAffine() if the matrix has scale and shear transformation.
2622 ref mat4
invertEuclidean() () {
2624 tmp
= mt
.ptr
[1]; mt
.ptr
[1] = mt
.ptr
[4]; mt
.ptr
[4] = tmp
;
2625 tmp
= mt
.ptr
[2]; mt
.ptr
[2] = mt
.ptr
[8]; mt
.ptr
[8] = tmp
;
2626 tmp
= mt
.ptr
[6]; mt
.ptr
[6] = mt
.ptr
[9]; mt
.ptr
[9] = tmp
;
2627 immutable Float x
= mt
.ptr
[12];
2628 immutable Float y
= mt
.ptr
[13];
2629 immutable Float z
= mt
.ptr
[14];
2630 mt
.ptr
[12] = -(mt
.ptr
[0]*x
+mt
.ptr
[4]*y
+mt
.ptr
[8]*z
);
2631 mt
.ptr
[13] = -(mt
.ptr
[1]*x
+mt
.ptr
[5]*y
+mt
.ptr
[9]*z
);
2632 mt
.ptr
[14] = -(mt
.ptr
[2]*x
+mt
.ptr
[6]*y
+mt
.ptr
[10]*z
);
2636 // ////////////////////////////////////////////////////////////////////////////
2637 // compute the inverse of a 4x4 affine transformation matrix
2639 // Affine transformations are generalizations of Euclidean transformations.
2640 // Affine transformation includes translation, rotation, reflection, scaling,
2641 // and shearing. Length and angle are NOT preserved.
2642 ref mat4
invertAffine() () {
2644 mixin(ImportCoreMath
!(Float
, "fabs"));
2645 // inverse 3x3 matrix
2646 Float
[9] r
= void; //[ mt.ptr[0],mt.ptr[1],mt.ptr[2], mt.ptr[4],mt.ptr[5],mt.ptr[6], mt.ptr[8],mt.ptr[9],mt.ptr[10] ];
2647 r
.ptr
[0] = mt
.ptr
[0];
2648 r
.ptr
[1] = mt
.ptr
[1];
2649 r
.ptr
[2] = mt
.ptr
[2];
2650 r
.ptr
[3] = mt
.ptr
[4];
2651 r
.ptr
[4] = mt
.ptr
[5];
2652 r
.ptr
[5] = mt
.ptr
[6];
2653 r
.ptr
[6] = mt
.ptr
[8];
2654 r
.ptr
[7] = mt
.ptr
[9];
2655 r
.ptr
[8] = mt
.ptr
[10];
2657 Float
[9] tmp
= void;
2658 tmp
.ptr
[0] = r
.ptr
[4]*r
.ptr
[8]-r
.ptr
[5]*r
.ptr
[7];
2659 tmp
.ptr
[1] = r
.ptr
[2]*r
.ptr
[7]-r
.ptr
[1]*r
.ptr
[8];
2660 tmp
.ptr
[2] = r
.ptr
[1]*r
.ptr
[5]-r
.ptr
[2]*r
.ptr
[4];
2661 tmp
.ptr
[3] = r
.ptr
[5]*r
.ptr
[6]-r
.ptr
[3]*r
.ptr
[8];
2662 tmp
.ptr
[4] = r
.ptr
[0]*r
.ptr
[8]-r
.ptr
[2]*r
.ptr
[6];
2663 tmp
.ptr
[5] = r
.ptr
[2]*r
.ptr
[3]-r
.ptr
[0]*r
.ptr
[5];
2664 tmp
.ptr
[6] = r
.ptr
[3]*r
.ptr
[7]-r
.ptr
[4]*r
.ptr
[6];
2665 tmp
.ptr
[7] = r
.ptr
[1]*r
.ptr
[6]-r
.ptr
[0]*r
.ptr
[7];
2666 tmp
.ptr
[8] = r
.ptr
[0]*r
.ptr
[4]-r
.ptr
[1]*r
.ptr
[3];
2667 // check determinant if it is 0
2668 immutable Float determinant
= r
.ptr
[0]*tmp
.ptr
[0]+r
.ptr
[1]*tmp
.ptr
[3]+r
.ptr
[2]*tmp
.ptr
[6];
2669 if (fabs(determinant
) <= EPSILON
!Float
) {
2670 // cannot inverse, make it idenety matrix
2672 r
.ptr
[0] = r
.ptr
[4] = r
.ptr
[8] = 1;
2674 // divide by the determinant
2675 immutable Float invDeterminant
= cast(Float
)1/determinant
;
2676 r
.ptr
[0] = invDeterminant
*tmp
.ptr
[0];
2677 r
.ptr
[1] = invDeterminant
*tmp
.ptr
[1];
2678 r
.ptr
[2] = invDeterminant
*tmp
.ptr
[2];
2679 r
.ptr
[3] = invDeterminant
*tmp
.ptr
[3];
2680 r
.ptr
[4] = invDeterminant
*tmp
.ptr
[4];
2681 r
.ptr
[5] = invDeterminant
*tmp
.ptr
[5];
2682 r
.ptr
[6] = invDeterminant
*tmp
.ptr
[6];
2683 r
.ptr
[7] = invDeterminant
*tmp
.ptr
[7];
2684 r
.ptr
[8] = invDeterminant
*tmp
.ptr
[8];
2688 mt
.ptr
[0] = r
.ptr
[0]; mt
.ptr
[1] = r
.ptr
[1]; mt
.ptr
[2] = r
.ptr
[2];
2689 mt
.ptr
[4] = r
.ptr
[3]; mt
.ptr
[5] = r
.ptr
[4]; mt
.ptr
[6] = r
.ptr
[5];
2690 mt
.ptr
[8] = r
.ptr
[6]; mt
.ptr
[9] = r
.ptr
[7]; mt
.ptr
[10]= r
.ptr
[8];
2693 immutable Float x
= mt
.ptr
[12];
2694 immutable Float y
= mt
.ptr
[13];
2695 immutable Float z
= mt
.ptr
[14];
2696 mt
.ptr
[12] = -(r
.ptr
[0]*x
+r
.ptr
[3]*y
+r
.ptr
[6]*z
);
2697 mt
.ptr
[13] = -(r
.ptr
[1]*x
+r
.ptr
[4]*y
+r
.ptr
[7]*z
);
2698 mt
.ptr
[14] = -(r
.ptr
[2]*x
+r
.ptr
[5]*y
+r
.ptr
[8]*z
);
2700 // last row should be unchanged (0,0,0,1)
2701 //mt.ptr[3] = mt.ptr[7] = mt.ptr[11] = 0.0f;
2702 //mt.ptr[15] = 1.0f;
2707 ref mat4
invert() () {
2708 // if the 4th row is [0,0,0,1] then it is affine matrix and
2709 // it has no projective transformation
2710 if (mt
.ptr
[3] == 0 && mt
.ptr
[7] == 0 && mt
.ptr
[11] == 0 && mt
.ptr
[15] == 1) {
2711 return invertedAffine();
2713 return invertedGeneral();
2717 ///////////////////////////////////////////////////////////////////////////////
2718 // compute the inverse of a general 4x4 matrix using Cramer's Rule
2719 // if cannot find inverse, return indentity matrix
2720 ref mat4
invertGeneral() () {
2721 mixin(ImportCoreMath
!(Float
, "fabs"));
2723 immutable Float cofactor0
= getCofactor(mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[13], mt
.ptr
[14], mt
.ptr
[15]);
2724 immutable Float cofactor1
= getCofactor(mt
.ptr
[4], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[14], mt
.ptr
[15]);
2725 immutable Float cofactor2
= getCofactor(mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[15]);
2726 immutable Float cofactor3
= getCofactor(mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2728 immutable Float determinant
= mt
.ptr
[0]*cofactor0
-mt
.ptr
[1]*cofactor1
+mt
.ptr
[2]*cofactor2
-mt
.ptr
[3]*cofactor3
;
2729 if (fabs(determinant
) <= EPSILON
!Float
) { this = Identity
; return this; }
2731 immutable Float cofactor4
= getCofactor(mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[13], mt
.ptr
[14], mt
.ptr
[15]);
2732 immutable Float cofactor5
= getCofactor(mt
.ptr
[0], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[8], mt
.ptr
[10], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[14], mt
.ptr
[15]);
2733 immutable Float cofactor6
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[3], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[11], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[15]);
2734 immutable Float cofactor7
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2736 immutable Float cofactor8
= getCofactor(mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[13], mt
.ptr
[14], mt
.ptr
[15]);
2737 immutable Float cofactor9
= getCofactor(mt
.ptr
[0], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[4], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[12], mt
.ptr
[14], mt
.ptr
[15]);
2738 immutable Float cofactor10
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[3], mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[7], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[15]);
2739 immutable Float cofactor11
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[12], mt
.ptr
[13], mt
.ptr
[14]);
2741 immutable Float cofactor12
= getCofactor(mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[9], mt
.ptr
[10], mt
.ptr
[11]);
2742 immutable Float cofactor13
= getCofactor(mt
.ptr
[0], mt
.ptr
[2], mt
.ptr
[3], mt
.ptr
[4], mt
.ptr
[6], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[10], mt
.ptr
[11]);
2743 immutable Float cofactor14
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[3], mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[7], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[11]);
2744 immutable Float cofactor15
= getCofactor(mt
.ptr
[0], mt
.ptr
[1], mt
.ptr
[2], mt
.ptr
[4], mt
.ptr
[5], mt
.ptr
[6], mt
.ptr
[8], mt
.ptr
[9], mt
.ptr
[10]);
2746 immutable Float invDeterminant
= cast(Float
)1/determinant
;
2747 mt
.ptr
[0] = invDeterminant
*cofactor0
;
2748 mt
.ptr
[1] = -invDeterminant
*cofactor4
;
2749 mt
.ptr
[2] = invDeterminant
*cofactor8
;
2750 mt
.ptr
[3] = -invDeterminant
*cofactor12
;
2752 mt
.ptr
[4] = -invDeterminant
*cofactor1
;
2753 mt
.ptr
[5] = invDeterminant
*cofactor5
;
2754 mt
.ptr
[6] = -invDeterminant
*cofactor9
;
2755 mt
.ptr
[7] = invDeterminant
*cofactor13
;
2757 mt
.ptr
[8] = invDeterminant
*cofactor2
;
2758 mt
.ptr
[9] = -invDeterminant
*cofactor6
;
2759 mt
.ptr
[10]= invDeterminant
*cofactor10
;
2760 mt
.ptr
[11]= -invDeterminant
*cofactor14
;
2762 mt
.ptr
[12]= -invDeterminant
*cofactor3
;
2763 mt
.ptr
[13]= invDeterminant
*cofactor7
;
2764 mt
.ptr
[14]= -invDeterminant
*cofactor11
;
2765 mt
.ptr
[15]= invDeterminant
*cofactor15
;
2770 // compute cofactor of 3x3 minor matrix without sign
2771 // input params are 9 elements of the minor matrix
2772 // NOTE: The caller must know its sign
2773 private static Float
getCofactor() (Float m0
, Float m1
, Float m2
, Float m3
, Float m4
, Float m5
, Float m6
, Float m7
, Float m8
) {
2774 pragma(inline
, true);
2775 return m0
*(m4
*m8
-m5
*m7
)-m1
*(m3
*m8
-m5
*m6
)+m2
*(m3
*m7
-m4
*m6
);
2778 Quat4
!VT
toQuaternion () const nothrow @trusted @nogc {
2779 mixin(ImportCoreMath
!(Float
, "sqrt"));
2780 Quat4
!VT res
= void;
2781 immutable Float tr
= mt
.ptr
[0*4+0]+mt
.ptr
[1*4+1]+mt
.ptr
[2*4+2];
2782 // check the diagonal
2784 Float s
= sqrt(tr
+cast(Float
)1);
2785 res
.w
= s
/cast(Float
)2;
2786 s
= cast(Float
)0.5/s
;
2787 res
.x
= (mt
.ptr
[2*4+1]-mt
.ptr
[1*4+2])*s
;
2788 res
.y
= (mt
.ptr
[0*4+2]-mt
.ptr
[2*4+0])*s
;
2789 res
.z
= (mt
.ptr
[1*4+0]-mt
.ptr
[0*4+1])*s
;
2791 // diagonal is negative
2792 int[3] nxt
= [1, 2, 0];
2794 if (mt
.ptr
[1*4+1] > mt
.ptr
[0*4+0]) i
= 1;
2795 if (mt
.ptr
[2*4+2] > mt
.ptr
[i
*4+i
]) i
= 2;
2798 Float s
= sqrt((mt
.ptr
[i
*4+i
]-(mt
.ptr
[j
*4+j
]+mt
.ptr
[k
*4+k
]))+cast(Float
)1);
2800 q
.ptr
[i
] = s
*cast(Float
)0.5;
2801 if (s
!= 0) s
= cast(Float
)0.5/s
;
2802 q
.ptr
[3] = (mt
.ptr
[k
*4+j
]-mt
.ptr
[j
*4+k
])*s
;
2803 q
.ptr
[j
] = (mt
.ptr
[j
*4+i
]+mt
.ptr
[i
*4+j
])*s
;
2804 q
.ptr
[k
] = (mt
.ptr
[k
*4+i
]+mt
.ptr
[i
*4+k
])*s
;
2815 // ////////////////////////////////////////////////////////////////////////// //
2816 alias quat4
= Quat4
!vec3
;
2818 align(1) struct Quat4(VT
) if (IsVector
!VT
) {
2821 alias Float
= VT
.Float
;
2822 alias quat4
= typeof(this);
2825 Float w
=1, x
=0, y
=0, z
=0; // default: identity
2828 this (Float aw
, Float ax
, Float ay
, Float az
) nothrow @trusted @nogc {
2829 pragma(inline
, true);
2836 // valid only for unit quaternions
2837 this (Float ax
, Float ay
, Float az
) nothrow @trusted @nogc {
2838 immutable Float t
= cast(Float
)1-(ax
*ax
)-(ay
*ay
)-(az
*az
);
2842 mixin(ImportCoreMath
!(Float
, "sqrt"));
2850 this() (in auto ref VT v
) nothrow @safe @nogc {
2851 pragma(inline
, true);
2858 // the rotation of a point by a quaternion is given by the formula:
2860 // where R is the resultant quaternion, Q is the orientation quaternion by which you want to perform a rotation,
2861 // Q* the conjugate of Q and P is the point converted to a quaternion.
2862 // note: here the "." is the multiplication operator.
2864 // to convert a 3D vector to a quaternion, copy the x, y and z components and set the w component to 0.
2865 // this is the same for quaternion to vector conversion: take the x, y and z components and forget the w.
2867 VT
asVector () const nothrow @safe @nogc { pragma(inline
, true); return VT(x
, y
, z
); }
2869 static quat4
Identity () nothrow @safe @nogc { pragma(inline
, true); return quat4(1, 0, 0, 0); }
2870 bool isIdentity () const nothrow @safe @nogc { pragma(inline
, true); return (w
== 1 && x
== 0 && y
== 0 && z
== 0); }
2872 static quat4
fromAngles (Float roll
, Float pitch
, Float yaw
) nothrow @trusted @nogc {
2873 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
2874 // calculate trig identities
2875 immutable Float cr
= cos(roll
/2);
2876 immutable Float cp
= cos(pitch
/2);
2877 immutable Float cy
= cos(yaw
/2);
2878 immutable Float sr
= sin(roll
/2);
2879 immutable Float sp
= sin(pitch
/2);
2880 immutable Float sy
= sin(yaw
/2);
2881 immutable Float cpcy
= cp
*cy
;
2882 immutable Float spsy
= sp
*sy
;
2891 //@property bool valid () const nothrow @safe @nogc { pragma(inline, true); import core.stdc.math : isnan; return !isnan(w) && !isnan(x) && !isnan(y) && !isnan(z); }
2892 @property bool valid () const nothrow @safe @nogc { pragma(inline
, true); import core
.stdc
.math
: isfinite
; return isfinite(w
) && isfinite(x
) && isfinite(y
) && isfinite(z
); }
2895 Float
opIndex (usize idx
) const nothrow @safe @nogc {
2896 pragma(inline
, true);
2906 void opIndexAssign (Float v
, usize idx
) {
2907 pragma(inline
, true);
2908 if (idx
== 0) x
= v
; else
2909 if (idx
== 1) y
= v
; else
2910 if (idx
== 2) z
= v
; else
2911 if (idx
== 3) w
= v
; else
2912 assert(0, "invalid quaternion index");
2915 Mat4
!VT
toMatrix () const nothrow @trusted @nogc {
2916 // calculate coefficients
2917 immutable Float x2
= this.x
+this.x
;
2918 immutable Float y2
= this.y
+this.y
;
2919 immutable Float z2
= this.z
+this.z
;
2920 immutable Float xx
= this.x
*x2
;
2921 immutable Float xy
= this.x
*y2
;
2922 immutable Float xz
= this.x
*z2
;
2923 immutable Float yy
= this.y
*y2
;
2924 immutable Float yz
= this.y
*z2
;
2925 immutable Float zz
= this.z
*z2
;
2926 immutable Float wx
= this.w
*x2
;
2927 immutable Float wy
= this.w
*y2
;
2928 immutable Float wz
= this.w
*z2
;
2931 res
.mt
.ptr
[0*4+0] = cast(Float
)1-(yy
+zz
);
2932 res
.mt
.ptr
[0*4+1] = xy
-wz
;
2933 res
.mt
.ptr
[0*4+2] = xz
+wy
;
2934 res
.mt
.ptr
[0*4+3] = 0;
2936 res
.mt
.ptr
[1*4+0] = xy
+wz
;
2937 res
.mt
.ptr
[1*4+1] = cast(Float
)1-(xx
+zz
);
2938 res
.mt
.ptr
[1*4+2] = yz
-wx
;
2939 res
.mt
.ptr
[1*4+3] = 0;
2941 res
.mt
.ptr
[2*4+0] = xz
-wy
;
2942 res
.mt
.ptr
[2*4+1] = yz
+wx
;
2943 res
.mt
.ptr
[2*4+2] = cast(Float
)1-(xx
+yy
);
2944 res
.mt
.ptr
[2*4+3] = 0;
2946 res
.mt
.ptr
[3*4+0] = 0;
2947 res
.mt
.ptr
[3*4+1] = 0;
2948 res
.mt
.ptr
[3*4+2] = 0;
2949 res
.mt
.ptr
[3*4+3] = 1;
2954 auto opUnary(string op
:"+") () const nothrow @safe @nogc { pragma(inline
, true); return this; }
2956 // for unit quaternions, this is inverse/conjugate
2957 auto opUnary(string op
:"-") () const nothrow @safe @nogc { pragma(inline
, true); return quat4(-w
, -x
, -y
, -z
); }
2959 quat4
opBinary(string op
:"*") (in auto ref quat4 q2
) const nothrow @safe @nogc {
2960 auto res
= quat4(this.w
, this.x
, this.y
, this.z
);
2964 ref quat4
opOpAssign(string op
:"*") (in auto ref quat4 q2
) nothrow @safe @nogc {
2965 immutable Float A
= (this.w
+this.x
)*(q2
.w
+q2
.x
);
2966 immutable Float B
= (this.z
-this.y
)*(q2
.y
-q2
.z
);
2967 immutable Float C
= (this.w
-this.x
)*(q2
.y
+q2
.z
);
2968 immutable Float D
= (this.y
+this.z
)*(q2
.w
-q2
.x
);
2969 immutable Float E
= (this.x
+this.z
)*(q2
.x
+q2
.y
);
2970 immutable Float F
= (this.x
-this.z
)*(q2
.x
-q2
.y
);
2971 immutable Float G
= (this.w
+this.y
)*(q2
.w
-q2
.z
);
2972 immutable Float H
= (this.w
-this.y
)*(q2
.w
+q2
.z
);
2973 this.w
= B
+(-E
-F
+G
+H
)/2;
2974 this.x
= A
-(E
+F
+G
+H
)/2;
2975 this.y
= C
+(E
-F
+G
-H
)/2;
2976 this.z
= D
+(E
-F
-G
+H
)/2;
2980 quat4
slerp() (in auto ref quat4 to
, Float t
) const nothrow @trusted @nogc {
2981 mixin(ImportCoreMath
!(Float
, "acos", "sin"));
2982 Float
[4] to1
= void;
2984 Float cosom
= this.x
*to
.x
+this.y
*to
.y
+this.z
*to
.z
+this.w
*to
.w
;
2985 // adjust signs (if necessary)
2998 Float scale0
= void, scale1
= void;
2999 // calculate coefficients
3000 if (cast(Float
)1-cosom
> EPSILON
!Float
) {
3001 // standard case (slerp)
3002 immutable Float omega
= acos(cosom
);
3003 immutable Float sinom
= sin(omega
);
3004 scale0
= sin((cast(Float
)1-t
)*omega
)/sinom
;
3005 scale1
= sin(t
*omega
)/sinom
;
3007 // "from" and "to" quaternions are very close, so we can do a linear interpolation
3008 scale0
= cast(Float
)1-t
;
3011 // calculate final values
3013 scale0
*this.w
+scale1
*to1
.ptr
[3],
3014 scale0
*this.x
+scale1
*to1
.ptr
[0],
3015 scale0
*this.y
+scale1
*to1
.ptr
[1],
3016 scale0
*this.z
+scale1
*to1
.ptr
[2],
3022 // ////////////////////////////////////////////////////////////////////////// //
3023 alias OBB2D
= OBB2d
!vec2
;
3025 /// Oriented Bounding Box
3026 struct OBB2d(VT
) if (IsVectorDim
!(VT
, 2)) {
3027 // to make the tests extremely efficient, `origin` stores the
3028 // projection of corner number zero onto a box's axes and the axes are stored
3029 // explicitly in axis. the magnitude of these stored axes is the inverse
3030 // of the corresponding edge length so that all overlap tests can be performed on
3031 // the interval [0, 1] without normalization, and square roots are avoided
3032 // throughout the entire test.
3034 alias Me
= typeof(this);
3035 alias Float
= VT
.Float
;
3039 VT
[4] corner
; // corners of the box, where 0 is the lower left
3040 bool aovalid
; // are axes and origin valid?
3041 VT
[2] axis
; // two edges of the box extended away from corner[0]
3042 VT
.Float
[2] origin
; // origin[a] = corner[0].dot(axis[a]);
3044 private nothrow @trusted @nogc:
3045 // returns true if other overlaps one dimension of this
3046 bool overlaps1Way() (in auto ref Me other
) const {
3047 foreach (immutable a
; 0..2) {
3048 Float t
= other
.corner
.ptr
[0].dot(axis
.ptr
[a
]);
3049 // find the extent of box 2 on axis a
3050 Float tMin
= t
, tMax
= t
;
3051 foreach (immutable c
; 1..4) {
3052 t
= other
.corner
.ptr
[c
].dot(axis
.ptr
[a
]);
3053 if (t
< tMin
) tMin
= t
; else if (t
> tMax
) tMax
= t
;
3055 // we have to subtract off the origin
3056 // see if [tMin, tMax] intersects [0, 1]
3057 if (tMin
> 1+origin
.ptr
[a
] || tMax
< origin
.ptr
[a
]) {
3058 // there was no intersection along this dimension; the boxes cannot possibly overlap
3062 // there was no dimension along which there is no intersection: therefore the boxes overlap
3066 // updates the axes after the corners move; assumes the corners actually form a rectangle
3067 void computeAxes () {
3068 axis
.ptr
[0] = corner
.ptr
[1]-corner
.ptr
[0];
3069 axis
.ptr
[1] = corner
.ptr
[3]-corner
.ptr
[0];
3070 // make the length of each axis 1/edge length so we know any dot product must be less than 1 to fall within the edge
3071 foreach (immutable a
; 0..2) {
3072 axis
.ptr
[a
] /= axis
.ptr
[a
].lengthSquared
;
3073 origin
.ptr
[a
] = corner
.ptr
[0].dot(axis
.ptr
[a
]);
3080 this() (in auto ref VT center
, in Float w
, in Float h
, in Float angle
) {
3081 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
3083 immutable Float ca
= cos(angle
);
3084 immutable Float sa
= sin(angle
);
3085 auto ox
= VT( ca
, sa
);
3086 auto oy
= VT(-sa
, ca
);
3091 corner
.ptr
[0] = center
-ox
-oy
;
3092 corner
.ptr
[1] = center
+ox
-oy
;
3093 corner
.ptr
[2] = center
+ox
+oy
;
3094 corner
.ptr
[3] = center
-ox
+oy
;
3096 // compute axes on demand
3100 VT
[4] corners () const { pragma(inline
, true); VT
[4] res
= corner
[]; return res
; }
3103 VT
opIndex (usize idx
) const {
3104 pragma(inline
, true);
3105 return (idx
< 4 ? corner
.ptr
[idx
] : VT(Float
.nan
, Float
.nan
, Float
.nan
));
3109 void moveTo() (in auto ref VT center
) {
3110 immutable centroid
= (corner
.ptr
[0]+corner
.ptr
[1]+corner
.ptr
[2]+corner
.ptr
[3])/4;
3111 immutable translation
= center
-centroid
;
3112 foreach (ref VT cv
; corner
[]) cv
+= translation
;
3113 aovalid
= false; // invalidate axes
3117 void moveBy() (in auto ref VT delta
) {
3118 foreach (ref VT cv
; corner
[]) cv
+= delta
;
3119 aovalid
= false; // invalidate axes
3122 /// rotate around centroid
3123 void rotate() (Float angle
) {
3124 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
3125 immutable centroid
= (corner
.ptr
[0]+corner
.ptr
[1]+corner
.ptr
[2]+corner
.ptr
[3])/4;
3126 immutable Float ca
= cos(angle
);
3127 immutable Float sa
= sin(angle
);
3128 foreach (ref cv
; corner
[]) {
3129 immutable Float ox
= cv
.x
-centroid
.x
;
3130 immutable Float oy
= cv
.y
-centroid
.y
;
3131 cv
.x
= ox
*ca
-oy
*sa
+centroid
.x
;
3132 cv
.y
= ox
*sa
+oy
*ca
+centroid
.y
;
3134 aovalid
= false; // invalidate axes
3138 void scale() (Float mult
) {
3139 immutable centroid
= (corner
.ptr
[0]+corner
.ptr
[1]+corner
.ptr
[2]+corner
.ptr
[3])/4;
3140 foreach (ref cv
; corner
[]) cv
= (cv
-centroid
)*mult
+centroid
;
3141 aovalid
= false; // invalidate axes
3144 /// apply transformation matrix, assuming that world (0,0) is at centroid
3145 void transform() (in auto ref Mat3
!VT mat
) {
3146 immutable centroid
= (corner
.ptr
[0]+corner
.ptr
[1]+corner
.ptr
[2]+corner
.ptr
[3])/4;
3147 foreach (ref VT cv
; corner
[]) cv
-= centroid
;
3148 foreach (ref VT cv
; corner
[]) cv
= cv
*mat
;
3149 foreach (ref VT cv
; corner
[]) cv
+= centroid
;
3150 aovalid
= false; // invalidate axes
3153 /// returns true if the intersection of the boxes is non-empty
3154 bool overlaps() (in auto ref Me other
) {
3155 pragma(inline
, true);
3156 if (!aovalid
) computeAxes(); // fix axes if necessary
3157 return (overlaps1Way(other
) && other
.overlaps1Way(this));
3162 // ////////////////////////////////////////////////////////////////////////// //
3163 template IsSphere(ST
) {
3164 static if (is(ST
== Sphere
!SVT
, SVT
)) {
3165 enum IsSphere
= IsVector
!SVT
;
3167 enum IsSphere
= false;
3171 template IsSphere(ST
, VT
) {
3172 static if (is(ST
== Sphere
!SVT
, SVT
)) {
3173 enum IsSphere
= (is(VT
== SVT
) && IsVector
!SVT
);
3175 enum IsSphere
= false;
3181 struct Sphere(VT
) if (IsVector
!VT
) {
3183 alias Float
= VT
.Float
;
3185 alias Me
= typeof(this);
3188 vec orig
; /// sphere origin
3189 Float radius
; /// sphere radius
3191 public nothrow @trusted @nogc:
3192 this() (in auto ref vec aorig
, Float arad
) { orig
= aorig
; radius
= arad
; }
3194 @property bool valid () const { import core
.stdc
.math
: isfinite
; pragma(inline
, true); return (isfinite(radius
) && radius
> 0); }
3197 bool sweep() (in auto ref vec amove
, in auto ref Me sb
, Float
* u0
, Float
* u1
) const {
3198 mixin(ImportCoreMath
!(Float
, "sqrt"));
3200 immutable odist
= sb
.orig
-this.orig
; // vector from A0 to B0
3201 immutable vab
= -amove
; // relative velocity (in normalized time)
3202 immutable rab
= this.radius
+sb
.radius
;
3203 immutable Float a
= vab
.dot(vab
); // u*u coefficient
3204 immutable Float b
= cast(Float
)2*vab
.dot(odist
); // u coefficient
3205 immutable Float c
= odist
.dot(odist
)-rab
*rab
; // constant term
3207 // check if they're currently overlapping
3208 if (odist
.dot(odist
) <= rab
*rab
) {
3209 if (u0
!is null) *u0
= 0;
3210 if (u0
!is null) *u1
= 0;
3214 // check if they hit each other during the frame
3215 immutable Float q
= b
*b
-4*a
*c
;
3216 if (q
< 0) return false; // alas, complex roots
3218 immutable Float sq
= sqrt(q
);
3219 immutable Float d
= cast(Float
)1/(cast(Float
)2*a
);
3220 Float uu0
= (-b
+sq
)*d
;
3221 Float uu1
= (-b
-sq
)*d
;
3223 if (uu0
> uu1
) { immutable t
= uu0
; uu0
= uu1
; uu1
= t
; } // swap
3224 if (u0
!is null) *u0
= uu0
;
3225 if (u1
!is null) *u1
= uu1
;
3230 // sweep test; if `true` (hit), `hitpos` will be sphere position when it hits this plane, and `u` will be normalized collision time
3231 bool sweep(PT
) (in auto ref PT plane
, in auto ref vec3 amove
, vec3
* hitpos
, Float
* u
) const if (IsPlane3
!(PT
, Float
)) {
3232 pragma(inline
, true);
3233 return plane
.sweep(this, amove
, hitpos
, u
);
3238 // ////////////////////////////////////////////////////////////////////////// //
3239 template IsPlane3(PT
) {
3240 static if (is(PT
== Plane3
!(PFT
, eps
, swiz
), PFT
, double eps
, bool swiz
)) {
3241 enum IsPlane3
= (is(PFT
== float) ||
is(PFT
== double));
3243 enum IsPlane3
= false;
3248 template IsPlane3(PT
, FT
) {
3249 static if (is(PT
== Plane3
!(PFT
, eps
, swiz
), PFT
, double eps
, bool swiz
)) {
3250 enum IsPlane3
= (is(FT
== PFT
) && (is(PFT
== float) ||
is(PFT
== double)));
3252 enum IsPlane3
= false;
3257 // plane in 3D space: Ax+By+Cz+D=0
3258 align(1) struct Plane3(FloatType
=VFloat
, double PlaneEps
=-1.0, bool enableSwizzling
=true) if (is(FloatType
== float) ||
is(FloatType
== double)) {
3261 alias Float
= FloatType
;
3262 alias plane3
= typeof(this);
3263 alias Me
= typeof(this);
3264 alias vec3
= VecN
!(3, Float
);
3265 static if (PlaneEps
< 0) {
3266 enum EPS
= EPSILON
!Float
;
3268 enum EPS
= cast(Float
)PlaneEps
;
3272 alias PType
= ubyte;
3281 //Float a = 0, b = 0, c = 0, d = 0;
3286 string
toString () const {
3287 import std
.string
: format
;
3289 return "(%s,%s,%s,%s)".format(normal
.x
, normal
.y
, normal
.z
, w
);
3290 } catch (Exception
) {
3296 this() (in auto ref vec3 aorigin
, in auto ref vec3 anormal
) { pragma(inline
, true); setOriginNormal(aorigin
, anormal
); }
3297 this() (in auto ref vec3 anormal
, Float aw
) { pragma(inline
, true); set(anormal
, aw
); }
3298 this() (in auto ref vec3 a
, in auto ref vec3 b
, in auto ref vec3 c
) { pragma(inline
, true); setFromPoints(a
, b
, c
); }
3300 @property Float
offset () const pure { pragma(inline
, true); return -w
; }
3302 void set () (in auto ref vec3 anormal
, Float aw
) {
3303 pragma(inline
, true);
3308 void setOriginNormal () (in auto ref vec3 aorigin
, in auto ref vec3 anormal
) {
3309 normal
= anormal
.normalized
;
3311 w
= normal
.x
*aorigin
.x
+normal
.y
*aorigin
.y
+normal
.z
*aorigin
.z
;
3314 void setFromPoints() (in auto ref vec3 a
, in auto ref vec3 b
, in auto ref vec3 c
) @trusted {
3315 //normal = ((b-a)^(c-a)).normalized;
3316 immutable vec3 b0
= b
-a
;
3317 immutable vec3 c0
= c
-a
;
3318 normal
= vec3(b0
.y
*c0
.z
-b0
.z
*c0
.y
, b0
.z
*c0
.x
-b0
.x
*c0
.z
, b0
.x
*c0
.y
-b0
.y
*c0
.x
);
3322 //w = normal*a; // n.dot(a)
3323 w
= normal
.x
*a
.x
+normal
.y
*a
.y
+normal
.z
*a
.z
;
3325 //normalize; // just in case, to tolerate some floating point errors
3328 immutable double len
= normal
.dbllength
;
3329 //{ import core.stdc.stdio; printf("**len=%g\n", len); }
3330 if (len
<= EPSILON
!Float
) {
3332 //{ import core.stdc.stdio; printf(" OOPS: n=(%g,%g,%g)\n", cast(double)normal.x, cast(double)normal.y, cast(double)normal.z); }
3333 normal
= vec3
.Invalid
;
3337 version(vmath_slow_normalize
) {
3342 immutable double dd = 1.0/len
;
3347 w
= normal
.x
*a
.x
+normal
.y
*a
.y
+normal
.z
*a
.z
;
3350 normalize; // just in case, to tolerate some floating point errors
3358 @property bool valid () const { pragma(inline
, true); import core
.stdc
.math
: isfinite
; return (isfinite(w
) != 0); }
3360 Float
opIndex (usize idx
) const {
3361 pragma(inline
, true);
3362 return (idx
== 0 ? normal
.x
: idx
== 1 ? normal
.y
: idx
== 2 ? normal
.z
: idx
== 3 ? w
: Float
.nan
);
3365 void opIndexAssign (Float v
, usize idx
) {
3366 pragma(inline
, true);
3367 if (idx
== 0) normal
.x
= v
; else if (idx
== 1) normal
.y
= v
; else if (idx
== 2) normal
.z
= v
; else if (idx
== 3) w
= v
;
3370 ref plane3
normalize () {
3371 if (!normal
.isFinite
) {
3372 normal
= vec3
.Invalid
;
3377 mixin(ImportCoreMath
!(Float
, "fabs"));
3378 double dd = normal
.dbllength
;
3379 if (fabs(1.0-dd) > EPSILON
!Float
) {
3380 version(vmath_slow_normalize
) {
3386 dd = cast(Float
)1/dd;
3394 mixin(ImportCoreMath
!(double, "sqrt"));
3395 double dd = sqrt(cast(double)normal
.x
*cast(double)normal
.x
+cast(double)normal
.y
*cast(double)normal
.y
+cast(double)normal
.z
*cast(double)normal
.z
);
3396 version(vmath_slow_normalize
) {
3412 //WARNING! won't check if this plane is valid
3414 pragma(inline
, true);
3419 //WARNING! won't check if this plane is valid
3420 plane3
fliped () const {
3421 pragma(inline
, true);
3422 return plane3(-normal
, -w
);
3425 PType
pointSide() (in auto ref vec3 p
) const {
3426 pragma(inline
, true);
3427 //immutable Float t = (normal*p)-w; // dot
3428 immutable double t
= cast(double)normal
.x
*cast(double)p
.x
+cast(double)normal
.y
*cast(double)p
.y
+cast(double)normal
.z
*cast(double)p
.z
-cast(double)w
;
3429 return (t
< -EPS ? Back
: (t
> EPS ? Front
: Coplanar
));
3432 double pointSideD() (in auto ref vec3 p
) const {
3433 pragma(inline
, true);
3434 //return (normal*p)-w; // dot
3435 return cast(double)normal
.x
*cast(double)p
.x
+cast(double)normal
.y
*cast(double)p
.y
+cast(double)normal
.z
*cast(double)p
.z
-cast(double)w
;
3438 Float
pointSideF() (in auto ref vec3 p
) const {
3439 pragma(inline
, true);
3440 //return (normal*p)-w; // dot
3441 return cast(Float
)(cast(double)normal
.x
*cast(double)p
.x
+cast(double)normal
.y
*cast(double)p
.y
+cast(double)normal
.z
*cast(double)p
.z
-cast(double)w
);
3444 // distance from point to plane
3445 // plane must be normalized
3446 double dbldistance() (in auto ref vec3 p
) const {
3447 pragma(inline
, true);
3448 return (cast(double)normal
.x
*p
.x
+cast(double)normal
.y
*p
.y
+cast(double)normal
.z
*cast(double)p
.z
)/normal
.dbllength
;
3451 // distance from point to plane
3452 // plane must be normalized
3453 Float
distance() (in auto ref vec3 p
) const {
3454 pragma(inline
, true);
3455 return cast(Float
)dbldistance
;
3458 // "land" point onto the plane
3459 // plane must be normalized
3460 vec3
landAlongNormal() (in auto ref vec3 p
) const {
3461 pragma(inline
, true);
3462 mixin(ImportCoreMath
!(Float
, "fabs"));
3463 immutable double pdist
= pointSideF(p
);
3464 return (fabs(pdist
) > EPSILON
!Float ? p
-normal
*pdist
: p
);
3467 // "land" point onto the plane
3468 // plane must be normalized
3470 vec3 land() (in auto ref vec3 p) const {
3471 mixin(ImportCoreMath!(double, "fabs"));
3472 // distance from point to plane
3473 double pdist = (cast(double)normal.x*p.x+cast(double)normal.y*p.y+cast(double)normal.z*cast(double)p.z)/normal.dbllength;
3474 // just-in-case check
3475 if (fabs(pdist) > EPSILON!double) {
3477 return p+normal*pdist;
3485 // plane must be normalized
3486 vec3
project() (in auto ref vec3 v
) const {
3487 pragma(inline
, true);
3488 mixin(ImportCoreMath
!(double, "fabs"));
3489 return v
-(v
-normal
*w
).dot(normal
)*normal
;
3492 // returns the point where the line p0-p1 intersects this plane
3493 vec3
lineIntersect() (in auto ref vec3 p0
, in auto ref vec3 p1
) const {
3494 pragma(inline
, true);
3495 immutable dif
= p1
-p0
;
3496 immutable t
= (w
-normal
.dot(p0
))/normal
.dot(dif
);
3501 static if (enableSwizzling
) auto opDispatch(string
fld) () if (isGoodSwizzling
!(fld, "xyzw", 2, 3)) {
3502 static if (fld.length
== 2) {
3503 return mixin(SwizzleCtor
!("vec2", fld));
3505 return mixin(SwizzleCtor
!("vec3", fld));
3509 // sweep test; if `true` (hit), `hitpos` will be sphere position when it hits this plane, and `u` will be normalized collision time
3510 bool sweep(ST
) (in auto ref ST sphere
, in auto ref vec3 amove
, vec3
* hitpos
, Float
* u
) const if (IsSphere
!(ST
, vec3
)) {
3511 mixin(ImportCoreMath
!(Float
, "fabs"));
3512 immutable c0
= sphere
.orig
;
3513 immutable c1
= c0
+amove
;
3514 immutable Float r
= sphere
.radius
;
3515 immutable Float d0
= (normal
*c0
)+w
;
3516 immutable Float d1
= (normal
*c1
)+w
;
3517 // check if the sphere is touching the plane
3518 if (fabs(d0
) <= r
) {
3519 if (hitpos
!is null) *hitpos
= c0
;
3520 if (u
!is null) *u
= 0;
3523 // check if the sphere penetrated during movement
3524 if (d0
> r
&& d1
< r
) {
3525 immutable Float uu
= (d0
-r
)/(d0
-d1
); // normalized time
3526 if (u
!is null) *u
= uu
;
3527 if (hitpos
!is null) *hitpos
= (1-uu
)*c0
+uu
*c1
; // point of first contact
3534 // intersection of 3 planes, Graphics Gems 1 pg 305
3535 auto intersectionPoint() (in auto ref plane3 plane2
, in auto ref plane3 plane3
) const {
3536 mixin(ImportCoreMath
!(Float
, "fabs"));
3537 alias plane1
= this;
3538 immutable Float det
= plane1
.normal
.cross(plane2
.normal
).dot(plane3
.normal
);
3539 // if the determinant is 0, that means parallel planes, no intersection
3540 if (fabs(det
) < EPSILON
!Float
) return vec3
.Invalid
;
3542 (plane2
.normal
.cross(plane3
.normal
)*(-plane1
.w
)+
3543 plane3
.normal
.cross(plane1
.normal
)*(-plane2
.w
)+
3544 plane1
.normal
.cross(plane2
.normal
)*(-plane3
.w
))/det
;
3549 // ////////////////////////////////////////////////////////////////////////// //
3550 struct Ray(VT
) if (IsVector
!VT
) {
3553 alias Float
= VT
.Float
;
3556 VT orig
, dir
; // dir should be normalized (setters does this)
3559 string
toString () const {
3560 import std
.format
: format
;
3562 return "[(%s,%s):(%s,%s)]".format(orig
.x
, orig
.y
, dir
.x
, dir
.y
);
3563 } catch (Exception
) {
3569 this (VT
.Float x
, VT
.Float y
, VT
.Float angle
) { pragma(inline
, true); setOrigDir(x
, y
, angle
); }
3570 this() (in auto ref VT aorg
, VT
.Float angle
) { pragma(inline
, true); setOrigDir(aorg
, angle
); }
3572 static Ray
!VT
fromPoints() (in auto ref VT p0
, in auto ref VT p1
) {
3573 pragma(inline
, true);
3576 res
.dir
= (p1
-p0
).normalized
;
3580 static if (VT
.Dims
== 2) void setOrigDir (VT
.Float x
, VT
.Float y
, VT
.Float angle
) {
3581 pragma(inline
, true);
3582 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
3589 static if (VT
.Dims
== 2) void setOrigDir() (in auto ref VT aorg
, VT
.Float angle
) {
3590 pragma(inline
, true);
3591 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
3598 static if (VT
.Dims
== 2) void setOrig (VT
.Float x
, VT
.Float y
) {
3599 pragma(inline
, true);
3604 void setOrig() (in auto ref VT aorg
) {
3605 pragma(inline
, true);
3608 static if (VT
.Dims
== 3) orig
.z
= aorg
.z
;
3611 static if (VT
.Dims
== 2) void setDir (VT
.Float angle
) {
3612 pragma(inline
, true);
3613 mixin(ImportCoreMath
!(Float
, "cos", "sin"));
3618 @property VT
right () const {
3619 pragma(inline
, true);
3620 static if (VT
.Dims
== 2) return VT(dir
.y
, -dir
.x
);
3621 else return VT(dir
.y
, -dir
.x
, dir
.z
);
3624 VT
pointAt (VT
.Float len
) const { pragma(inline
, true); return orig
+dir
*len
; }
3628 // ////////////////////////////////////////////////////////////////////////// //
3629 public struct AABBImpl(VT
) if (IsVector
!VT
) {
3631 static T
nmin(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
< b ? a
: b
); }
3632 static T
nmax(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
> b ? a
: b
); }
3636 alias Float
= VT
.Float
;
3637 alias Me
= typeof(this);
3641 //VT half; // should be positive
3645 string
toString () const {
3646 import std
.format
: format
;
3647 return "[%s-%s]".format(min
, max
);
3650 public nothrow @safe @nogc:
3651 this() (in auto ref VT amin
, in auto ref VT amax
) {
3652 pragma(inline
, true);
3653 //center = (amin+amax)/2;
3654 //half = VT((nmax(amin.x, amax.x)-nmin(amin.x, amax.x))/2, (nmax(amin.y, amax.y)-nmin(amin.y, amax.y))/2);
3656 // this breaks VecN ctor inliner (fuck!)
3657 static if (VT
.Dims
== 2) {
3658 min
= VT(nmin(amin
.x
, amax
.x
), nmin(amin
.y
, amax
.y
));
3659 max
= VT(nmax(amin
.x
, amax
.x
), nmax(amin
.y
, amax
.y
));
3661 min
= VT(nmin(amin
.x
, amax
.x
), nmin(amin
.y
, amax
.y
), nmin(amin
.z
, amax
.z
));
3662 max
= VT(nmax(amin
.x
, amax
.x
), nmax(amin
.y
, amax
.y
), nmax(amin
.z
, amax
.z
));
3665 min
.x
= nmin(amin
.x
, amax
.x
);
3666 min
.y
= nmin(amin
.y
, amax
.y
);
3667 static if (VT
.Dims
== 3) min
.z
= nmin(amin
.z
, amax
.z
);
3668 max
.x
= nmax(amin
.x
, amax
.x
);
3669 max
.y
= nmax(amin
.y
, amax
.y
);
3670 static if (VT
.Dims
== 3) max
.z
= nmax(amin
.z
, amax
.z
);
3675 static if (VT
.Dims
== 2) {
3676 min
= VT(+VT
.Float
.infinity
, +VT
.Float
.infinity
);
3677 max
= VT(-VT
.Float
.infinity
, -VT
.Float
.infinity
);
3679 min
= VT(+VT
.Float
.infinity
, +VT
.Float
.infinity
, +VT
.Float
.infinity
);
3680 max
= VT(-VT
.Float
.infinity
, -VT
.Float
.infinity
, -VT
.Float
.infinity
);
3684 void setMinMax() (in auto ref VT amin
, in auto ref VT amax
) pure {
3685 pragma(inline
, true);
3687 // this breaks VecN ctor inliner (fuck!)
3688 static if (VT
.Dims
== 2) {
3689 min
= VT(nmin(amin
.x
, amax
.x
), nmin(amin
.y
, amax
.y
));
3690 max
= VT(nmax(amin
.x
, amax
.x
), nmax(amin
.y
, amax
.y
));
3692 min
= VT(nmin(amin
.x
, amax
.x
), nmin(amin
.y
, amax
.y
), nmin(amin
.z
, amax
.z
));
3693 max
= VT(nmax(amin
.x
, amax
.x
), nmax(amin
.y
, amax
.y
), nmax(amin
.z
, amax
.z
));
3696 min
.x
= nmin(amin
.x
, amax
.x
);
3697 min
.y
= nmin(amin
.y
, amax
.y
);
3698 static if (VT
.Dims
== 3) min
.z
= nmin(amin
.z
, amax
.z
);
3699 max
.x
= nmax(amin
.x
, amax
.x
);
3700 max
.y
= nmax(amin
.y
, amax
.y
);
3701 static if (VT
.Dims
== 3) max
.z
= nmax(amin
.z
, amax
.z
);
3705 //@property VT min () const pure { pragma(inline, true); return center-half; }
3706 //@property VT max () const pure { pragma(inline, true); return center+half; }
3708 VT
center () const pure { pragma(inline
, true); return (min
+max
)/2; }
3709 VT
extent () const pure { pragma(inline
, true); return max
-min
; }
3711 //@property valid () const { pragma(inline, true); return center.isFinite && half.isFinite; }
3712 @property valid () const {
3713 pragma(inline
, true);
3714 static if (VT
.Dims
== 2) {
3715 return (min
.isFinite
&& max
.isFinite
&& min
.x
<= max
.x
&& min
.y
<= max
.y
);
3717 return (min
.isFinite
&& max
.isFinite
&& min
.x
<= max
.x
&& min
.y
<= max
.y
&& min
.z
<= max
.z
);
3721 // return the volume of the AABB
3722 @property Float
volume () const {
3723 pragma(inline
, true);
3724 immutable diff
= max
-min
;
3725 static if (VT
.Dims
== 3) {
3726 return diff
.x
*diff
.y
*diff
.z
;
3728 return diff
.x
*diff
.y
;
3732 static auto mergeAABBs() (in auto ref Me aabb1
, in auto ref Me aabb2
) {
3734 res
.merge(aabb1
, aabb2
);
3738 void merge() (in auto ref Me aabb1
, in auto ref Me aabb2
) {
3739 pragma(inline
, true);
3740 min
.x
= nmin(aabb1
.min
.x
, aabb2
.min
.x
);
3741 min
.y
= nmin(aabb1
.min
.y
, aabb2
.min
.y
);
3742 max
.x
= nmax(aabb1
.max
.x
, aabb2
.max
.x
);
3743 max
.y
= nmax(aabb1
.max
.y
, aabb2
.max
.y
);
3744 static if (VT
.Dims
== 3) {
3745 min
.z
= nmin(aabb1
.min
.z
, aabb2
.min
.z
);
3746 max
.z
= nmax(aabb1
.max
.z
, aabb2
.max
.z
);
3750 void merge() (in auto ref Me aabb1
) {
3751 pragma(inline
, true);
3752 min
.x
= nmin(aabb1
.min
.x
, min
.x
);
3753 min
.y
= nmin(aabb1
.min
.y
, min
.y
);
3754 max
.x
= nmax(aabb1
.max
.x
, max
.x
);
3755 max
.y
= nmax(aabb1
.max
.y
, max
.y
);
3756 static if (VT
.Dims
== 3) {
3757 min
.z
= nmin(aabb1
.min
.z
, min
.z
);
3758 max
.z
= nmax(aabb1
.max
.z
, max
.z
);
3762 void addPoint() (in auto ref VT v
) {
3763 min
.x
= nmin(min
.x
, v
.x
);
3764 max
.x
= nmax(max
.x
, v
.x
);
3765 min
.y
= nmin(min
.y
, v
.y
);
3766 max
.y
= nmax(max
.y
, v
.y
);
3767 static if (VT
.Dims
== 3) {
3768 min
.z
= nmin(min
.z
, v
.z
);
3769 max
.z
= nmax(max
.z
, v
.z
);
3773 void opOpAssign(string op
:"~") (in auto ref VT v
) { pragma(inline
, true); addPoint(v
); }
3774 void opOpAssign(string op
:"~") (in auto ref Me aabb1
) { pragma(inline
, true); merge(aabb1
); }
3776 // return true if the current AABB contains the AABB given in parameter
3777 bool contains() (in auto ref Me aabb
) const {
3778 //pragma(inline, true);
3779 static if (VT
.Dims
== 3) {
3781 aabb
.min
.x
>= min
.x
&& aabb
.min
.y
>= min
.y
&& aabb
.min
.z
>= min
.z
&&
3782 aabb
.max
.x
<= max
.x
&& aabb
.max
.y
<= max
.y
&& aabb
.max
.z
<= max
.z
;
3785 aabb
.min
.x
>= min
.x
&& aabb
.min
.y
>= min
.y
&&
3786 aabb
.max
.x
<= max
.x
&& aabb
.max
.y
<= max
.y
;
3790 bool contains() (in auto ref VT p
) const {
3791 pragma(inline
, true);
3792 static if (VT
.Dims
== 2) {
3793 return (p
.x
>= min
.x
&& p
.y
>= min
.y
&& p
.x
<= max
.x
&& p
.y
<= max
.y
);
3795 return (p
.x
>= min
.x
&& p
.y
>= min
.y
&& p
.z
>= min
.z
&& p
.x
<= max
.x
&& p
.y
<= max
.y
&& p
.z
<= max
.z
);
3799 // extrude bbox a little, to compensate floating point inexactness
3801 void extrude (Float delta) pure {
3804 static if (VT.Dims == 3) min.z -= delta;
3807 static if (VT.Dims == 3) max.z += delta;
3811 // return true if the current AABB is overlapping with the AABB in parameter
3812 // two AABBs overlap if they overlap in the two(three) x, y (and z) axes at the same time
3813 bool overlaps() (in auto ref Me aabb
) const {
3814 //pragma(inline, true);
3815 // exit with no intersection if found separated along any axis
3816 if (max
.x
< aabb
.min
.x || min
.x
> aabb
.max
.x
) return false;
3817 if (max
.y
< aabb
.min
.y || min
.y
> aabb
.max
.y
) return false;
3818 static if (VT
.Dims
== 3) {
3819 if (max
.z
< aabb
.min
.z || min
.z
> aabb
.max
.z
) return false;
3824 // ////////////////////////////////////////////////////////////////////////// //
3825 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
3826 // rd: ray direction, normalized
3827 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
3828 static bool intersects() (in auto ref VT bmin
, in auto ref VT bmax
, in auto ref Ray
!VT ray
, Float
* tmino
=null, Float
* tmaxo
=null) {
3829 // ok with coplanars, but dmd sux at unrolled loops
3831 immutable Float dinvp0
= cast(Float
)1/ray
.dir
.x
; // 1/0 will produce inf
3832 immutable Float t1p0
= (bmin
.x
-ray
.orig
.x
)*dinvp0
;
3833 immutable Float t2p0
= (bmax
.x
-ray
.orig
.x
)*dinvp0
;
3834 Float tmin
= nmin(t1p0
, t2p0
);
3835 Float tmax
= nmax(t1p0
, t2p0
);
3838 immutable Float dinv
= cast(Float
)1/ray
.dir
.y
; // 1/0 will produce inf
3839 immutable Float t1
= (bmin
.y
-ray
.orig
.y
)*dinv
;
3840 immutable Float t2
= (bmax
.y
-ray
.orig
.y
)*dinv
;
3841 tmin
= nmax(tmin
, nmin(nmin(t1
, t2
), tmax
));
3842 tmax
= nmin(tmax
, nmax(nmax(t1
, t2
), tmin
));
3845 static if (VT
.Dims
== 3) {
3847 immutable Float dinv
= cast(Float
)1/ray
.dir
.z
; // 1/0 will produce inf
3848 immutable Float t1
= (bmin
.z
-ray
.orig
.z
)*dinv
;
3849 immutable Float t2
= (bmax
.z
-ray
.orig
.z
)*dinv
;
3850 tmin
= nmax(tmin
, nmin(nmin(t1
, t2
), tmax
));
3851 tmax
= nmin(tmax
, nmax(nmax(t1
, t2
), tmin
));
3854 if (tmax
> nmax(tmin
, cast(Float
)0)) {
3855 if (tmino
!is null) *tmino
= tmin
;
3856 if (tmaxo
!is null) *tmaxo
= tmax
;
3863 bool intersects() (in auto ref Ray
!VT ray
, Float
* tmino
=null, Float
* tmaxo
=null) const @trusted {
3864 // ok with coplanars, but dmd sux at unrolled loops
3866 immutable Float dinvp0
= cast(Float
)1/ray
.dir
.x
; // 1/0 will produce inf
3867 immutable Float t1p0
= (min
.x
-ray
.orig
.x
)*dinvp0
;
3868 immutable Float t2p0
= (max
.x
-ray
.orig
.x
)*dinvp0
;
3869 Float tmin
= nmin(t1p0
, t2p0
);
3870 Float tmax
= nmax(t1p0
, t2p0
);
3873 immutable Float dinv
= cast(Float
)1/ray
.dir
.y
; // 1/0 will produce inf
3874 immutable Float t1
= (min
.y
-ray
.orig
.y
)*dinv
;
3875 immutable Float t2
= (max
.y
-ray
.orig
.y
)*dinv
;
3876 tmin
= nmax(tmin
, nmin(nmin(t1
, t2
), tmax
));
3877 tmax
= nmin(tmax
, nmax(nmax(t1
, t2
), tmin
));
3880 static if (VT
.Dims
== 3) {
3882 immutable Float dinv
= cast(Float
)1/ray
.dir
.z
; // 1/0 will produce inf
3883 immutable Float t1
= (min
.z
-ray
.orig
.z
)*dinv
;
3884 immutable Float t2
= (max
.z
-ray
.orig
.z
)*dinv
;
3885 tmin
= nmax(tmin
, nmin(nmin(t1
, t2
), tmax
));
3886 tmax
= nmin(tmax
, nmax(nmax(t1
, t2
), tmin
));
3889 if (tmax
> nmax(tmin
, cast(Float
)0)) {
3890 if (tmino
!is null) *tmino
= tmin
;
3891 if (tmaxo
!is null) *tmaxo
= tmax
;
3898 Float
segIntersectMin() (in auto ref VT a
, in auto ref VT b
) const @trusted {
3900 if (!intersects(Ray
!VT
.fromPoints(a
, b
), &tmin
)) return -1;
3901 if (tmin
< 0) return 0; // inside
3902 if (tmin
*tmin
> (b
-a
).lengthSquared
) return -1;
3906 Float
segIntersectMax() (in auto ref VT a
, in auto ref VT b
) const @trusted {
3908 if (!intersects(Ray
!VT
.fromPoints(a
, b
), null, &tmax
)) return -1;
3909 if (tmax
*tmax
> (b
-a
).lengthSquared
) return -1;
3913 bool isIntersects() (in auto ref VT a
, in auto ref VT b
) const @trusted {
3914 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
3915 static if (VT
.Dims
== 2) {
3916 if (a
.x
>= min
.x
&& a
.y
>= min
.y
&& a
.x
<= max
.x
&& a
.y
<= max
.y
) return true; // a
3917 if (b
.x
>= min
.x
&& b
.y
>= min
.y
&& b
.x
<= max
.x
&& b
.y
<= max
.y
) return true; // b
3919 if (a
.x
>= min
.x
&& a
.y
>= min
.y
&& a
.z
>= min
.z
&& a
.x
<= max
.x
&& a
.y
<= max
.y
&& a
.z
<= max
.z
) return true; // a
3920 if (b
.x
>= min
.x
&& b
.y
>= min
.y
&& b
.z
>= min
.z
&& b
.x
<= max
.x
&& b
.y
<= max
.y
&& b
.z
<= max
.z
) return true; // b
3922 // nope, do it hard way
3924 if (!intersects(Ray
!VT
.fromPoints(a
, b
), &tmin
)) return false;
3925 if (tmin
< 0) return true; // inside, just in case
3926 return (tmin
*tmin
<= (b
-a
).lengthSquared
);
3929 ref inout(VT
) opIndex (usize idx
) inout {
3930 pragma(inline
, true);
3931 return (idx
== 0 ? min
: max
);
3934 /// sweep two AABB's to see if and when they are overlapping
3935 /// returns `true` if collision was detected (or boxes overlaps)
3936 /// u0 = normalized time of first collision (i.e. collision starts at myMove*u0)
3937 /// u1 = normalized time of second collision (i.e. collision stops after myMove*u1)
3938 /// hitnormal = normal that will move `this` apart of `b` edge it collided with
3939 /// no output values are valid if no collision was detected
3940 /// WARNING! hit normal calculation is not tested!
3941 bool sweep() (in auto ref VT myMove
, in auto ref Me b
, Float
* u0
, VT
* hitnormal
=null, Float
* u1
=null) const @trusted {
3942 // check if they are overlapping right now
3943 if (this.overlaps(b
)) {
3944 if (u0
!is null) *u0
= 0;
3945 if (u1
!is null) *u1
= 0;
3946 if (hitnormal
!is null) *hitnormal
= VT(); // oops
3950 immutable v
= -myMove
; // treat b as stationary, so invert v to get relative velocity
3952 // not moving, and not overlapping
3953 if (v
.isZero
) return false;
3957 Float
[VT
.Dims
] overlapTime
= 0;
3960 foreach (immutable aidx
; 0..VT
.Dims
) {
3962 immutable Float vv
= v
[aidx
];
3964 immutable Float invv
= cast(Float
)1/vv
;
3965 if (b
.max
[aidx
] < a
.min
[aidx
]) return false;
3966 if (b
.max
[aidx
] > a
.min
[aidx
]) outTime
= nmin((a
.min
[aidx
]-b
.max
[aidx
])*invv
, outTime
);
3967 if (a
.max
[aidx
] < b
.min
[aidx
]) hitTime
= nmax((overlapTime
.ptr
[aidx
] = (a
.max
[aidx
]-b
.min
[aidx
])*invv
), hitTime
);
3968 if (hitTime
> outTime
) return false;
3969 } else if (vv
> 0) {
3970 immutable Float invv
= cast(Float
)1/vv
;
3971 if (b
.min
[aidx
] > a
.max
[aidx
]) return false;
3972 if (a
.max
[aidx
] > b
.min
[aidx
]) outTime
= nmin((a
.max
[aidx
]-b
.min
[aidx
])*invv
, outTime
);
3973 if (b
.max
[aidx
] < a
.min
[aidx
]) hitTime
= nmax((overlapTime
.ptr
[aidx
] = (a
.min
[aidx
]-b
.max
[aidx
])*invv
), hitTime
);
3974 if (hitTime
> outTime
) return false;
3978 if (u0
!is null) *u0
= hitTime
;
3979 if (u1
!is null) *u1
= outTime
;
3981 // hit normal is along axis with the highest overlap time
3982 if (hitnormal
!is null) {
3983 static if (VT
.Dims
== 3) {
3985 if (overlapTime
.ptr
[1] > overlapTime
.ptr
[0]) aidx
= 1;
3986 if (overlapTime
.ptr
[2] > overlapTime
.ptr
[aidx
]) aidx
= 2;
3987 VT hn
; // zero vector
3988 hn
[aidx
] = (v
[aidx
] < 0 ?
-1 : v
[aidx
] > 0 ?
1 : 0);
3991 if (overlapTime
.ptr
[0] > overlapTime
.ptr
[1]) {
3992 *hitnormal
= VT((v
.x
< 0 ?
-1 : v
.x
> 0 ?
1 : 0), 0);
3994 *hitnormal
= VT(0, (v
.y
< 0 ?
-1 : v
.y
> 0 ?
1 : 0));
4003 auto u_0 = VT(0, 0, 0); // first times of overlap along each axis
4004 auto u_1 = VT(1, 1, 1); // last times of overlap along each axis
4005 bool wasHit = false;
4007 // find the possible first and last times of overlap along each axis
4008 foreach (immutable idx; 0..VT.Dims) {
4009 Float dinv = v[idx];
4011 dinv = cast(Float)1/dinv;
4012 if (this.max[idx] < b.min[idx] && dinv < 0) {
4013 u_0[idx] = (this.max[idx]-b.min[idx])*dinv;
4015 } else if (b.max[idx] < this.min[idx] && dinv > 0) {
4016 u_0[idx] = (this.min[idx]-b.max[idx])*dinv;
4019 if (b.max[idx] > this.min[idx] && dinv < 0) {
4020 u_1[idx] = (this.min[idx]-b.max[idx])*dinv;
4022 } else if (this.max[idx] > b.min[idx] && dinv > 0) {
4023 u_1[idx] = (this.max[idx]-b.min[idx])*dinv;
4031 if (u0 !is null) *u0 = 0;
4032 if (u1 !is null) *u1 = 0;
4036 static if (VT.Dims == 3) {
4037 immutable Float uu0 = nmax(u_0.x, nmax(u_0.y, u_0.z)); // possible first time of overlap
4038 immutable Float uu1 = nmin(u_1.x, nmin(u_1.y, u_1.z)); // possible last time of overlap
4040 immutable Float uu0 = nmax(u_0.x, u_0.y); // possible first time of overlap
4041 immutable Float uu1 = nmin(u_1.x, u_1.y); // possible last time of overlap
4044 if (u0 !is null) *u0 = uu0;
4045 if (u1 !is null) *u1 = uu1;
4047 // they could have only collided if the first time of overlap occurred before the last time of overlap
4048 return (uu0 <= uu1);
4053 /// check to see if the sphere overlaps the AABB
4054 bool overlaps(ST
) (in auto ref ST sphere
) if (IsSphere
!(ST
, VT
)) { pragma(inline
, true); return overlapsSphere(sphere
.orig
, sphere
.radius
); }
4056 /// check to see if the sphere overlaps the AABB
4057 bool overlapsSphere() (in auto ref VT center
, Float radius
) {
4059 // find the square of the distance from the sphere to the box
4060 foreach (immutable idx
; 0..VT
.Dims
) {
4061 if (center
[idx
] < min
[idx
]) {
4062 immutable Float s
= center
[idx
]-min
[idx
];
4064 } else if (center
[idx
] > max
[idx
]) {
4065 immutable Float s
= center
[idx
]-max
[idx
];
4069 return (d
<= radius
*radius
);
4074 // ////////////////////////////////////////////////////////////////////////// //
4075 /* Dynamic AABB tree (bounding volume hierarchy)
4076 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
4077 * Copyright (c) 2010-2016 Daniel Chappuis
4079 * This software is provided 'as-is', without any express or implied warranty.
4080 * In no event will the authors be held liable for any damages arising from the
4081 * use of this software.
4083 * Permission is granted to anyone to use this software for any purpose,
4084 * including commercial applications, and to alter it and redistribute it
4085 * freely, subject to the following restrictions:
4087 * 1. The origin of this software must not be misrepresented; you must not claim
4088 * that you wrote the original software. If you use this software in a
4089 * product, an acknowledgment in the product documentation would be
4090 * appreciated but is not required.
4092 * 2. Altered source versions must be plainly marked as such, and must not be
4093 * misrepresented as being the original software.
4095 * 3. This notice may not be removed or altered from any source distribution.
4097 /* WARNING! BY DEFAULT TREE WILL NOT PROTECT OBJECTS FROM GC! */
4098 // ////////////////////////////////////////////////////////////////////////// //
4100 * This class implements a dynamic AABB tree that is used for broad-phase
4101 * collision detection. This data structure is inspired by Nathanael Presson's
4102 * dynamic tree implementation in BulletPhysics. The following implementation is
4103 * based on the one from Erin Catto in Box2D as described in the book
4104 * "Introduction to Game Physics with Box2D" by Ian Parberry.
4106 /// Dynamic AABB Tree: can be used to speed up broad phase in various engines
4107 /// GCAnchor==true: add nodes as GC roots; you won't need it if you're storing nodes in some other way
4108 public final class DynamicAABBTree(VT
, BodyBase
, bool GCAnchor
=false) if (IsVector
!VT
&& is(BodyBase
== class)) {
4110 static T
min(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
< b ? a
: b
); }
4111 static T
max(T
) (in T a
, in T b
) { pragma(inline
, true); return (a
> b ? a
: b
); }
4115 alias Float
= VT
.Float
;
4116 alias Me
= typeof(this);
4117 alias AABB
= AABBImpl
!VT
;
4119 enum FloatNum(Float v
) = cast(Float
)v
;
4122 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
4123 // also inflated in direction of the linear motion of the body by mutliplying the
4124 // followin constant with the linear velocity and the elapsed time between two frames
4125 enum Float LinearMotionGapMultiplier
= FloatNum
!(1.7);
4128 // called when a overlapping node has been found during the call to forEachAABBOverlap()
4129 // return `true` to stop
4130 alias OverlapCallback
= void delegate (BodyBase abody
);
4131 alias SimpleQueryCallback
= bool delegate (BodyBase abody
);
4132 alias SimpleQueryCallbackNR
= void delegate (BodyBase abody
);
4133 alias SegQueryCallback
= Float
delegate (BodyBase abody
, in ref VT a
, in ref VT b
); // return dist from a to abody
4136 align(1) struct TreeNode
{
4138 enum NullTreeNode
= -1;
4139 enum { Left
= 0, Right
= 1 }
4140 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
4145 // a node is either a leaf (has data) or is an internal node (has children)
4147 int[2] children
; /// left and right child of the node (children[0] = left child)
4150 // height of the node in the tree (-1 for free nodes)
4152 // fat axis aligned bounding box (AABB) corresponding to the node
4154 // return true if the node is a leaf of the tree
4155 @property const pure nothrow @safe @nogc {
4156 bool leaf () { pragma(inline
, true); return (height
== 0); }
4157 bool free () { pragma(inline
, true); return (height
== -1); }
4162 TreeNode
* mNodes
; // pointer to the memory location of the nodes of the tree
4163 int mRootNodeId
; // id of the root node of the tree
4164 int mFreeNodeId
; // id of the first node of the list of free (allocated) nodes in the tree that we can use
4165 int mAllocCount
; // number of allocated nodes in the tree
4166 int mNodeCount
; // number of nodes in the tree
4168 // extra AABB Gap used to allow the collision shape to move a little bit
4169 // without triggering a large modification of the tree which can be costly
4173 // allocate and return a node to use in the tree
4174 int allocateNode () {
4175 // if there is no more allocated node to use
4176 if (mFreeNodeId
== TreeNode
.NullTreeNode
) {
4177 import core
.stdc
.stdlib
: realloc
;
4178 version(aabbtree_many_asserts
) assert(mNodeCount
== mAllocCount
);
4179 // allocate more nodes in the tree
4180 auto newsz
= (mAllocCount
< 8192 ? mAllocCount
*2 : mAllocCount
+8192);
4181 TreeNode
* nn
= cast(TreeNode
*)realloc(mNodes
, newsz
*TreeNode
.sizeof
);
4182 if (nn
is null) assert(0, "out of memory");
4183 //{ import core.stdc.stdio; printf("realloced: old=%u; new=%u\n", mAllocCount, newsz); }
4184 mAllocCount
= newsz
;
4186 // initialize the allocated nodes
4187 foreach (int i
; mNodeCount
..mAllocCount
-1) {
4188 mNodes
[i
].nextNodeId
= i
+1;
4189 mNodes
[i
].height
= -1;
4191 mNodes
[mAllocCount
-1].nextNodeId
= TreeNode
.NullTreeNode
;
4192 mNodes
[mAllocCount
-1].height
= -1;
4193 mFreeNodeId
= mNodeCount
;
4195 // get the next free node
4196 int freeNodeId
= mFreeNodeId
;
4197 version(aabbtree_many_asserts
) assert(freeNodeId
< mAllocCount
);
4198 mFreeNodeId
= mNodes
[freeNodeId
].nextNodeId
;
4199 mNodes
[freeNodeId
].parentId
= TreeNode
.NullTreeNode
;
4200 mNodes
[freeNodeId
].height
= 0;
4206 void releaseNode (int nodeId
) {
4207 version(aabbtree_many_asserts
) assert(mNodeCount
> 0);
4208 version(aabbtree_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
4209 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].height
>= 0);
4210 mNodes
[nodeId
].nextNodeId
= mFreeNodeId
;
4211 mNodes
[nodeId
].height
= -1;
4212 mFreeNodeId
= nodeId
;
4216 // insert a leaf node in the tree
4217 // the process of inserting a new leaf node in the dynamic tree is described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
4218 void insertLeafNode (int nodeId
) {
4219 // if the tree is empty
4220 if (mRootNodeId
== TreeNode
.NullTreeNode
) {
4221 mRootNodeId
= nodeId
;
4222 mNodes
[mRootNodeId
].parentId
= TreeNode
.NullTreeNode
;
4226 version(aabbtree_many_asserts
) assert(mRootNodeId
!= TreeNode
.NullTreeNode
);
4228 // find the best sibling node for the new node
4229 AABB newNodeAABB
= mNodes
[nodeId
].aabb
;
4230 int currentNodeId
= mRootNodeId
;
4231 while (!mNodes
[currentNodeId
].leaf
) {
4232 int leftChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
4233 int rightChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
4235 // compute the merged AABB
4236 Float volumeAABB
= mNodes
[currentNodeId
].aabb
.volume
;
4237 AABB mergedAABBs
= AABB
.mergeAABBs(mNodes
[currentNodeId
].aabb
, newNodeAABB
);
4238 Float mergedVolume
= mergedAABBs
.volume
;
4240 // compute the cost of making the current node the sibling of the new node
4241 Float costS
= FloatNum
!2*mergedVolume
;
4243 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
4244 Float costI
= FloatNum
!2*(mergedVolume
-volumeAABB
);
4246 // compute the cost of descending into the left child
4247 AABB currentAndLeftAABB
= AABB
.mergeAABBs(newNodeAABB
, mNodes
[leftChild
].aabb
);
4248 Float costLeft
= currentAndLeftAABB
.volume
+costI
;
4249 if (!mNodes
[leftChild
].leaf
) costLeft
-= mNodes
[leftChild
].aabb
.volume
;
4251 // compute the cost of descending into the right child
4252 AABB currentAndRightAABB
= AABB
.mergeAABBs(newNodeAABB
, mNodes
[rightChild
].aabb
);
4253 Float costRight
= currentAndRightAABB
.volume
+costI
;
4254 if (!mNodes
[rightChild
].leaf
) costRight
-= mNodes
[rightChild
].aabb
.volume
;
4256 // if the cost of making the current node a sibling of the new node is smaller than the cost of going down into the left or right child
4257 if (costS
< costLeft
&& costS
< costRight
) break;
4259 // it is cheaper to go down into a child of the current node, choose the best child
4260 currentNodeId
= (costLeft
< costRight ? leftChild
: rightChild
);
4263 int siblingNode
= currentNodeId
;
4265 // create a new parent for the new node and the sibling node
4266 int oldParentNode
= mNodes
[siblingNode
].parentId
;
4267 int newParentNode
= allocateNode();
4268 mNodes
[newParentNode
].parentId
= oldParentNode
;
4269 mNodes
[newParentNode
].aabb
.merge(mNodes
[siblingNode
].aabb
, newNodeAABB
);
4270 mNodes
[newParentNode
].height
= cast(short)(mNodes
[siblingNode
].height
+1);
4271 version(aabbtree_many_asserts
) assert(mNodes
[newParentNode
].height
> 0);
4273 // if the sibling node was not the root node
4274 if (oldParentNode
!= TreeNode
.NullTreeNode
) {
4275 version(aabbtree_many_asserts
) assert(!mNodes
[oldParentNode
].leaf
);
4276 if (mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Left
] == siblingNode
) {
4277 mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Left
] = newParentNode
;
4279 mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Right
] = newParentNode
;
4281 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Left
] = siblingNode
;
4282 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Right
] = nodeId
;
4283 mNodes
[siblingNode
].parentId
= newParentNode
;
4284 mNodes
[nodeId
].parentId
= newParentNode
;
4286 // if the sibling node was the root node
4287 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Left
] = siblingNode
;
4288 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Right
] = nodeId
;
4289 mNodes
[siblingNode
].parentId
= newParentNode
;
4290 mNodes
[nodeId
].parentId
= newParentNode
;
4291 mRootNodeId
= newParentNode
;
4294 // move up in the tree to change the AABBs that have changed
4295 currentNodeId
= mNodes
[nodeId
].parentId
;
4296 version(aabbtree_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
4297 while (currentNodeId
!= TreeNode
.NullTreeNode
) {
4298 // balance the sub-tree of the current node if it is not balanced
4299 currentNodeId
= balanceSubTreeAtNode(currentNodeId
);
4300 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].leaf
);
4302 version(aabbtree_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
4303 int leftChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
4304 int rightChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
4305 version(aabbtree_many_asserts
) assert(leftChild
!= TreeNode
.NullTreeNode
);
4306 version(aabbtree_many_asserts
) assert(rightChild
!= TreeNode
.NullTreeNode
);
4308 // recompute the height of the node in the tree
4309 mNodes
[currentNodeId
].height
= cast(short)(max(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
)+1);
4310 version(aabbtree_many_asserts
) assert(mNodes
[currentNodeId
].height
> 0);
4312 // recompute the AABB of the node
4313 mNodes
[currentNodeId
].aabb
.merge(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
4315 currentNodeId
= mNodes
[currentNodeId
].parentId
;
4318 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].leaf
);
4321 // remove a leaf node from the tree
4322 void removeLeafNode (int nodeId
) {
4323 version(aabbtree_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
4324 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].leaf
);
4326 // if we are removing the root node (root node is a leaf in this case)
4327 if (mRootNodeId
== nodeId
) { mRootNodeId
= TreeNode
.NullTreeNode
; return; }
4329 int parentNodeId
= mNodes
[nodeId
].parentId
;
4330 int grandParentNodeId
= mNodes
[parentNodeId
].parentId
;
4333 if (mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
4334 siblingNodeId
= mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Right
];
4336 siblingNodeId
= mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Left
];
4339 // if the parent of the node to remove is not the root node
4340 if (grandParentNodeId
!= TreeNode
.NullTreeNode
) {
4341 // destroy the parent node
4342 if (mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Left
] == parentNodeId
) {
4343 mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Left
] = siblingNodeId
;
4345 version(aabbtree_many_asserts
) assert(mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Right
] == parentNodeId
);
4346 mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Right
] = siblingNodeId
;
4348 mNodes
[siblingNodeId
].parentId
= grandParentNodeId
;
4349 releaseNode(parentNodeId
);
4351 // now, we need to recompute the AABBs of the node on the path back to the root and make sure that the tree is still balanced
4352 int currentNodeId
= grandParentNodeId
;
4353 while (currentNodeId
!= TreeNode
.NullTreeNode
) {
4354 // balance the current sub-tree if necessary
4355 currentNodeId
= balanceSubTreeAtNode(currentNodeId
);
4357 version(aabbtree_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
4359 // get the two children.ptr of the current node
4360 int leftChildId
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
4361 int rightChildId
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
4363 // recompute the AABB and the height of the current node
4364 mNodes
[currentNodeId
].aabb
.merge(mNodes
[leftChildId
].aabb
, mNodes
[rightChildId
].aabb
);
4365 mNodes
[currentNodeId
].height
= cast(short)(max(mNodes
[leftChildId
].height
, mNodes
[rightChildId
].height
)+1);
4366 version(aabbtree_many_asserts
) assert(mNodes
[currentNodeId
].height
> 0);
4368 currentNodeId
= mNodes
[currentNodeId
].parentId
;
4371 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
4372 mRootNodeId
= siblingNodeId
;
4373 mNodes
[siblingNodeId
].parentId
= TreeNode
.NullTreeNode
;
4374 releaseNode(parentNodeId
);
4378 // balance the sub-tree of a given node using left or right rotations
4379 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
4380 // this method returns the new root node id
4381 int balanceSubTreeAtNode (int nodeId
) {
4382 version(aabbtree_many_asserts
) assert(nodeId
!= TreeNode
.NullTreeNode
);
4384 TreeNode
* nodeA
= mNodes
+nodeId
;
4386 // if the node is a leaf or the height of A's sub-tree is less than 2
4387 if (nodeA
.leaf || nodeA
.height
< 2) return nodeId
; // do not perform any rotation
4389 // get the two children nodes
4390 int nodeBId
= nodeA
.children
.ptr
[TreeNode
.Left
];
4391 int nodeCId
= nodeA
.children
.ptr
[TreeNode
.Right
];
4392 version(aabbtree_many_asserts
) assert(nodeBId
>= 0 && nodeBId
< mAllocCount
);
4393 version(aabbtree_many_asserts
) assert(nodeCId
>= 0 && nodeCId
< mAllocCount
);
4394 TreeNode
* nodeB
= mNodes
+nodeBId
;
4395 TreeNode
* nodeC
= mNodes
+nodeCId
;
4397 // compute the factor of the left and right sub-trees
4398 int balanceFactor
= nodeC
.height
-nodeB
.height
;
4400 // if the right node C is 2 higher than left node B
4401 if (balanceFactor
> 1) {
4402 version(aabbtree_many_asserts
) assert(!nodeC
.leaf
);
4404 int nodeFId
= nodeC
.children
.ptr
[TreeNode
.Left
];
4405 int nodeGId
= nodeC
.children
.ptr
[TreeNode
.Right
];
4406 version(aabbtree_many_asserts
) assert(nodeFId
>= 0 && nodeFId
< mAllocCount
);
4407 version(aabbtree_many_asserts
) assert(nodeGId
>= 0 && nodeGId
< mAllocCount
);
4408 TreeNode
* nodeF
= mNodes
+nodeFId
;
4409 TreeNode
* nodeG
= mNodes
+nodeGId
;
4411 nodeC
.children
.ptr
[TreeNode
.Left
] = nodeId
;
4412 nodeC
.parentId
= nodeA
.parentId
;
4413 nodeA
.parentId
= nodeCId
;
4415 if (nodeC
.parentId
!= TreeNode
.NullTreeNode
) {
4416 if (mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
4417 mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Left
] = nodeCId
;
4419 version(aabbtree_many_asserts
) assert(mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Right
] == nodeId
);
4420 mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Right
] = nodeCId
;
4423 mRootNodeId
= nodeCId
;
4426 version(aabbtree_many_asserts
) assert(!nodeC
.leaf
);
4427 version(aabbtree_many_asserts
) assert(!nodeA
.leaf
);
4429 // if the right node C was higher than left node B because of the F node
4430 if (nodeF
.height
> nodeG
.height
) {
4431 nodeC
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
4432 nodeA
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
4433 nodeG
.parentId
= nodeId
;
4435 // recompute the AABB of node A and C
4436 nodeA
.aabb
.merge(nodeB
.aabb
, nodeG
.aabb
);
4437 nodeC
.aabb
.merge(nodeA
.aabb
, nodeF
.aabb
);
4439 // recompute the height of node A and C
4440 nodeA
.height
= cast(short)(max(nodeB
.height
, nodeG
.height
)+1);
4441 nodeC
.height
= cast(short)(max(nodeA
.height
, nodeF
.height
)+1);
4442 version(aabbtree_many_asserts
) assert(nodeA
.height
> 0);
4443 version(aabbtree_many_asserts
) assert(nodeC
.height
> 0);
4445 // if the right node C was higher than left node B because of node G
4446 nodeC
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
4447 nodeA
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
4448 nodeF
.parentId
= nodeId
;
4450 // recompute the AABB of node A and C
4451 nodeA
.aabb
.merge(nodeB
.aabb
, nodeF
.aabb
);
4452 nodeC
.aabb
.merge(nodeA
.aabb
, nodeG
.aabb
);
4454 // recompute the height of node A and C
4455 nodeA
.height
= cast(short)(max(nodeB
.height
, nodeF
.height
)+1);
4456 nodeC
.height
= cast(short)(max(nodeA
.height
, nodeG
.height
)+1);
4457 version(aabbtree_many_asserts
) assert(nodeA
.height
> 0);
4458 version(aabbtree_many_asserts
) assert(nodeC
.height
> 0);
4461 // return the new root of the sub-tree
4465 // if the left node B is 2 higher than right node C
4466 if (balanceFactor
< -1) {
4467 version(aabbtree_many_asserts
) assert(!nodeB
.leaf
);
4469 int nodeFId
= nodeB
.children
.ptr
[TreeNode
.Left
];
4470 int nodeGId
= nodeB
.children
.ptr
[TreeNode
.Right
];
4471 version(aabbtree_many_asserts
) assert(nodeFId
>= 0 && nodeFId
< mAllocCount
);
4472 version(aabbtree_many_asserts
) assert(nodeGId
>= 0 && nodeGId
< mAllocCount
);
4473 TreeNode
* nodeF
= mNodes
+nodeFId
;
4474 TreeNode
* nodeG
= mNodes
+nodeGId
;
4476 nodeB
.children
.ptr
[TreeNode
.Left
] = nodeId
;
4477 nodeB
.parentId
= nodeA
.parentId
;
4478 nodeA
.parentId
= nodeBId
;
4480 if (nodeB
.parentId
!= TreeNode
.NullTreeNode
) {
4481 if (mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
4482 mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Left
] = nodeBId
;
4484 version(aabbtree_many_asserts
) assert(mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Right
] == nodeId
);
4485 mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Right
] = nodeBId
;
4488 mRootNodeId
= nodeBId
;
4491 version(aabbtree_many_asserts
) assert(!nodeB
.leaf
);
4492 version(aabbtree_many_asserts
) assert(!nodeA
.leaf
);
4494 // if the left node B was higher than right node C because of the F node
4495 if (nodeF
.height
> nodeG
.height
) {
4496 nodeB
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
4497 nodeA
.children
.ptr
[TreeNode
.Left
] = nodeGId
;
4498 nodeG
.parentId
= nodeId
;
4500 // recompute the AABB of node A and B
4501 nodeA
.aabb
.merge(nodeC
.aabb
, nodeG
.aabb
);
4502 nodeB
.aabb
.merge(nodeA
.aabb
, nodeF
.aabb
);
4504 // recompute the height of node A and B
4505 nodeA
.height
= cast(short)(max(nodeC
.height
, nodeG
.height
)+1);
4506 nodeB
.height
= cast(short)(max(nodeA
.height
, nodeF
.height
)+1);
4507 version(aabbtree_many_asserts
) assert(nodeA
.height
> 0);
4508 version(aabbtree_many_asserts
) assert(nodeB
.height
> 0);
4510 // if the left node B was higher than right node C because of node G
4511 nodeB
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
4512 nodeA
.children
.ptr
[TreeNode
.Left
] = nodeFId
;
4513 nodeF
.parentId
= nodeId
;
4515 // recompute the AABB of node A and B
4516 nodeA
.aabb
.merge(nodeC
.aabb
, nodeF
.aabb
);
4517 nodeB
.aabb
.merge(nodeA
.aabb
, nodeG
.aabb
);
4519 // recompute the height of node A and B
4520 nodeA
.height
= cast(short)(max(nodeC
.height
, nodeF
.height
)+1);
4521 nodeB
.height
= cast(short)(max(nodeA
.height
, nodeG
.height
)+1);
4522 version(aabbtree_many_asserts
) assert(nodeA
.height
> 0);
4523 version(aabbtree_many_asserts
) assert(nodeB
.height
> 0);
4526 // return the new root of the sub-tree
4530 // if the sub-tree is balanced, return the current root node
4534 // compute the height of a given node in the tree
4535 int computeHeight (int nodeId
) {
4536 version(aabbtree_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
4537 TreeNode
* node
= mNodes
+nodeId
;
4539 // if the node is a leaf, its height is zero
4540 if (node
.leaf
) return 0;
4542 // compute the height of the left and right sub-tree
4543 int leftHeight
= computeHeight(node
.children
.ptr
[TreeNode
.Left
]);
4544 int rightHeight
= computeHeight(node
.children
.ptr
[TreeNode
.Right
]);
4546 // return the height of the node
4547 return 1+max(leftHeight
, rightHeight
);
4550 // internally add an object into the tree
4551 int insertObjectInternal (in ref AABB aabb
, bool staticObject
) {
4552 // get the next available node (or allocate new ones if necessary)
4553 int nodeId
= allocateNode();
4555 // create the fat aabb to use in the tree
4556 mNodes
[nodeId
].aabb
= aabb
;
4557 if (!staticObject
) {
4558 static if (VT
.Dims
== 2) {
4559 immutable gap
= VT(mExtraGap
, mExtraGap
);
4561 immutable gap
= VT(mExtraGap
, mExtraGap
, mExtraGap
);
4563 mNodes
[nodeId
].aabb
.min
-= gap
;
4564 mNodes
[nodeId
].aabb
.max
+= gap
;
4567 // set the height of the node in the tree
4568 mNodes
[nodeId
].height
= 0;
4570 // insert the new leaf node in the tree
4571 insertLeafNode(nodeId
);
4572 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].leaf
);
4574 version(aabbtree_many_asserts
) assert(nodeId
>= 0);
4576 // return the id of the node
4580 // initialize the tree
4582 import core
.stdc
.stdlib
: malloc
;
4583 import core
.stdc
.string
: memset
;
4585 mRootNodeId
= TreeNode
.NullTreeNode
;
4589 mNodes
= cast(TreeNode
*)malloc(mAllocCount
*TreeNode
.sizeof
);
4590 if (mNodes
is null) assert(0, "out of memory");
4591 memset(mNodes
, 0, mAllocCount
*TreeNode
.sizeof
);
4593 // initialize the allocated nodes
4594 foreach (int i
; 0..mAllocCount
-1) {
4595 mNodes
[i
].nextNodeId
= i
+1;
4596 mNodes
[i
].height
= -1;
4598 mNodes
[mAllocCount
-1].nextNodeId
= TreeNode
.NullTreeNode
;
4599 mNodes
[mAllocCount
-1].height
= -1;
4603 // also, checks if the tree structure is valid (for debugging purpose)
4604 public void forEachLeaf (scope void delegate (BodyBase abody
, in ref AABB aabb
) dg
) {
4605 void forEachNode (int nodeId
) {
4606 if (nodeId
== TreeNode
.NullTreeNode
) return;
4607 // if it is the root
4608 if (nodeId
== mRootNodeId
) {
4609 assert(mNodes
[nodeId
].parentId
== TreeNode
.NullTreeNode
);
4611 // get the children nodes
4612 TreeNode
* pNode
= mNodes
+nodeId
;
4613 assert(pNode
.height
>= 0);
4614 assert(pNode
.aabb
.volume
> 0);
4615 // if the current node is a leaf
4617 assert(pNode
.height
== 0);
4618 if (dg
!is null) dg(pNode
.flesh
, pNode
.aabb
);
4620 int leftChild
= pNode
.children
.ptr
[TreeNode
.Left
];
4621 int rightChild
= pNode
.children
.ptr
[TreeNode
.Right
];
4622 // check that the children node Ids are valid
4623 assert(0 <= leftChild
&& leftChild
< mAllocCount
);
4624 assert(0 <= rightChild
&& rightChild
< mAllocCount
);
4625 // check that the children nodes have the correct parent node
4626 assert(mNodes
[leftChild
].parentId
== nodeId
);
4627 assert(mNodes
[rightChild
].parentId
== nodeId
);
4628 // check the height of node
4629 int height
= 1+max(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
);
4630 assert(mNodes
[nodeId
].height
== height
);
4631 // check the AABB of the node
4632 AABB aabb
= AABB
.mergeAABBs(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
4633 assert(aabb
.min
== mNodes
[nodeId
].aabb
.min
);
4634 assert(aabb
.max
== mNodes
[nodeId
].aabb
.max
);
4635 // recursively check the children nodes
4636 forEachNode(leftChild
);
4637 forEachNode(rightChild
);
4640 // recursively check each node
4641 forEachNode(mRootNodeId
);
4644 static if (GCAnchor
) void gcRelease () {
4645 import core
.memory
: GC
;
4646 foreach (ref TreeNode n
; mNodes
[0..mNodeCount
]) {
4648 auto flesh
= n
.flesh
;
4649 GC
.clrAttr(*cast(void**)&flesh
, GC
.BlkAttr
.NO_MOVE
);
4650 GC
.removeRoot(*cast(void**)&flesh
);
4655 version(aabbtree_query_count
) public int nodesVisited
, nodesDeepVisited
;
4657 // return `true` from visitor to stop immediately
4658 // checker should check if this node should be considered to further checking
4659 // returns tree node if visitor says stop or -1
4660 private int visit (scope bool delegate (TreeNode
* node
) checker
, scope bool delegate (BodyBase abody
) visitor
) {
4661 int[1024] stack
= void; // stack with the nodes to visit
4663 int[] bigstack
= null;
4664 scope(exit
) if (bigstack
.ptr
!is null) delete bigstack
;
4666 void spush (int id
) {
4667 if (sp
< stack
.length
) {
4668 // use "small stack"
4669 stack
.ptr
[sp
++] = id
;
4671 if (sp
>= int.max
/2) assert(0, "huge tree!");
4673 immutable int xsp
= sp
-cast(int)stack
.length
;
4674 if (xsp
< bigstack
.length
) {
4676 bigstack
.ptr
[xsp
] = id
;
4679 auto optr
= bigstack
.ptr
;
4681 if (bigstack
.ptr
!is optr
) {
4682 import core
.memory
: GC
;
4683 optr
= bigstack
.ptr
;
4684 if (optr
is GC
.addrOf(optr
)) GC
.setAttr(optr
, GC
.BlkAttr
.NO_INTERIOR
);
4692 pragma(inline
, true); // why not?
4693 if (sp
== 0) assert(0, "stack underflow");
4694 if (sp
<= stack
.length
) {
4695 // use "small stack"
4696 return stack
.ptr
[--sp
];
4700 return bigstack
.ptr
[sp
-cast(int)stack
.length
];
4704 version(aabbtree_query_count
) nodesVisited
= nodesDeepVisited
= 0;
4706 // start from root node
4709 // while there are still nodes to visit
4711 // get the next node id to visit
4712 int nodeId
= spop();
4713 // skip it if it is a null node
4714 if (nodeId
== TreeNode
.NullTreeNode
) continue;
4715 version(aabbtree_query_count
) ++nodesVisited
;
4716 // get the corresponding node
4717 TreeNode
* node
= mNodes
+nodeId
;
4718 // should we investigate this node?
4719 if (checker(node
)) {
4720 // if the node is a leaf
4722 // call visitor on it
4723 version(aabbtree_query_count
) ++nodesDeepVisited
;
4724 if (visitor(node
.flesh
)) return nodeId
;
4726 // if the node is not a leaf, we need to visit its children
4727 spush(node
.children
.ptr
[TreeNode
.Left
]);
4728 spush(node
.children
.ptr
[TreeNode
.Right
]);
4737 /// add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
4738 /// extra AABB Gap used to allow the collision shape to move a little bit without triggering a large modification of the tree which can be costly
4739 this (Float extraAABBGap
=FloatNum
!0) nothrow {
4740 mExtraGap
= extraAABBGap
;
4745 import core
.stdc
.stdlib
: free
;
4746 static if (GCAnchor
) gcRelease();
4750 /// return the root AABB of the tree
4751 AABB
getRootAABB () nothrow @trusted @nogc {
4752 pragma(inline
, true);
4753 version(aabbtree_many_asserts
) assert(mRootNodeId
>= 0 && mRootNodeId
< mAllocCount
);
4754 return mNodes
[mRootNodeId
].aabb
;
4757 /// does the given id represents a valid object?
4758 /// WARNING: ids of removed objects can be reused on later insertions!
4759 bool isValidId (int id
) const nothrow @trusted @nogc { pragma(inline
, true); return (id
>= 0 && id
< mAllocCount
&& mNodes
[id
].leaf
); }
4761 /// get current extra AABB gap
4762 @property Float
extraGap () const pure nothrow @trusted @nogc { pragma(inline
, true); return mExtraGap
; }
4764 /// set current extra AABB gap
4765 @property void extraGap (Float aExtraGap
) pure nothrow @trusted @nogc { pragma(inline
, true); mExtraGap
= aExtraGap
; }
4767 /// get object by id; can return null for invalid ids
4768 BodyBase
getObject (int id
) nothrow @trusted @nogc { pragma(inline
, true); return (id
>= 0 && id
< mAllocCount
&& mNodes
[id
].leaf ? mNodes
[id
].flesh
: null); }
4770 /// get fat object AABB by id; returns random shit for invalid ids
4771 AABB
getObjectFatAABB (int id
) nothrow @trusted @nogc { pragma(inline
, true); return (id
>= 0 && id
< mAllocCount
&& !mNodes
[id
].free ? mNodes
[id
].aabb
: AABB()); }
4773 /// insert an object into the tree
4774 /// this method creates a new leaf node in the tree and returns the id of the corresponding node
4775 /// AABB for static object will not be "fat" (simple optimization)
4776 /// WARNING! inserting the same object several times *WILL* break everything!
4777 int insertObject (BodyBase flesh
, bool staticObject
=false) {
4778 auto aabb
= flesh
.getAABB(); // can be passed as argument
4779 int nodeId
= insertObjectInternal(aabb
, staticObject
);
4780 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].leaf
);
4781 mNodes
[nodeId
].flesh
= flesh
;
4782 static if (GCAnchor
) {
4783 import core
.memory
: GC
;
4784 GC
.addRoot(*cast(void**)&flesh
);
4785 GC
.setAttr(*cast(void**)&flesh
, GC
.BlkAttr
.NO_MOVE
);
4790 /// remove an object from the tree
4791 /// WARNING: ids of removed objects can be reused on later insertions!
4792 void removeObject (int nodeId
) {
4793 if (nodeId
< 0 || nodeId
>= mAllocCount ||
!mNodes
[nodeId
].leaf
) assert(0, "invalid node id");
4794 static if (GCAnchor
) {
4795 import core
.memory
: GC
;
4796 auto flesh
= mNodes
[nodeId
].flesh
;
4797 GC
.clrAttr(*cast(void**)&flesh
, GC
.BlkAttr
.NO_MOVE
);
4798 GC
.removeRoot(*cast(void**)&flesh
);
4800 // remove the node from the tree
4801 removeLeafNode(nodeId
);
4802 releaseNode(nodeId
);
4805 /** update the dynamic tree after an object has moved.
4807 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
4808 * otherwise, the corresponding node is removed and reinserted into the tree.
4809 * the method returns true if the object has been reinserted into the tree.
4810 * the "displacement" parameter is the linear velocity of the AABB multiplied by the elapsed time between two frames.
4811 * if the "forceReinsert" parameter is true, we force a removal and reinsertion of the node
4812 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
4814 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
4816 * if `forceReinsert` == `true` and `displacement` is zero, convert object to "static" (don't extrude AABB).
4818 * return `true` if the tree was modified.
4820 bool updateObject() (int nodeId
, in auto ref AABB
.VType displacement
, bool forceReinsert
=false) {
4821 if (nodeId
< 0 || nodeId
>= mAllocCount ||
!mNodes
[nodeId
].leaf
) assert(0, "invalid node id");
4823 auto newAABB
= mNodes
[nodeId
].flesh
.getAABB(); // can be passed as argument
4825 // if the new AABB is still inside the fat AABB of the node
4826 if (!forceReinsert
&& mNodes
[nodeId
].aabb
.contains(newAABB
)) return false;
4828 // if the new AABB is outside the fat AABB, we remove the corresponding node
4829 removeLeafNode(nodeId
);
4831 // compute the fat AABB by inflating the AABB with a constant gap
4832 mNodes
[nodeId
].aabb
= newAABB
;
4833 if (!(forceReinsert
&& displacement
.isZero
)) {
4834 static if (VT
.Dims
== 2) {
4835 immutable gap
= VT(mExtraGap
, mExtraGap
);
4837 immutable gap
= VT(mExtraGap
, mExtraGap
, mExtraGap
);
4839 mNodes
[nodeId
].aabb
.mMin
-= gap
;
4840 mNodes
[nodeId
].aabb
.mMax
+= gap
;
4843 // inflate the fat AABB in direction of the linear motion of the AABB
4844 if (displacement
.x
< FloatNum
!0) {
4845 mNodes
[nodeId
].aabb
.mMin
.x
+= LinearMotionGapMultiplier
*displacement
.x
;
4847 mNodes
[nodeId
].aabb
.mMax
.x
+= LinearMotionGapMultiplier
*displacement
.x
;
4849 if (displacement
.y
< FloatNum
!0) {
4850 mNodes
[nodeId
].aabb
.mMin
.y
+= LinearMotionGapMultiplier
*displacement
.y
;
4852 mNodes
[nodeId
].aabb
.mMax
.y
+= LinearMotionGapMultiplier
*displacement
.y
;
4854 static if (AABB
.VType
.Dims
== 3) {
4855 if (displacement
.z
< FloatNum
!0) {
4856 mNodes
[nodeId
].aabb
.mMin
.z
+= LinearMotionGapMultiplier
*displacement
.z
;
4858 mNodes
[nodeId
].aabb
.mMax
.z
+= LinearMotionGapMultiplier
*displacement
.z
;
4862 version(aabbtree_many_asserts
) assert(mNodes
[nodeId
].aabb
.contains(newAABB
));
4864 // reinsert the node into the tree
4865 insertLeafNode(nodeId
);
4870 /// report all shapes overlapping with the AABB given in parameter
4871 void forEachAABBOverlap() (in auto ref AABB aabb
, scope OverlapCallback cb
) {
4874 (node
) => aabb
.overlaps(node
.aabb
),
4876 (flesh
) { cb(flesh
); return false; }
4880 /// report body that contains the given point
4881 BodyBase
pointQuery() (in auto ref VT point
, scope SimpleQueryCallback cb
) {
4884 (node
) => node
.aabb
.contains(point
),
4886 (flesh
) => cb(flesh
),
4888 version(aabbtree_many_asserts
) assert(nid
< 0 ||
(nid
>= 0 && nid
< mAllocCount
&& mNodes
[nid
].leaf
));
4889 return (nid
>= 0 ? mNodes
[nid
].flesh
: null);
4892 /// report all bodies containing the given point
4893 void pointQuery() (in auto ref VT point
, scope SimpleQueryCallbackNR cb
) {
4896 (node
) => node
.aabb
.contains(point
),
4898 (flesh
) { cb(flesh
); return false; },
4903 static struct SegmentQueryResult
{
4904 Float dist
= -1; /// <0: nothing was hit
4907 @property bool valid () const nothrow @safe @nogc { pragma(inline
, true); return (dist
>= 0 && flesh
!is null); } ///
4910 /// segment querying method
4911 SegmentQueryResult
segmentQuery() (in auto ref VT a
, in auto ref VT b
, scope SegQueryCallback cb
) {
4912 SegmentQueryResult res
;
4913 Float maxFraction
= Float
.infinity
;
4915 immutable VT cura
= a
;
4917 immutable VT dir
= (curb
-cura
).normalized
;
4921 (node
) => node
.aabb
.isIntersects(cura
, curb
),
4924 Float hitFraction
= cb(flesh
, cura
, curb
);
4925 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
4926 if (hitFraction
== FloatNum
!0) {
4931 // if the user returned a positive fraction
4932 if (hitFraction
> FloatNum
!0) {
4933 // we update the maxFraction value and the ray AABB using the new maximum fraction
4934 if (hitFraction
< maxFraction
) {
4935 maxFraction
= hitFraction
;
4936 res
.dist
= hitFraction
;
4939 curb
= cura
+dir
*hitFraction
;
4942 return false; // continue
4949 /// compute the height of the tree
4950 int computeHeight () { pragma(inline
, true); return computeHeight(mRootNodeId
); }
4952 @property int nodeCount () const pure nothrow @safe @nogc { pragma(inline
, true); return mNodeCount
; }
4953 @property int nodeAlloced () const pure nothrow @safe @nogc { pragma(inline
, true); return mAllocCount
; }
4955 /// clear all the nodes and reset the tree
4957 import core
.stdc
.stdlib
: free
;
4958 static if (GCAnchor
) gcRelease();
4966 // ////////////////////////////////////////////////////////////////////////// //
4970 this (vec2 amin, vec2 amax) { aabb.min = amin; aabb.max = amax; }
4972 AABB getAABB () { return aabb; }
4976 // ////////////////////////////////////////////////////////////////////////// //
4980 auto tree = new DynamicAABBTree!Body(0.2);
4982 vec2 bmin = vec2(10, 15);
4983 vec2 bmax = vec2(42, 54);
4985 auto flesh = new Body(bmin, bmax);
4987 tree.insertObject(flesh);
4989 vec2 ro = vec2(5, 18);
4990 vec2 rd = vec2(1, 0.2).normalized;
4993 writeln(flesh.aabb.segIntersectMin(ro, re));
4995 auto res = tree.segmentQuery(ro, re, delegate (flesh, in ref vec2 a, in ref vec2 b) {
4996 auto dst = flesh.aabb.segIntersectMin(a, b);
4997 writeln("a=", a, "; b=", b, "; dst=", dst);
4998 if (dst < 0) return -1;