1 /** 2 inmath.linalg 3 4 Special thanks to: 5 $(UL 6 $(LI Tomasz Stachowiak (h3r3tic): allowed me to use parts of $(LINK2 https://bitbucket.org/h3r3tic/boxen/src/default/src/xf/omg, omg).) 7 $(LI Jakob Øvrum (jA_cOp): improved the code a lot!) 8 $(LI Florian Boesch (___doc__): helps me to understand opengl/complex maths better, see: $(LINK http://codeflow.org/).) 9 $(LI #D on freenode: answered general questions about D.) 10 ) 11 12 Authors: David Herberth, Inochi2D Project 13 License: MIT 14 15 Note: All methods marked with pure are weakly pure since, they all access an instance member. 16 All static methods are strongly pure. 17 */ 18 19 20 module inmath.linalg; 21 22 private { 23 import std.math : isNaN, isInfinity; 24 import std.conv : to; 25 import std.traits : isIntegral, isFloatingPoint, isStaticArray, isDynamicArray, isImplicitlyConvertible, isArray; 26 import std.string : format, rightJustify; 27 import std.array : join; 28 import std.algorithm : max, min, reduce; 29 import inmath.math : clamp, PI, sqrt, sin, cos, acos, tan, asin, atan2, almostEqual; 30 import inmath.util : isVector, isMatrix, isQuaternion, isRect, TupleRange; 31 } 32 33 version(NoReciprocalMul) { 34 private enum rmul = false; 35 } else { 36 private enum rmul = true; 37 } 38 39 /// Base template for all vector-types. 40 /// Params: 41 /// type = all values get stored as this type 42 /// dimension = specifies the dimension of the vector, can be 1, 2, 3 or 4 43 /// Examples: 44 /// --- 45 /// alias Vector!(int, 3) vec3i; 46 /// alias Vector!(float, 4) vec4; 47 /// alias Vector!(real, 2) vec2r; 48 /// --- 49 struct Vector(type, int dimension_) { 50 static assert(dimension > 0, "0 dimensional vectors don't exist."); 51 52 alias vt = type; /// Holds the internal type of the vector. 53 static const int dimension = dimension_; ///Holds the dimension of the vector. 54 55 vt[dimension] vector; /// Holds all coordinates, length conforms dimension. 56 57 /// Returns a pointer to the coordinates. 58 auto ptr() const { return vector.ptr; } 59 60 /// Returns the current vector formatted as string, useful for printing the vector. 61 const(string) toString() const { 62 return format("%s", vector); 63 } 64 65 /// Gets a hash of this item 66 size_t toHash() const { return typeid(this).getHash(&this); } 67 68 @safe pure nothrow: 69 70 /** 71 A vector with all zeroes 72 */ 73 static auto zero() { 74 return Vector!(type, dimension_)(0); 75 } 76 77 /** 78 A vector with all ones 79 */ 80 static auto one() { 81 return Vector!(type, dimension_)(1); 82 } 83 84 /** 85 Up vector (0, -1) OR (0, 1, 0) 86 */ 87 static auto up() { 88 static if (dimension_ == 2) return Vector!(type, dimension_)(cast(type)0, cast(type)-1); 89 else static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)0, cast(type)1, cast(type)0); 90 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)0, cast(type)1, cast(type)0, cast(type)0); 91 else return Vector!(type, dimension_).init; 92 } 93 94 /** 95 Down vector (0, 1) OR (0, -1, 0) 96 */ 97 static auto down() { 98 static if (dimension_ == 2) return Vector!(type, dimension_)(cast(type)0, cast(type)1); 99 else static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)0, cast(type)-1, cast(type)0); 100 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)0, cast(type)-1, cast(type)0, cast(type)0); 101 else return Vector!(type, dimension_).init; 102 } 103 104 /** 105 Left vector (-1, 0, 0) 106 */ 107 static auto left() { 108 static if (dimension_ == 2) return Vector!(type, dimension_)(cast(type)-1, cast(type)0); 109 else static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)-1, cast(type)0, cast(type)0); 110 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)-1, cast(type)0, cast(type)0, cast(type)0); 111 else return Vector!(type, dimension_).init; 112 } 113 114 /** 115 Right vector (1, 0, 0) 116 */ 117 static auto right() { 118 static if (dimension_ == 2) return Vector!(type, dimension_)(cast(type)1, cast(type)0); 119 else static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)1, cast(type)0, cast(type)0); 120 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)1, cast(type)0, cast(type)0, cast(type)0); 121 else return Vector!(type, dimension_).init; 122 } 123 124 static if (dimension_ >= 3) { 125 126 /** 127 Forward vector (0, 0, 1) 128 */ 129 static auto forward() { 130 static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)0, cast(type)0, cast(type)1); 131 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)0, cast(type)0, cast(type)1, cast(type)0); 132 else return Vector!(type, dimension_).init; 133 } 134 135 /** 136 Back vector (0, 0, -1) 137 */ 138 static auto back() { 139 static if (dimension_ == 3) return Vector!(type, dimension_)(cast(type)0, cast(type)0, cast(type)-1); 140 else static if (dimension_ == 4) return Vector!(type, dimension_)(cast(type)0, cast(type)0, cast(type)-1, cast(type)0); 141 else return Vector!(type, dimension_).init; 142 } 143 } 144 145 /// 146 ref inout(vt) get_(char coord)() inout { 147 return vector[coordToIndex!coord]; 148 } 149 150 alias x = get_!'x'; /// static properties to access the values. 151 alias u = x; /// ditto 152 alias s = x; /// ditto 153 alias r = x; /// ditto 154 static if(dimension >= 2) { 155 alias y = get_!'y'; /// ditto 156 alias v = y; /// ditto 157 alias t = y; /// ditto 158 alias g = y; /// ditto 159 } 160 static if(dimension >= 3) { 161 alias z = get_!'z'; /// ditto 162 alias b = z; /// ditto 163 alias p = z; /// ditto 164 } 165 static if(dimension >= 4) { 166 alias w = get_!'w'; /// ditto 167 alias a = w; /// ditto 168 alias q = w; /// ditto 169 } 170 171 static if(dimension == 2) { 172 enum Vector e1 = Vector(1.to!vt, 0.to!vt); /// canonical basis for Euclidian space 173 enum Vector e2 = Vector(0.to!vt, 1.to!vt); /// ditto 174 } else static if(dimension == 3) { 175 enum Vector e1 = Vector(1.to!vt, 0.to!vt, 0.to!vt); /// canonical basis for Euclidian space 176 enum Vector e2 = Vector(0.to!vt, 1.to!vt, 0.to!vt); /// ditto 177 enum Vector e3 = Vector(0.to!vt, 0.to!vt, 1.to!vt); /// ditto 178 } else static if(dimension == 4) { 179 enum Vector e1 = Vector(1.to!vt, 0.to!vt, 0.to!vt, 0.to!vt); /// canonical basis for Euclidian space 180 enum Vector e2 = Vector(0.to!vt, 1.to!vt, 0.to!vt, 0.to!vt); /// ditto 181 enum Vector e3 = Vector(0.to!vt, 0.to!vt, 1.to!vt, 0.to!vt); /// ditto 182 enum Vector e4 = Vector(0.to!vt, 0.to!vt, 0.to!vt, 1.to!vt); /// ditto 183 } 184 185 unittest { 186 assert(vec2.e1.vector == [1.0, 0.0]); 187 assert(vec2.e2.vector == [0.0, 1.0]); 188 189 assert(vec3.e1.vector == [1.0, 0.0, 0.0]); 190 assert(vec3.e2.vector == [0.0, 1.0, 0.0]); 191 assert(vec3.e3.vector == [0.0, 0.0, 1.0]); 192 193 assert(vec4.e1.vector == [1.0, 0.0, 0.0, 0.0]); 194 assert(vec4.e2.vector == [0.0, 1.0, 0.0, 0.0]); 195 assert(vec4.e3.vector == [0.0, 0.0, 1.0, 0.0]); 196 assert(vec4.e4.vector == [0.0, 0.0, 0.0, 1.0]); 197 } 198 199 static void isCompatibleVectorImpl(int d)(Vector!(vt, d) vec) if(d <= dimension) { 200 } 201 202 template isCompatibleVector(T) { 203 enum isCompatibleVector = is(typeof(isCompatibleVectorImpl(T.init))); 204 } 205 206 static void isCompatibleMatrixImpl(int r, int c)(Matrix!(vt, r, c) m) { 207 } 208 209 template isCompatibleMatrix(T) { 210 enum isCompatibleMatrix = is(typeof(isCompatibleMatrixImpl(T.init))); 211 } 212 213 private void construct(int i, T, Tail...)(T head, Tail tail) { 214 static if(i >= dimension) { 215 static assert(false, "Too many arguments passed to constructor"); 216 } else static if(is(T : vt)) { 217 vector[i] = head; 218 construct!(i + 1)(tail); 219 } else static if(isDynamicArray!T) { 220 static assert((Tail.length == 0) && (i == 0), "dynamic array can not be passed together with other arguments"); 221 vector[] = head[]; 222 } else static if(isStaticArray!T) { 223 vector[i .. i + T.length] = head[]; 224 construct!(i + T.length)(tail); 225 } else static if(isCompatibleVector!T) { 226 vector[i .. i + T.dimension] = head.vector[]; 227 construct!(i + T.dimension)(tail); 228 } else { 229 static assert(false, "Vector constructor argument must be of type " ~ vt.stringof ~ " or Vector, not " ~ T.stringof); 230 } 231 } 232 233 private void construct(int i)() { // terminate 234 static assert(i == dimension, "Not enough arguments passed to constructor"); 235 } 236 237 /// Constructs the vector. 238 /// If a single value is passed the vector, the vector will be cleared with this value. 239 /// If a vector with a higher dimension is passed the vector will hold the first values up to its dimension. 240 /// If mixed types are passed they will be joined together (allowed types: vector, static array, $(I vt)). 241 /// Examples: 242 /// --- 243 /// vec4 v4 = vec4(1.0f, vec2(2.0f, 3.0f), 4.0f); 244 /// vec3 v3 = vec3(v4); // v3 = vec3(1.0f, 2.0f, 3.0f); 245 /// vec2 v2 = v3.xy; // swizzling returns a static array. 246 /// vec3 v3_2 = vec3(1.0f); // vec3 v3_2 = vec3(1.0f, 1.0f, 1.0f); 247 /// --- 248 this(Args...)(Args args) { 249 construct!(0)(args); 250 } 251 252 /// ditto 253 this(T)(T vec) if(isVector!T && is(T.vt : vt) && (T.dimension >= dimension)) { 254 foreach(i; TupleRange!(0, dimension)) { 255 vector[i] = vec.vector[i]; 256 } 257 } 258 259 /// ditto 260 this()(vt value) { 261 clear(value); 262 } 263 264 /// Returns true if all values are not nan and finite, otherwise false. 265 bool isFinite() const { 266 static if(isIntegral!type) { 267 return true; 268 } 269 else { 270 foreach(v; vector) { 271 if(isNaN(v) || isInfinity(v)) { 272 return false; 273 } 274 } 275 return true; 276 } 277 } 278 279 /// Sets all values of the vector to value. 280 void clear(vt value) { 281 foreach(i; TupleRange!(0, dimension)) { 282 vector[i] = value; 283 } 284 } 285 286 unittest { 287 vec3 vec_clear; 288 assert(!vec_clear.isFinite); 289 vec_clear.clear(1.0f); 290 assert(vec_clear.isFinite); 291 assert(vec_clear.vector == [1.0f, 1.0f, 1.0f]); 292 assert(vec_clear.vector == vec3(1.0f).vector); 293 vec_clear.clear(float.infinity); 294 assert(!vec_clear.isFinite); 295 vec_clear.clear(float.nan); 296 assert(!vec_clear.isFinite); 297 vec_clear.clear(1.0f); 298 assert(vec_clear.isFinite); 299 300 vec4 b = vec4(1.0f, vec_clear); 301 assert(b.isFinite); 302 assert(b.vector == [1.0f, 1.0f, 1.0f, 1.0f]); 303 assert(b.vector == vec4(1.0f).vector); 304 305 vec2 v2_1 = vec2(vec2(0.0f, 1.0f)); 306 assert(v2_1.vector == [0.0f, 1.0f]); 307 308 vec2 v2_2 = vec2(1.0f, 1.0f); 309 assert(v2_2.vector == [1.0f, 1.0f]); 310 311 vec3 v3 = vec3(v2_1, 2.0f); 312 assert(v3.vector == [0.0f, 1.0f, 2.0f]); 313 314 vec4 v4_1 = vec4(1.0f, vec2(2.0f, 3.0f), 4.0f); 315 assert(v4_1.vector == [1.0f, 2.0f, 3.0f, 4.0f]); 316 assert(vec3(v4_1).vector == [1.0f, 2.0f, 3.0f]); 317 assert(vec2(vec3(v4_1)).vector == [1.0f, 2.0f]); 318 assert(vec2(vec3(v4_1)).vector == vec2(v4_1).vector); 319 assert(v4_1.vector == vec4([1.0f, 2.0f, 3.0f, 4.0f]).vector); 320 321 vec4 v4_2 = vec4(vec2(1.0f, 2.0f), vec2(3.0f, 4.0f)); 322 assert(v4_2.vector == [1.0f, 2.0f, 3.0f, 4.0f]); 323 assert(vec3(v4_2).vector == [1.0f, 2.0f, 3.0f]); 324 assert(vec2(vec3(v4_2)).vector == [1.0f, 2.0f]); 325 assert(vec2(vec3(v4_2)).vector == vec2(v4_2).vector); 326 assert(v4_2.vector == vec4([1.0f, 2.0f, 3.0f, 4.0f]).vector); 327 328 float[2] f2 = [1.0f, 2.0f]; 329 float[3] f3 = [1.0f, 2.0f, 3.0f]; 330 float[4] f4 = [1.0f, 2.0f, 3.0f, 4.0f]; 331 assert(vec2(1.0f, 2.0f).vector == vec2(f2).vector); 332 assert(vec3(1.0f, 2.0f, 3.0f).vector == vec3(f3).vector); 333 assert(vec3(1.0f, 2.0f, 3.0f).vector == vec3(f2, 3.0f).vector); 334 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f).vector == vec4(f4).vector); 335 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f).vector == vec4(f3, 4.0f).vector); 336 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f).vector == vec4(f2, 3.0f, 4.0f).vector); 337 // useful for: "vec4 v4 = […]" or "vec4 v4 = other_vector.rgba" 338 339 assert(vec3(vec3i(1, 2, 3)) == vec3(1.0, 2.0, 3.0)); 340 assert(vec3d(vec3(1.0, 2.0, 3.0)) == vec3d(1.0, 2.0, 3.0)); 341 342 static assert(!__traits(compiles, vec3(0.0f, 0.0f))); 343 static assert(!__traits(compiles, vec4(0.0f, 0.0f, 0.0f))); 344 static assert(!__traits(compiles, vec4(0.0f, vec2(0.0f, 0.0f)))); 345 static assert(!__traits(compiles, vec4(vec3(0.0f, 0.0f, 0.0f)))); 346 } 347 348 template coordToIndex(char c) { 349 static if((c == 'x') || (c == 'r') || (c == 'u') || (c == 's')) { 350 enum coordToIndex = 0; 351 } else static if((c == 'y') || (c == 'g') || (c == 'v') || (c == 't')) { 352 enum coordToIndex = 1; 353 } else static if((c == 'z') || (c == 'b') || (c == 'p')) { 354 static assert(dimension >= 3, "the " ~ c ~ " property is only available on vectors with a third dimension."); 355 enum coordToIndex = 2; 356 } else static if((c == 'w') || (c == 'a') || (c == 'q')) { 357 static assert(dimension >= 4, "the " ~ c ~ " property is only available on vectors with a fourth dimension."); 358 enum coordToIndex = 3; 359 } else { 360 static assert(false, "accepted coordinates are x, s, r, u, y, g, t, v, z, p, b, w, q and a not " ~ c ~ "."); 361 } 362 } 363 364 static if(dimension == 2) { void set(vt x, vt y) { vector[0] = x; vector[1] = y; } } 365 static if(dimension == 3) { void set(vt x, vt y, vt z) { vector[0] = x; vector[1] = y; vector[2] = z; } } 366 static if(dimension == 4) { void set(vt x, vt y, vt z, vt w) { vector[0] = x; vector[1] = y; vector[2] = z; vector[3] = w; } } 367 368 /// Updates the vector with the values from other. 369 void update(Vector!(vt, dimension) other) { 370 vector = other.vector; 371 } 372 373 unittest { 374 vec2 v2 = vec2(1.0f, 2.0f); 375 assert(v2.x == 1.0f); 376 assert(v2.y == 2.0f); 377 v2.x = 3.0f; 378 v2.x += 1; 379 v2.x -= 1; 380 assert(v2.vector == [3.0f, 2.0f]); 381 v2.y = 4.0f; 382 v2.y += 1; 383 v2.y -= 1; 384 assert(v2.vector == [3.0f, 4.0f]); 385 assert((v2.x == 3.0f) && (v2.x == v2.u) && (v2.x == v2.s) && (v2.x == v2.r)); 386 assert(v2.y == 4.0f); 387 assert((v2.y == 4.0f) && (v2.y == v2.v) && (v2.y == v2.t) && (v2.y == v2.g)); 388 v2.set(0.0f, 1.0f); 389 assert(v2.vector == [0.0f, 1.0f]); 390 v2.update(vec2(3.0f, 4.0f)); 391 assert(v2.vector == [3.0f, 4.0f]); 392 393 vec3 v3 = vec3(1.0f, 2.0f, 3.0f); 394 assert(v3.x == 1.0f); 395 assert(v3.y == 2.0f); 396 assert(v3.z == 3.0f); 397 v3.x = 3.0f; 398 v3.x += 1; 399 v3.x -= 1; 400 assert(v3.vector == [3.0f, 2.0f, 3.0f]); 401 v3.y = 4.0f; 402 v3.y += 1; 403 v3.y -= 1; 404 assert(v3.vector == [3.0f, 4.0f, 3.0f]); 405 v3.z = 5.0f; 406 v3.z += 1; 407 v3.z -= 1; 408 assert(v3.vector == [3.0f, 4.0f, 5.0f]); 409 assert((v3.x == 3.0f) && (v3.x == v3.s) && (v3.x == v3.r)); 410 assert((v3.y == 4.0f) && (v3.y == v3.t) && (v3.y == v3.g)); 411 assert((v3.z == 5.0f) && (v3.z == v3.p) && (v3.z == v3.b)); 412 v3.set(0.0f, 1.0f, 2.0f); 413 assert(v3.vector == [0.0f, 1.0f, 2.0f]); 414 v3.update(vec3(3.0f, 4.0f, 5.0f)); 415 assert(v3.vector == [3.0f, 4.0f, 5.0f]); 416 417 vec4 v4 = vec4(1.0f, 2.0f, vec2(3.0f, 4.0f)); 418 assert(v4.x == 1.0f); 419 assert(v4.y == 2.0f); 420 assert(v4.z == 3.0f); 421 assert(v4.w == 4.0f); 422 v4.x = 3.0f; 423 v4.x += 1; 424 v4.x -= 1; 425 assert(v4.vector == [3.0f, 2.0f, 3.0f, 4.0f]); 426 v4.y = 4.0f; 427 v4.y += 1; 428 v4.y -= 1; 429 assert(v4.vector == [3.0f, 4.0f, 3.0f, 4.0f]); 430 v4.z = 5.0f; 431 v4.z += 1; 432 v4.z -= 1; 433 assert(v4.vector == [3.0f, 4.0f, 5.0f, 4.0f]); 434 v4.w = 6.0f; 435 v4.w += 1; 436 v4.w -= 1; 437 assert(v4.vector == [3.0f, 4.0f, 5.0f, 6.0f]); 438 assert((v4.x == 3.0f) && (v4.x == v4.s) && (v4.x == v4.r)); 439 assert((v4.y == 4.0f) && (v4.y == v4.t) && (v4.y == v4.g)); 440 assert((v4.z == 5.0f) && (v4.z == v4.p) && (v4.z == v4.b)); 441 assert((v4.w == 6.0f) && (v4.w == v4.q) && (v4.w == v4.a)); 442 v4.set(0.0f, 1.0f, 2.0f, 3.0f); 443 assert(v4.vector == [0.0f, 1.0f, 2.0f, 3.0f]); 444 v4.update(vec4(3.0f, 4.0f, 5.0f, 6.0f)); 445 assert(v4.vector == [3.0f, 4.0f, 5.0f, 6.0f]); 446 } 447 448 private void dispatchImpl(int i, string s, int size)(ref vt[size] result) const { 449 static if(s.length > 0) { 450 result[i] = vector[coordToIndex!(s[0])]; 451 dispatchImpl!(i + 1, s[1..$])(result); 452 } 453 } 454 455 /// Implements dynamic swizzling. 456 /// Returns: a Vector 457 Vector!(vt, s.length) opDispatch(string s)() const { 458 vt[s.length] ret; 459 dispatchImpl!(0, s)(ret); 460 Vector!(vt, s.length) ret_vec; 461 ret_vec.vector = ret; 462 return ret_vec; 463 } 464 465 unittest { 466 vec2 v2 = vec2(1.0f, 2.0f); 467 assert(v2.xytsy == [1.0f, 2.0f, 2.0f, 1.0f, 2.0f]); 468 469 assert(vec3(1.0f, 2.0f, 3.0f).xybzyr == [1.0f, 2.0f, 3.0f, 3.0f, 2.0f, 1.0f]); 470 assert(vec4(v2, 3.0f, 4.0f).xyzwrgbastpq == [1.0f, 2.0f, 3.0f, 4.0f, 471 1.0f, 2.0f, 3.0f, 4.0f, 472 1.0f, 2.0f, 3.0f, 4.0f]); 473 assert(vec4(v2, 3.0f, 4.0f).wgyzax == [4.0f, 2.0f, 2.0f, 3.0f, 4.0f, 1.0f]); 474 assert(vec4(v2.xyst).vector == [1.0f, 2.0f, 1.0f, 2.0f]); 475 } 476 477 /// Returns the squared magnitude of the vector. 478 real lengthSquared() const { 479 real temp = 0; 480 481 foreach(index; TupleRange!(0, dimension)) { 482 temp += vector[index]^^2; 483 } 484 485 return temp; 486 } 487 488 /// Returns the magnitude of the vector. 489 real length() const { 490 return sqrt(lengthSquared); 491 } 492 493 /// Normalizes the vector. 494 void normalize() { 495 real len = length; 496 497 if(len != 0) { 498 foreach(index; TupleRange!(0, dimension)) { 499 vector[index] = cast(type)(vector[index]/len); 500 } 501 } 502 } 503 504 /// Returns a normalized copy of the current vector. 505 Vector normalized() const { 506 Vector ret; 507 ret.update(this); 508 ret.normalize(); 509 return ret; 510 } 511 512 Vector opUnary(string op : "-")() const { 513 Vector ret; 514 515 foreach(index; TupleRange!(0, dimension)) { 516 ret.vector[index] = -vector[index]; 517 } 518 519 return ret; 520 } 521 522 unittest { 523 assert(vec2(1.0f, 1.0f) == -vec2(-1.0f, -1.0f)); 524 assert(vec2(-1.0f, 1.0f) == -vec2(1.0f, -1.0f)); 525 526 assert(-vec3(1.0f, 1.0f, 1.0f) == vec3(-1.0f, -1.0f, -1.0f)); 527 assert(-vec3(-1.0f, 1.0f, -1.0f) == vec3(1.0f, -1.0f, 1.0f)); 528 529 assert(vec4(1.0f, 1.0f, 1.0f, 1.0f) == -vec4(-1.0f, -1.0f, -1.0f, -1.0f)); 530 assert(vec4(-1.0f, 1.0f, -1.0f, 1.0f) == -vec4(1.0f, -1.0f, 1.0f, -1.0f)); 531 } 532 533 // let the math begin! 534 Vector opBinary(string op : "*")(vt r) const { 535 Vector ret; 536 537 static foreach(index; TupleRange!(0, dimension)) { 538 ret.vector[index] = vector[index] * r; 539 } 540 541 return ret; 542 } 543 544 Vector opBinary(string op : "/")(vt r) const { 545 Vector ret; 546 547 static foreach(index; TupleRange!(0, dimension)) { 548 ret.vector[index] = cast(vt)(vector[index] / r); 549 } 550 551 return ret; 552 } 553 554 Vector opBinary(string op)(Vector r) const if((op == "+") || (op == "-")) { 555 Vector ret; 556 557 static foreach(index; TupleRange!(0, dimension)) { 558 ret.vector[index] = mixin("cast(vt)(vector[index]" ~ op ~ "r.vector[index])"); 559 } 560 561 return ret; 562 } 563 564 Vector opBinary(string op : "*")(Vector r) const { 565 Vector ret; 566 567 static foreach(index; 0..dimension) { 568 ret.vector[index] = vector[index] * r.vector[index]; 569 } 570 571 return ret; 572 } 573 574 // vector * matrix (for matrix * vector -> struct Matrix) 575 Vector!(vt, T.cols) opBinary(string op : "*", T)(T inp) const if(isCompatibleMatrix!T && (T.rows == dimension)) { 576 Vector!(vt, T.cols) ret; 577 ret.clear(0); 578 579 foreach(c; TupleRange!(0, T.cols)) { 580 foreach(r; TupleRange!(0, T.rows)) { 581 ret.vector[c] += vector[r] * inp.matrix[r][c]; 582 } 583 } 584 585 return ret; 586 } 587 588 auto opBinaryRight(string op, T)(T inp) const if(!isVector!T && !isMatrix!T && !isQuaternion!T) { 589 return this.opBinary!(op)(inp); 590 } 591 592 unittest { 593 // vec2 v2 = vec2(1.0f, 3.0f); 594 // auto v2times2 = 2 * v2; 595 // assert((v2*2.5f).vector == [2.5f, 7.5f]); 596 // assert((v2+vec2(3.0f, 1.0f)).vector == [4.0f, 4.0f]); 597 // assert((v2-vec2(1.0f, 3.0f)).vector == [0.0f, 0.0f]); 598 // assert((v2*vec2(2.0f, 2.0f)) == 8.0f); 599 600 // vec3 v3 = vec3(1.0f, 3.0f, 5.0f); 601 // assert((v3*2.5f).vector == [2.5f, 7.5f, 12.5f]); 602 // assert((v3+vec3(3.0f, 1.0f, -1.0f)).vector == [4.0f, 4.0f, 4.0f]); 603 // assert((v3-vec3(1.0f, 3.0f, 5.0f)).vector == [0.0f, 0.0f, 0.0f]); 604 // assert((v3*vec3(2.0f, 2.0f, 2.0f)) == 18.0f); 605 606 // vec4 v4 = vec4(1.0f, 3.0f, 5.0f, 7.0f); 607 // assert((v4*2.5f).vector == [2.5f, 7.5f, 12.5f, 17.5]); 608 // assert((v4+vec4(3.0f, 1.0f, -1.0f, -3.0f)).vector == [4.0f, 4.0f, 4.0f, 4.0f]); 609 // assert((v4-vec4(1.0f, 3.0f, 5.0f, 7.0f)).vector == [0.0f, 0.0f, 0.0f, 0.0f]); 610 // assert((v4*vec4(2.0f, 2.0f, 2.0f, 2.0f)) == 32.0f); 611 612 // mat2 m2 = mat2(1.0f, 2.0f, 3.0f, 4.0f); 613 // vec2 v2_2 = vec2(2.0f, 2.0f); 614 // assert((v2_2*m2).vector == [8.0f, 12.0f]); 615 616 // mat3 m3 = mat3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f); 617 // vec3 v3_2 = vec3(2.0f, 2.0f, 2.0f); 618 // assert((v3_2*m3).vector == [24.0f, 30.0f, 36.0f]); 619 } 620 621 void opOpAssign(string op : "*")(vt r) { 622 foreach(index; TupleRange!(0, dimension)) { 623 vector[index] *= r; 624 } 625 } 626 627 void opOpAssign(string op : "/")(vt r) { 628 foreach(index; TupleRange!(0, dimension)) { 629 vector[index] /= r; 630 } 631 } 632 633 void opOpAssign(string op)(Vector r) if((op == "+") || (op == "-")) { 634 foreach(index; TupleRange!(0, dimension)) { 635 mixin("vector[index]" ~ op ~ "= r.vector[index];"); 636 } 637 } 638 639 unittest { 640 vec2 v2 = vec2(1.0f, 3.0f); 641 v2 *= 2.5f; 642 assert(v2.vector == [2.5f, 7.5f]); 643 v2 -= vec2(2.5f, 7.5f); 644 assert(v2.vector == [0.0f, 0.0f]); 645 v2 += vec2(1.0f, 3.0f); 646 assert(v2.vector == [1.0f, 3.0f]); 647 assert(almostEqual(v2.length, sqrt(10.0f))); 648 assert(v2.lengthSquared == 10.0f); 649 assert((v2.length == v2.length) && (v2.lengthSquared == v2.lengthSquared)); 650 v2 /= 2.0f; 651 assert(v2.vector == [0.5f, 1.5f]); 652 assert(almostEqual(v2.normalized, vec2(1.0f/sqrt(10.0f), 3.0f/sqrt(10.0f)))); 653 654 vec3 v3 = vec3(1.0f, 3.0f, 5.0f); 655 v3 *= 2.5f; 656 assert(v3.vector == [2.5f, 7.5f, 12.5f]); 657 v3 -= vec3(2.5f, 7.5f, 12.5f); 658 assert(v3.vector == [0.0f, 0.0f, 0.0f]); 659 v3 += vec3(1.0f, 3.0f, 5.0f); 660 assert(v3.vector == [1.0f, 3.0f, 5.0f]); 661 assert(almostEqual(v3.length, sqrt(35.0f))); 662 assert(v3.lengthSquared == 35.0f); 663 assert((v3.length == v3.length) && (v3.lengthSquared == v3.lengthSquared)); 664 v3 /= 2.0f; 665 assert(v3.vector == [0.5f, 1.5f, 2.5f]); 666 assert(almostEqual(v3.normalized, vec3(1.0f/sqrt(35.0f), 3.0f/sqrt(35.0f), 5.0f/sqrt(35.0f)))); 667 668 vec4 v4 = vec4(1.0f, 3.0f, 5.0f, 7.0f); 669 v4 *= 2.5f; 670 assert(v4.vector == [2.5f, 7.5f, 12.5f, 17.5]); 671 v4 -= vec4(2.5f, 7.5f, 12.5f, 17.5f); 672 assert(v4.vector == [0.0f, 0.0f, 0.0f, 0.0f]); 673 v4 += vec4(1.0f, 3.0f, 5.0f, 7.0f); 674 assert(v4.vector == [1.0f, 3.0f, 5.0f, 7.0f]); 675 assert(almostEqual(v4.length, sqrt(84.0f))); 676 assert(v4.lengthSquared == 84.0f); 677 assert((v4.length == v4.length) && (v4.lengthSquared == v4.lengthSquared)); 678 v4 /= 2.0f; 679 assert(v4.vector == [0.5f, 1.5f, 2.5f, 3.5f]); 680 assert(almostEqual(v4.normalized, vec4(1.0f/sqrt(84.0f), 3.0f/sqrt(84.0f), 5.0f/sqrt(84.0f), 7.0f/sqrt(84.0f)))); 681 } 682 683 int opCmp(ref const Vector vec) const { 684 foreach(i, a; vector) { 685 if(a < vec.vector[i]) { 686 return -1; 687 } else if(a > vec.vector[i]) { 688 return 1; 689 } 690 } 691 692 // Vectors are the same 693 return 0; 694 } 695 696 bool opEquals(T)(const T vec) const if(!isArray!T && T.dimension == dimension) { 697 return vector == vec.vector; 698 } 699 700 bool opEquals(T)(const(T)[] array) const if(!isArray!T && !isVector!T) { 701 if(array.length != dimension) { 702 return false; 703 } 704 705 foreach(index; TupleRange!(0, dimension)) { 706 if(vector[index] != array[index]) { 707 return false; 708 } 709 } 710 711 return true; 712 } 713 714 unittest { 715 assert(vec2(1.0f, 2.0f) == vec2(1.0f, 2.0f)); 716 assert(vec2(1.0f, 2.0f) != vec2(1.0f, 1.0f)); 717 assert(vec2(1.0f, 2.0f) == vec2d(1.0, 2.0)); 718 assert(vec2(1.0f, 2.0f) != vec2d(1.0, 1.0)); 719 assert(vec2(1.0f, 2.0f) == vec2(1.0f, 2.0f).vector); 720 assert(vec2(1.0f, 2.0f) != vec2(1.0f, 1.0f).vector); 721 assert(vec2(1.0f, 2.0f) == vec2d(1.0, 2.0).vector); 722 assert(vec2(1.0f, 2.0f) != vec2d(1.0, 1.0).vector); 723 724 assert(vec3(1.0f, 2.0f, 3.0f) == vec3(1.0f, 2.0f, 3.0f)); 725 assert(vec3(1.0f, 2.0f, 3.0f) != vec3(1.0f, 2.0f, 2.0f)); 726 assert(vec3(1.0f, 2.0f, 3.0f) == vec3d(1.0, 2.0, 3.0)); 727 assert(vec3(1.0f, 2.0f, 3.0f) != vec3d(1.0, 2.0, 2.0)); 728 assert(vec3(1.0f, 2.0f, 3.0f) == vec3(1.0f, 2.0f, 3.0f).vector); 729 assert(vec3(1.0f, 2.0f, 3.0f) != vec3(1.0f, 2.0f, 2.0f).vector); 730 assert(vec3(1.0f, 2.0f, 3.0f) == vec3d(1.0, 2.0, 3.0).vector); 731 assert(vec3(1.0f, 2.0f, 3.0f) != vec3d(1.0, 2.0, 2.0).vector); 732 733 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) == vec4(1.0f, 2.0f, 3.0f, 4.0f)); 734 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) != vec4(1.0f, 2.0f, 3.0f, 3.0f)); 735 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) == vec4d(1.0, 2.0, 3.0, 4.0)); 736 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) != vec4d(1.0, 2.0, 3.0, 3.0)); 737 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) == vec4(1.0f, 2.0f, 3.0f, 4.0f).vector); 738 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) != vec4(1.0f, 2.0f, 3.0f, 3.0f).vector); 739 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) == vec4d(1.0, 2.0, 3.0, 4.0).vector); 740 assert(vec4(1.0f, 2.0f, 3.0f, 4.0f) != vec4d(1.0, 2.0, 3.0, 3.0).vector); 741 742 assert(!vec4(float.nan).isFinite); 743 if(vec4(1.0f).isFinite) { } 744 else { assert(false); } 745 } 746 } 747 748 /// Calculates the product between two vectors. 749 T.vt dot(T)(const T veca, const T vecb) @safe pure nothrow if(isVector!T) { 750 T.vt temp = 0; 751 752 foreach(index; TupleRange!(0, T.dimension)) { 753 temp += veca.vector[index] * vecb.vector[index]; 754 } 755 756 return temp; 757 } 758 759 /// Calculates the cross product of two 3-dimensional vectors. 760 T cross(T)(const T veca, const T vecb) @safe pure nothrow if(isVector!T && (T.dimension == 3)) { 761 return T(veca.y * vecb.z - vecb.y * veca.z, 762 veca.z * vecb.x - vecb.z * veca.x, 763 veca.x * vecb.y - vecb.x * veca.y); 764 } 765 766 /// Calculates the distance between two vectors. 767 T.vt distance(T)(const T veca, const T vecb) @safe pure nothrow if(isVector!T) { 768 return (veca - vecb).length; 769 } 770 771 @("vector dot product") 772 unittest { 773 // dot is already tested in Vector.opBinary, so no need for testing with more vectors 774 vec3 v1 = vec3(1.0f, 2.0f, -3.0f); 775 vec3 v2 = vec3(1.0f, 3.0f, 2.0f); 776 777 assert(dot(v1, v2) == 1.0f); 778 assert(dot(v1, v2) == dot(v2, v1)); 779 assert((v1 * v2) == (v1 * v2)); 780 781 assert(cross(v1, v2).vector == [13.0f, -5.0f, 1.0f]); 782 assert(cross(v2, v1).vector == [-13.0f, 5.0f, -1.0f]); 783 784 assert(distance(vec2(0.0f, 0.0f), vec2(0.0f, 10.0f)) == 10.0); 785 } 786 787 /// reflect a vector using a surface normal 788 T reflect(T)(const T vec, const T norm) @safe pure nothrow if(isVector!T) { 789 return (2 * (vec * norm) * norm) - vec; 790 } 791 792 @("vector reflect") 793 unittest 794 { 795 assert(vec2(1,1).reflect(vec2(0,1)) == vec2(-1,1)); 796 assert(vec2(-1,1).reflect(vec2(0,1)) == vec2(1,1)); 797 assert(vec2(2,1).reflect(vec2(0,1)) == vec2(-2,1)); 798 799 assert(vec3(1,1,1).reflect(vec3(0,1,0)) == vec3(-1,1,-1)); 800 } 801 802 /// Pre-defined vector types, the number represents the dimension and the last letter the type (none = float, d = double, i = int). 803 alias vec2 = Vector!(float, 2); 804 alias vec3 = Vector!(float, 3); /// ditto 805 alias vec4 = Vector!(float, 4); /// ditto 806 807 alias vec2d = Vector!(double, 2); /// ditto 808 alias vec3d = Vector!(double, 3); /// ditto 809 alias vec4d = Vector!(double, 4); /// ditto 810 811 alias vec2i = Vector!(int, 2); /// ditto 812 alias vec3i = Vector!(int, 3); /// ditto 813 alias vec4i = Vector!(int, 4); /// ditto 814 815 alias vec2u = Vector!(uint, 2); /// ditto 816 alias vec3u = Vector!(uint, 3); /// ditto 817 alias vec4u = Vector!(uint, 4); /// ditto 818 819 /*alias Vector!(ubyte, 2) vec2ub; 820 alias Vector!(ubyte, 3) vec3ub; 821 alias Vector!(ubyte, 4) vec4ub;*/ 822 823 824 /// Base template for all matrix-types. 825 /// Params: 826 /// type = all values get stored as this type 827 /// rows_ = rows of the matrix 828 /// cols_ = columns of the matrix 829 /// Examples: 830 /// --- 831 /// alias Matrix!(float, 4, 4) mat4; 832 /// alias Matrix!(double, 3, 4) mat34d; 833 /// alias Matrix!(real, 2, 2) mat2r; 834 /// --- 835 struct Matrix(type, int rows_, int cols_) if((rows_ > 0) && (cols_ > 0)) { 836 alias mt = type; /// Holds the internal type of the matrix; 837 static const int rows = rows_; /// Holds the number of rows; 838 static const int cols = cols_; /// Holds the number of columns; 839 840 /// Holds the matrix $(RED row-major) in memory. 841 mt[cols][rows] matrix; // In C it would be mt[rows][cols], D does it like this: (mt[foo])[bar] 842 alias matrix this; 843 844 unittest { 845 mat2 m2 = mat2(0.0f, 1.0f, 2.0f, 3.0f); 846 assert(m2[0][0] == 0.0f); 847 assert(m2[0][1] == 1.0f); 848 assert(m2[1][0] == 2.0f); 849 assert(m2[1][1] == 3.0f); 850 m2[0..1] = [2.0f, 2.0f]; 851 assert(m2 == [[2.0f, 2.0f], [2.0f, 3.0f]]); 852 853 mat3 m3 = mat3(0.0f, 0.1f, 0.2f, 1.0f, 1.1f, 1.2f, 2.0f, 2.1f, 2.2f); 854 assert(m3[0][1] == 0.1f); 855 assert(m3[2][0] == 2.0f); 856 assert(m3[1][2] == 1.2f); 857 m3[0][0..$] = 0.0f; 858 assert(m3 == [[0.0f, 0.0f, 0.0f], 859 [1.0f, 1.1f, 1.2f], 860 [2.0f, 2.1f, 2.2f]]); 861 862 mat4 m4 = mat4(0.0f, 0.1f, 0.2f, 0.3f, 863 1.0f, 1.1f, 1.2f, 1.3f, 864 2.0f, 2.1f, 2.2f, 2.3f, 865 3.0f, 3.1f, 3.2f, 3.3f); 866 assert(m4[0][3] == 0.3f); 867 assert(m4[1][1] == 1.1f); 868 assert(m4[2][0] == 2.0f); 869 assert(m4[3][2] == 3.2f); 870 m4[2][1..3] = [1.0f, 2.0f]; 871 assert(m4 == [[0.0f, 0.1f, 0.2f, 0.3f], 872 [1.0f, 1.1f, 1.2f, 1.3f], 873 [2.0f, 1.0f, 2.0f, 2.3f], 874 [3.0f, 3.1f, 3.2f, 3.3f]]); 875 876 } 877 878 /// Returns the pointer to the stored values as OpenGL requires it. 879 /// Note this will return a pointer to a $(RED row-major) matrix, 880 /// $(RED this means you've to set the transpose argument to GL_TRUE when passing it to OpenGL). 881 /// Examples: 882 /// --- 883 /// // 3rd argument = GL_TRUE 884 /// glUniformMatrix4fv(programs.main.model, 1, GL_TRUE, mat4.translation(-0.5f, -0.5f, 1.0f).ptr); 885 /// --- 886 auto ptr() const { return matrix[0].ptr; } 887 888 /// Returns the current matrix formatted as flat string. 889 string toString() const { 890 return format("%s", matrix); 891 } 892 893 /// Returns the current matrix as pretty formatted string. 894 string toPrettyString() { 895 string fmtr = "%s"; 896 897 size_t rjust = max(format(fmtr, reduce!(max)(matrix[])).length, 898 format(fmtr, reduce!(min)(matrix[])).length) - 1; 899 900 string[] outer_parts; 901 foreach(mt[] row; matrix) { 902 string[] inner_parts; 903 foreach(mt col; row) { 904 inner_parts ~= rightJustify(format(fmtr, col), rjust); 905 } 906 outer_parts ~= " [" ~ join(inner_parts, ", ") ~ "]"; 907 } 908 909 return "[" ~ join(outer_parts, "\n")[1..$] ~ "]"; 910 } 911 912 @safe pure nothrow: 913 static void isCompatibleMatrixImpl(int r, int c)(Matrix!(mt, r, c) m) { 914 } 915 916 template isCompatibleMatrix(T) { 917 enum isCompatibleMatrix = is(typeof(isCompatibleMatrixImpl(T.init))); 918 } 919 920 static void isCompatibleVectorImpl(int d)(Vector!(mt, d) vec) { 921 } 922 923 template isCompatibleVector(T) { 924 enum isCompatibleVector = is(typeof(isCompatibleVectorImpl(T.init))); 925 } 926 927 private void construct(int i, T, Tail...)(T head, Tail tail) { 928 static if(i >= rows*cols) { 929 static assert(false, "Too many arguments passed to constructor"); 930 } else static if(is(T : mt)) { 931 matrix[i / cols][i % cols] = head; 932 construct!(i + 1)(tail); 933 } else static if(is(T == Vector!(mt, cols))) { 934 static if(i % cols == 0) { 935 matrix[i / cols] = head.vector; 936 construct!(i + T.dimension)(tail); 937 } else { 938 static assert(false, "Can't convert Vector into the matrix. Maybe it doesn't align to the columns correctly or dimension doesn't fit"); 939 } 940 } else static if(isDynamicArray!T) { 941 foreach(j; 0..cols*rows) 942 matrix[j / cols][j % cols] = head[j]; 943 } else { 944 static assert(false, "Matrix constructor argument must be of type " ~ mt.stringof ~ " or Vector, not " ~ T.stringof); 945 } 946 } 947 948 private void construct(int i)() { // terminate 949 static assert(i == rows*cols, "Not enough arguments passed to constructor"); 950 } 951 952 /// Constructs the matrix: 953 /// If a single value is passed, the matrix will be cleared with this value (each column in each row will contain this value). 954 /// If a matrix with more rows and columns is passed, the matrix will be the upper left nxm matrix. 955 /// If a matrix with less rows and columns is passed, the passed matrix will be stored in the upper left of an identity matrix. 956 /// It's also allowed to pass vectors and scalars at a time, but the vectors dimension must match the number of columns and align correctly. 957 /// Examples: 958 /// --- 959 /// mat2 m2 = mat2(0.0f); // mat2 m2 = mat2(0.0f, 0.0f, 0.0f, 0.0f); 960 /// mat3 m3 = mat3(m2); // mat3 m3 = mat3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); 961 /// mat3 m3_2 = mat3(vec3(1.0f, 2.0f, 3.0f), 4.0f, 5.0f, 6.0f, vec3(7.0f, 8.0f, 9.0f)); 962 /// mat4 m4 = mat4.identity; // just an identity matrix 963 /// mat3 m3_3 = mat3(m4); // mat3 m3_3 = mat3.identity 964 /// --- 965 this(Args...)(Args args) { 966 construct!(0)(args); 967 } 968 969 /// ditto 970 this(T)(T mat) if(isMatrix!T && (T.cols >= cols) && (T.rows >= rows)) { 971 foreach(r; TupleRange!(0, rows)) { 972 foreach(c; TupleRange!(0, cols)) { 973 matrix[r][c] = mat.matrix[r][c]; 974 } 975 } 976 } 977 978 /// ditto 979 this(T)(T mat) if(isMatrix!T && (T.cols < cols) && (T.rows < rows)) { 980 makeIdentity(); 981 982 foreach(r; TupleRange!(0, T.rows)) { 983 foreach(c; TupleRange!(0, T.cols)) { 984 matrix[r][c] = mat.matrix[r][c]; 985 } 986 } 987 } 988 989 /// ditto 990 this()(mt value) { 991 clear(value); 992 } 993 994 /// Returns true if all values are not nan and finite, otherwise false. 995 bool isFinite() const { 996 static if(isIntegral!type) { 997 return true; 998 } 999 else { 1000 foreach(row; matrix) { 1001 foreach(col; row) { 1002 if(isNaN(col) || isInfinity(col)) { 1003 return false; 1004 } 1005 } 1006 } 1007 return true; 1008 } 1009 1010 } 1011 deprecated("Use isFinite instead of ok") alias ok = isFinite; 1012 1013 /// Sets all values of the matrix to value (each column in each row will contain this value). 1014 void clear(mt value) { 1015 foreach(r; TupleRange!(0, rows)) { 1016 foreach(c; TupleRange!(0, cols)) { 1017 matrix[r][c] = value; 1018 } 1019 } 1020 } 1021 1022 unittest { 1023 mat2 m2 = mat2(1.0f, 1.0f, vec2(2.0f, 2.0f)); 1024 assert(m2.matrix == [[1.0f, 1.0f], [2.0f, 2.0f]]); 1025 m2.clear(3.0f); 1026 assert(m2.matrix == [[3.0f, 3.0f], [3.0f, 3.0f]]); 1027 assert(m2.isFinite); 1028 m2.clear(float.nan); 1029 assert(!m2.isFinite); 1030 m2.clear(float.infinity); 1031 assert(!m2.isFinite); 1032 m2.clear(0.0f); 1033 assert(m2.isFinite); 1034 1035 mat3 m3 = mat3(1.0f); 1036 assert(m3.matrix == [[1.0f, 1.0f, 1.0f], 1037 [1.0f, 1.0f, 1.0f], 1038 [1.0f, 1.0f, 1.0f]]); 1039 1040 mat4 m4 = mat4(vec4(1.0f, 1.0f, 1.0f, 1.0f), 1041 2.0f, 2.0f, 2.0f, 2.0f, 1042 3.0f, 3.0f, 3.0f, 3.0f, 1043 vec4(4.0f, 4.0f, 4.0f, 4.0f)); 1044 assert(m4.matrix == [[1.0f, 1.0f, 1.0f, 1.0f], 1045 [2.0f, 2.0f, 2.0f, 2.0f], 1046 [3.0f, 3.0f, 3.0f, 3.0f], 1047 [4.0f, 4.0f, 4.0f, 4.0f]]); 1048 assert(mat3(m4).matrix == [[1.0f, 1.0f, 1.0f], 1049 [2.0f, 2.0f, 2.0f], 1050 [3.0f, 3.0f, 3.0f]]); 1051 assert(mat2(mat3(m4)).matrix == [[1.0f, 1.0f], [2.0f, 2.0f]]); 1052 assert(mat2(m4).matrix == mat2(mat3(m4)).matrix); 1053 assert(mat4(mat3(m4)).matrix == [[1.0f, 1.0f, 1.0f, 0.0f], 1054 [2.0f, 2.0f, 2.0f, 0.0f], 1055 [3.0f, 3.0f, 3.0f, 0.0f], 1056 [0.0f, 0.0f, 0.0f, 1.0f]]); 1057 1058 Matrix!(float, 2, 3) mt1 = Matrix!(float, 2, 3)(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f); 1059 Matrix!(float, 3, 2) mt2 = Matrix!(float, 3, 2)(6.0f, -1.0f, 3.0f, 2.0f, 0.0f, -3.0f); 1060 1061 assert(mt1.matrix == [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 6.0f]]); 1062 assert(mt2.matrix == [[6.0f, -1.0f], [3.0f, 2.0f], [0.0f, -3.0f]]); 1063 1064 static assert(!__traits(compiles, mat2(1, 2, 1))); 1065 static assert(!__traits(compiles, mat3(1, 2, 3, 1, 2, 3, 1, 2))); 1066 static assert(!__traits(compiles, mat4(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3))); 1067 1068 auto m5 = mat2([0.0f,1,2,3]); 1069 assert(m5.matrix == [[0.0f, 1.0f], [2.0f, 3.0f]]); 1070 1071 auto m6 = Matrix!(int, 2, 3)([0,1,2,3,4,5]); 1072 assert(m6.matrix == [[0, 1, 2], [3, 4, 5]]); 1073 } 1074 1075 static if(rows == cols) { 1076 /// Makes the current matrix an identity matrix. 1077 void makeIdentity() { 1078 clear(0); 1079 foreach(r; TupleRange!(0, rows)) { 1080 matrix[r][r] = 1; 1081 } 1082 } 1083 1084 /// Returns a identity matrix. 1085 static Matrix identity() { 1086 Matrix ret; 1087 ret.clear(0); 1088 1089 foreach(r; TupleRange!(0, rows)) { 1090 ret.matrix[r][r] = 1; 1091 } 1092 1093 return ret; 1094 } 1095 1096 /// Transposes the current matrix; 1097 void transpose() { 1098 matrix = transposed().matrix; 1099 } 1100 1101 unittest { 1102 mat2 m2 = mat2(1.0f); 1103 m2.transpose(); 1104 assert(m2.matrix == mat2(1.0f).matrix); 1105 m2.makeIdentity(); 1106 assert(m2.matrix == [[1.0f, 0.0f], 1107 [0.0f, 1.0f]]); 1108 m2.transpose(); 1109 assert(m2.matrix == [[1.0f, 0.0f], 1110 [0.0f, 1.0f]]); 1111 assert(m2.matrix == m2.identity.matrix); 1112 1113 mat3 m3 = mat3(1.1f, 1.2f, 1.3f, 1114 2.1f, 2.2f, 2.3f, 1115 3.1f, 3.2f, 3.3f); 1116 m3.transpose(); 1117 assert(m3.matrix == [[1.1f, 2.1f, 3.1f], 1118 [1.2f, 2.2f, 3.2f], 1119 [1.3f, 2.3f, 3.3f]]); 1120 1121 mat4 m4 = mat4(2.0f); 1122 m4.transpose(); 1123 assert(m4.matrix == mat4(2.0f).matrix); 1124 m4.makeIdentity(); 1125 assert(m4.matrix == [[1.0f, 0.0f, 0.0f, 0.0f], 1126 [0.0f, 1.0f, 0.0f, 0.0f], 1127 [0.0f, 0.0f, 1.0f, 0.0f], 1128 [0.0f, 0.0f, 0.0f, 1.0f]]); 1129 assert(m4.matrix == m4.identity.matrix); 1130 } 1131 1132 } 1133 1134 /// Returns a transposed copy of the matrix. 1135 Matrix!(mt, cols, rows) transposed() const { 1136 typeof(return) ret; 1137 1138 foreach(r; TupleRange!(0, rows)) { 1139 foreach(c; TupleRange!(0, cols)) { 1140 ret.matrix[c][r] = matrix[r][c]; 1141 } 1142 } 1143 1144 return ret; 1145 } 1146 1147 // transposed already tested in last unittest 1148 1149 1150 static if((rows == 2) && (cols == 2)) { 1151 mt det() const { 1152 return (matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]); 1153 } 1154 1155 private Matrix invert(ref Matrix mat) const { 1156 static if(isFloatingPoint!mt && rmul) { 1157 mt d = 1 / det; 1158 1159 mat.matrix = [[matrix[1][1]*d, -matrix[0][1]*d], 1160 [-matrix[1][0]*d, matrix[0][0]*d]]; 1161 } else { 1162 mt d = det; 1163 1164 mat.matrix = [[matrix[1][1]/d, -matrix[0][1]/d], 1165 [-matrix[1][0]/d, matrix[0][0]/d]]; 1166 } 1167 1168 return mat; 1169 } 1170 1171 static Matrix scaling(mt x, mt y) { 1172 Matrix ret = Matrix.identity; 1173 1174 ret.matrix[0][0] = x; 1175 ret.matrix[1][1] = y; 1176 1177 return ret; 1178 } 1179 1180 Matrix scale(mt x, mt y) { 1181 this = Matrix.scaling(x, y) * this; 1182 return this; 1183 } 1184 1185 unittest { 1186 assert(mat2.scaling(3, 3).matrix == mat2.identity.scale(3, 3).matrix); 1187 assert(mat2.scaling(3, 3).matrix == [[3.0f, 0.0f], [0.0f, 3.0f]]); 1188 } 1189 1190 } else static if((rows == 3) && (cols == 3)) { 1191 mt det() const { 1192 return (matrix[0][0] * matrix[1][1] * matrix[2][2] 1193 + matrix[0][1] * matrix[1][2] * matrix[2][0] 1194 + matrix[0][2] * matrix[1][0] * matrix[2][1] 1195 - matrix[0][2] * matrix[1][1] * matrix[2][0] 1196 - matrix[0][1] * matrix[1][0] * matrix[2][2] 1197 - matrix[0][0] * matrix[1][2] * matrix[2][1]); 1198 } 1199 1200 private Matrix invert(ref Matrix mat) const { 1201 static if(isFloatingPoint!mt && rmul) { 1202 mt d = 1 / det; 1203 enum op = "*"; 1204 } else { 1205 mt d = det; 1206 enum op = "/"; 1207 } 1208 1209 mixin(` 1210 mat.matrix = [[(matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])`~op~`d, 1211 (matrix[0][2] * matrix[2][1] - matrix[0][1] * matrix[2][2])`~op~`d, 1212 (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])`~op~`d], 1213 [(matrix[1][2] * matrix[2][0] - matrix[1][0] * matrix[2][2])`~op~`d, 1214 (matrix[0][0] * matrix[2][2] - matrix[0][2] * matrix[2][0])`~op~`d, 1215 (matrix[0][2] * matrix[1][0] - matrix[0][0] * matrix[1][2])`~op~`d], 1216 [(matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0])`~op~`d, 1217 (matrix[0][1] * matrix[2][0] - matrix[0][0] * matrix[2][1])`~op~`d, 1218 (matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0])`~op~`d]]; 1219 `); 1220 1221 return mat; 1222 } 1223 } else static if((rows == 4) && (cols == 4)) { 1224 /// Returns the determinant of the current matrix (2x2, 3x3 and 4x4 matrices). 1225 mt det() const { 1226 return (matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] 1227 - matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] 1228 + matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] 1229 - matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] 1230 + matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] 1231 - matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] 1232 + matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] 1233 - matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] 1234 + matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] 1235 - matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] 1236 + matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] 1237 - matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3]); 1238 } 1239 1240 private Matrix invert(ref Matrix mat) const { 1241 static if(isFloatingPoint!mt && rmul) { 1242 mt d = 1 / det; 1243 enum op = "*"; 1244 } else { 1245 mt d = det; 1246 enum op = "/"; 1247 } 1248 1249 mixin(` 1250 mat.matrix = [[(matrix[1][1] * matrix[2][2] * matrix[3][3] + matrix[1][2] * matrix[2][3] * matrix[3][1] + matrix[1][3] * matrix[2][1] * matrix[3][2] 1251 - matrix[1][1] * matrix[2][3] * matrix[3][2] - matrix[1][2] * matrix[2][1] * matrix[3][3] - matrix[1][3] * matrix[2][2] * matrix[3][1])`~op~`d, 1252 (matrix[0][1] * matrix[2][3] * matrix[3][2] + matrix[0][2] * matrix[2][1] * matrix[3][3] + matrix[0][3] * matrix[2][2] * matrix[3][1] 1253 - matrix[0][1] * matrix[2][2] * matrix[3][3] - matrix[0][2] * matrix[2][3] * matrix[3][1] - matrix[0][3] * matrix[2][1] * matrix[3][2])`~op~`d, 1254 (matrix[0][1] * matrix[1][2] * matrix[3][3] + matrix[0][2] * matrix[1][3] * matrix[3][1] + matrix[0][3] * matrix[1][1] * matrix[3][2] 1255 - matrix[0][1] * matrix[1][3] * matrix[3][2] - matrix[0][2] * matrix[1][1] * matrix[3][3] - matrix[0][3] * matrix[1][2] * matrix[3][1])`~op~`d, 1256 (matrix[0][1] * matrix[1][3] * matrix[2][2] + matrix[0][2] * matrix[1][1] * matrix[2][3] + matrix[0][3] * matrix[1][2] * matrix[2][1] 1257 - matrix[0][1] * matrix[1][2] * matrix[2][3] - matrix[0][2] * matrix[1][3] * matrix[2][1] - matrix[0][3] * matrix[1][1] * matrix[2][2])`~op~`d], 1258 [(matrix[1][0] * matrix[2][3] * matrix[3][2] + matrix[1][2] * matrix[2][0] * matrix[3][3] + matrix[1][3] * matrix[2][2] * matrix[3][0] 1259 - matrix[1][0] * matrix[2][2] * matrix[3][3] - matrix[1][2] * matrix[2][3] * matrix[3][0] - matrix[1][3] * matrix[2][0] * matrix[3][2])`~op~`d, 1260 (matrix[0][0] * matrix[2][2] * matrix[3][3] + matrix[0][2] * matrix[2][3] * matrix[3][0] + matrix[0][3] * matrix[2][0] * matrix[3][2] 1261 - matrix[0][0] * matrix[2][3] * matrix[3][2] - matrix[0][2] * matrix[2][0] * matrix[3][3] - matrix[0][3] * matrix[2][2] * matrix[3][0])`~op~`d, 1262 (matrix[0][0] * matrix[1][3] * matrix[3][2] + matrix[0][2] * matrix[1][0] * matrix[3][3] + matrix[0][3] * matrix[1][2] * matrix[3][0] 1263 - matrix[0][0] * matrix[1][2] * matrix[3][3] - matrix[0][2] * matrix[1][3] * matrix[3][0] - matrix[0][3] * matrix[1][0] * matrix[3][2])`~op~`d, 1264 (matrix[0][0] * matrix[1][2] * matrix[2][3] + matrix[0][2] * matrix[1][3] * matrix[2][0] + matrix[0][3] * matrix[1][0] * matrix[2][2] 1265 - matrix[0][0] * matrix[1][3] * matrix[2][2] - matrix[0][2] * matrix[1][0] * matrix[2][3] - matrix[0][3] * matrix[1][2] * matrix[2][0])`~op~`d], 1266 [(matrix[1][0] * matrix[2][1] * matrix[3][3] + matrix[1][1] * matrix[2][3] * matrix[3][0] + matrix[1][3] * matrix[2][0] * matrix[3][1] 1267 - matrix[1][0] * matrix[2][3] * matrix[3][1] - matrix[1][1] * matrix[2][0] * matrix[3][3] - matrix[1][3] * matrix[2][1] * matrix[3][0])`~op~`d, 1268 (matrix[0][0] * matrix[2][3] * matrix[3][1] + matrix[0][1] * matrix[2][0] * matrix[3][3] + matrix[0][3] * matrix[2][1] * matrix[3][0] 1269 - matrix[0][0] * matrix[2][1] * matrix[3][3] - matrix[0][1] * matrix[2][3] * matrix[3][0] - matrix[0][3] * matrix[2][0] * matrix[3][1])`~op~`d, 1270 (matrix[0][0] * matrix[1][1] * matrix[3][3] + matrix[0][1] * matrix[1][3] * matrix[3][0] + matrix[0][3] * matrix[1][0] * matrix[3][1] 1271 - matrix[0][0] * matrix[1][3] * matrix[3][1] - matrix[0][1] * matrix[1][0] * matrix[3][3] - matrix[0][3] * matrix[1][1] * matrix[3][0])`~op~`d, 1272 (matrix[0][0] * matrix[1][3] * matrix[2][1] + matrix[0][1] * matrix[1][0] * matrix[2][3] + matrix[0][3] * matrix[1][1] * matrix[2][0] 1273 - matrix[0][0] * matrix[1][1] * matrix[2][3] - matrix[0][1] * matrix[1][3] * matrix[2][0] - matrix[0][3] * matrix[1][0] * matrix[2][1])`~op~`d], 1274 [(matrix[1][0] * matrix[2][2] * matrix[3][1] + matrix[1][1] * matrix[2][0] * matrix[3][2] + matrix[1][2] * matrix[2][1] * matrix[3][0] 1275 - matrix[1][0] * matrix[2][1] * matrix[3][2] - matrix[1][1] * matrix[2][2] * matrix[3][0] - matrix[1][2] * matrix[2][0] * matrix[3][1])`~op~`d, 1276 (matrix[0][0] * matrix[2][1] * matrix[3][2] + matrix[0][1] * matrix[2][2] * matrix[3][0] + matrix[0][2] * matrix[2][0] * matrix[3][1] 1277 - matrix[0][0] * matrix[2][2] * matrix[3][1] - matrix[0][1] * matrix[2][0] * matrix[3][2] - matrix[0][2] * matrix[2][1] * matrix[3][0])`~op~`d, 1278 (matrix[0][0] * matrix[1][2] * matrix[3][1] + matrix[0][1] * matrix[1][0] * matrix[3][2] + matrix[0][2] * matrix[1][1] * matrix[3][0] 1279 - matrix[0][0] * matrix[1][1] * matrix[3][2] - matrix[0][1] * matrix[1][2] * matrix[3][0] - matrix[0][2] * matrix[1][0] * matrix[3][1])`~op~`d, 1280 (matrix[0][0] * matrix[1][1] * matrix[2][2] + matrix[0][1] * matrix[1][2] * matrix[2][0] + matrix[0][2] * matrix[1][0] * matrix[2][1] 1281 - matrix[0][0] * matrix[1][2] * matrix[2][1] - matrix[0][1] * matrix[1][0] * matrix[2][2] - matrix[0][2] * matrix[1][1] * matrix[2][0])`~op~`d]]; 1282 `); 1283 1284 return mat; 1285 } 1286 1287 // some static fun ... 1288 // (1) glprogramming.com/red/appendixf.html - ortographic is broken! 1289 // (2) http://fly.cc.fer.hr/~unreal/theredbook/appendixg.html 1290 // (3) http://en.wikipedia.org/wiki/Orthographic_projection_(geometry) 1291 1292 static if(isFloatingPoint!mt) { 1293 static private mt[6] cperspective(mt width, mt height, mt fov, mt near, mt far) 1294 in { assert(height != 0); } 1295 do { 1296 mt aspect = width/height; 1297 mt top = near * tan(fov*(PI/360.0)); 1298 mt bottom = -top; 1299 mt right = top * aspect; 1300 mt left = -right; 1301 1302 return [left, right, bottom, top, near, far]; 1303 } 1304 1305 /// Returns a perspective matrix (4x4 and floating-point matrices only). 1306 static Matrix perspective(mt width, mt height, mt fov, mt near, mt far) { 1307 mt[6] cdata = cperspective(width, height, fov, near, far); 1308 return perspective(cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5]); 1309 } 1310 1311 /// ditto 1312 static Matrix perspective(mt left, mt right, mt bottom, mt top, mt near, mt far) 1313 in { 1314 assert(right-left != 0); 1315 assert(top-bottom != 0); 1316 assert(far-near != 0); 1317 } 1318 do { 1319 Matrix ret; 1320 ret.clear(0); 1321 1322 ret.matrix[0][0] = (2*near)/(right-left); 1323 ret.matrix[0][2] = (right+left)/(right-left); 1324 ret.matrix[1][1] = (2*near)/(top-bottom); 1325 ret.matrix[1][2] = (top+bottom)/(top-bottom); 1326 ret.matrix[2][2] = -(far+near)/(far-near); 1327 ret.matrix[2][3] = -(2*far*near)/(far-near); 1328 ret.matrix[3][2] = -1; 1329 1330 return ret; 1331 } 1332 1333 /// Returns an inverse perspective matrix (4x4 and floating-point matrices only). 1334 static Matrix persperctiveInverse(mt width, mt height, mt fov, mt near, mt far) { 1335 mt[6] cdata = cperspective(width, height, fov, near, far); 1336 return persperctiveInverse(cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5]); 1337 } 1338 1339 /// ditto 1340 static Matrix persperctiveInverse(mt left, mt right, mt bottom, mt top, mt near, mt far) 1341 in { 1342 assert(near != 0); 1343 assert(far != 0); 1344 } 1345 do { 1346 Matrix ret; 1347 ret.clear(0); 1348 1349 ret.matrix[0][0] = (right-left)/(2*near); 1350 ret.matrix[0][3] = (right+left)/(2*near); 1351 ret.matrix[1][1] = (top-bottom)/(2*near); 1352 ret.matrix[1][3] = (top+bottom)/(2*near); 1353 ret.matrix[2][3] = -1; 1354 ret.matrix[3][2] = -(far-near)/(2*far*near); 1355 ret.matrix[3][3] = (far+near)/(2*far*near); 1356 1357 return ret; 1358 } 1359 1360 // (2) and (3) say this one is correct 1361 /// Returns an orthographic matrix (4x4 and floating-point matrices only). 1362 static Matrix orthographic(mt left, mt right, mt bottom, mt top, mt near, mt far) 1363 in { 1364 assert(right-left != 0); 1365 assert(top-bottom != 0); 1366 assert(far-near != 0); 1367 } 1368 do { 1369 Matrix ret; 1370 ret.clear(0); 1371 1372 ret.matrix[0][0] = 2/(right-left); 1373 ret.matrix[0][3] = -(right+left)/(right-left); 1374 ret.matrix[1][1] = 2/(top-bottom); 1375 ret.matrix[1][3] = -(top+bottom)/(top-bottom); 1376 ret.matrix[2][2] = -2/(far-near); 1377 ret.matrix[2][3] = -(far+near)/(far-near); 1378 ret.matrix[3][3] = 1; 1379 1380 return ret; 1381 } 1382 1383 // (1) and (2) say this one is correct 1384 /// Returns an inverse ortographic matrix (4x4 and floating-point matrices only). 1385 static Matrix orthographicInverse(mt left, mt right, mt bottom, mt top, mt near, mt far) { 1386 Matrix ret; 1387 ret.clear(0); 1388 1389 ret.matrix[0][0] = (right-left)/2; 1390 ret.matrix[0][3] = (right+left)/2; 1391 ret.matrix[1][1] = (top-bottom)/2; 1392 ret.matrix[1][3] = (top+bottom)/2; 1393 ret.matrix[2][2] = (far-near)/-2; 1394 ret.matrix[2][3] = (far+near)/2; 1395 ret.matrix[3][3] = 1; 1396 1397 return ret; 1398 } 1399 1400 /// Returns a look at matrix (4x4 and floating-point matrices only). 1401 static Matrix lookAt(Vector!(mt, 3) eye, Vector!(mt, 3) target, Vector!(mt, 3) up) { 1402 alias vec3mt = Vector!(mt, 3); 1403 vec3mt look_dir = (target - eye).normalized; 1404 vec3mt up_dir = up.normalized; 1405 1406 vec3mt right_dir = cross(look_dir, up_dir).normalized; 1407 vec3mt perp_up_dir = cross(right_dir, look_dir); 1408 1409 Matrix ret = Matrix.identity; 1410 ret.matrix[0][0..3] = right_dir.vector[]; 1411 ret.matrix[1][0..3] = perp_up_dir.vector[]; 1412 ret.matrix[2][0..3] = (-look_dir).vector[]; 1413 1414 ret.matrix[0][3] = -dot(eye, right_dir); 1415 ret.matrix[1][3] = -dot(eye, perp_up_dir); 1416 ret.matrix[2][3] = dot(eye, look_dir); 1417 1418 return ret; 1419 } 1420 1421 unittest { 1422 mt[6] cp = cperspective(600f, 900f, 60f, 1f, 100f); 1423 assert(cp[4] == 1.0f); 1424 assert(cp[5] == 100.0f); 1425 assert(cp[0] == -cp[1]); 1426 assert((cp[0] < -0.38489f) && (cp[0] > -0.38491f)); 1427 assert(cp[2] == -cp[3]); 1428 assert((cp[2] < -0.577349f) && (cp[2] > -0.577351f)); 1429 1430 assert(mat4.perspective(600f, 900f, 60.0, 1.0, 100.0) == mat4.perspective(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5])); 1431 float[4][4] m4p = mat4.perspective(600f, 900f, 60.0, 1.0, 100.0).matrix; 1432 assert((m4p[0][0] < 2.598077f) && (m4p[0][0] > 2.598075f)); 1433 assert(m4p[0][2] == 0.0f); 1434 assert((m4p[1][1] < 1.732052) && (m4p[1][1] > 1.732050)); 1435 assert(m4p[1][2] == 0.0f); 1436 assert((m4p[2][2] < -1.020201) && (m4p[2][2] > -1.020203)); 1437 assert((m4p[2][3] < -2.020201) && (m4p[2][3] > -2.020203)); 1438 assert((m4p[3][2] < -0.9f) && (m4p[3][2] > -1.1f)); 1439 1440 float[4][4] m4pi = mat4.persperctiveInverse(600f, 900f, 60.0, 1.0, 100.0).matrix; 1441 assert((m4pi[0][0] < 0.384901) && (m4pi[0][0] > 0.384899)); 1442 assert(m4pi[0][3] == 0.0f); 1443 assert((m4pi[1][1] < 0.577351) && (m4pi[1][1] > 0.577349)); 1444 assert(m4pi[1][3] == 0.0f); 1445 assert(m4pi[2][3] == -1.0f); 1446 assert((m4pi[3][2] < -0.494999) && (m4pi[3][2] > -0.495001)); 1447 assert((m4pi[3][3] < 0.505001) && (m4pi[3][3] > 0.504999)); 1448 1449 // maybe the next tests should be improved 1450 float[4][4] m4o = mat4.orthographic(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f).matrix; 1451 assert(m4o == [[1.0f, 0.0f, 0.0f, 0.0f], 1452 [0.0f, 1.0f, 0.0f, 0.0f], 1453 [0.0f, 0.0f, -1.0f, 0.0f], 1454 [0.0f, 0.0f, 0.0f, 1.0f]]); 1455 1456 float[4][4] m4oi = mat4.orthographicInverse(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f).matrix; 1457 assert(m4oi == [[1.0f, 0.0f, 0.0f, 0.0f], 1458 [0.0f, 1.0f, 0.0f, 0.0f], 1459 [0.0f, 0.0f, -1.0f, 0.0f], 1460 [0.0f, 0.0f, 0.0f, 1.0f]]); 1461 1462 //TODO: lookAt tests 1463 } 1464 } 1465 } 1466 1467 static if((rows == cols) && (rows >= 3) && (rows <= 4)) { 1468 /// Returns a translation matrix (3x3 and 4x4 matrices). 1469 static Matrix translation(mt x, mt y, mt z) { 1470 Matrix ret = Matrix.identity; 1471 1472 ret.matrix[0][cols-1] = x; 1473 ret.matrix[1][cols-1] = y; 1474 ret.matrix[2][cols-1] = z; 1475 1476 return ret; 1477 } 1478 1479 /// ditto 1480 static Matrix translation(Vector!(mt, 3) v) { 1481 Matrix ret = Matrix.identity; 1482 1483 ret.matrix[0][cols-1] = v.x; 1484 ret.matrix[1][cols-1] = v.y; 1485 ret.matrix[2][cols-1] = v.z; 1486 1487 return ret; 1488 } 1489 1490 /// Applys a translation on the current matrix and returns $(I this) (3x3 and 4x4 matrices). 1491 Matrix translate(mt x, mt y, mt z) { 1492 this = Matrix.translation(x, y, z) * this; 1493 return this; 1494 } 1495 1496 /// ditto 1497 Matrix translate(Vector!(mt, 3) v) { 1498 this = Matrix.translation(v) * this; 1499 return this; 1500 } 1501 1502 /// Returns a scaling matrix (3x3 and 4x4 matrices); 1503 static Matrix scaling(mt x, mt y, mt z) { 1504 Matrix ret = Matrix.identity; 1505 1506 ret.matrix[0][0] = x; 1507 ret.matrix[1][1] = y; 1508 ret.matrix[2][2] = z; 1509 1510 return ret; 1511 } 1512 1513 /// Applys a scale to the current matrix and returns $(I this) (3x3 and 4x4 matrices). 1514 Matrix scale(mt x, mt y, mt z) { 1515 this = Matrix.scaling(x, y, z) * this; 1516 return this; 1517 } 1518 1519 unittest { 1520 mat3 m3 = mat3.identity; 1521 assert(m3.translate(1.0f, 2.0f, 3.0f).matrix == mat3.translation(1.0f, 2.0f, 3.0f).matrix); 1522 assert(mat3.translation(1.0f, 2.0f, 3.0f).matrix == [[1.0f, 0.0f, 1.0f], 1523 [0.0f, 1.0f, 2.0f], 1524 [0.0f, 0.0f, 3.0f]]); 1525 assert(mat3.identity.translate(0.0f, 1.0f, 2.0f).matrix == mat3.translation(0.0f, 1.0f, 2.0f).matrix); 1526 1527 mat3 m31 = mat3.identity; 1528 assert(m31.translate(vec3(1.0f, 2.0f, 3.0f)).matrix == mat3.translation(vec3(1.0f, 2.0f, 3.0f)).matrix); 1529 assert(mat3.translation(vec3(1.0f, 2.0f, 3.0f)).matrix == [[1.0f, 0.0f, 1.0f], 1530 [0.0f, 1.0f, 2.0f], 1531 [0.0f, 0.0f, 3.0f]]); 1532 assert(mat3.identity.translate(vec3(0.0f, 1.0f, 2.0f)).matrix == mat3.translation(vec3(0.0f, 1.0f, 2.0f)).matrix); 1533 1534 assert(m3.scaling(0.0f, 1.0f, 2.0f).matrix == mat3.scaling(0.0f, 1.0f, 2.0f).matrix); 1535 assert(mat3.scaling(0.0f, 1.0f, 2.0f).matrix == [[0.0f, 0.0f, 0.0f], 1536 [0.0f, 1.0f, 0.0f], 1537 [0.0f, 0.0f, 2.0f]]); 1538 assert(mat3.identity.scale(0.0f, 1.0f, 2.0f).matrix == mat3.scaling(0.0f, 1.0f, 2.0f).matrix); 1539 1540 // same tests for 4x4 1541 1542 mat4 m4 = mat4(1.0f); 1543 assert(m4.translation(1.0f, 2.0f, 3.0f).matrix == mat4.translation(1.0f, 2.0f, 3.0f).matrix); 1544 assert(mat4.translation(1.0f, 2.0f, 3.0f).matrix == [[1.0f, 0.0f, 0.0f, 1.0f], 1545 [0.0f, 1.0f, 0.0f, 2.0f], 1546 [0.0f, 0.0f, 1.0f, 3.0f], 1547 [0.0f, 0.0f, 0.0f, 1.0f]]); 1548 assert(mat4.identity.translate(0.0f, 1.0f, 2.0f).matrix == mat4.translation(0.0f, 1.0f, 2.0f).matrix); 1549 1550 assert(m4.scaling(0.0f, 1.0f, 2.0f).matrix == mat4.scaling(0.0f, 1.0f, 2.0f).matrix); 1551 assert(mat4.scaling(0.0f, 1.0f, 2.0f).matrix == [[0.0f, 0.0f, 0.0f, 0.0f], 1552 [0.0f, 1.0f, 0.0f, 0.0f], 1553 [0.0f, 0.0f, 2.0f, 0.0f], 1554 [0.0f, 0.0f, 0.0f, 1.0f]]); 1555 assert(mat4.identity.scale(0.0f, 1.0f, 2.0f).matrix == mat4.scaling(0.0f, 1.0f, 2.0f).matrix); 1556 } 1557 } 1558 1559 1560 static if((rows == cols) && (rows >= 3)) { 1561 static if(isFloatingPoint!mt) { 1562 /// Returns an identity matrix with an applied rotateAxis around an arbitrary axis (nxn matrices, n >= 3). 1563 static Matrix rotation(real alpha, Vector!(mt, 3) axis) { 1564 Matrix mult = Matrix.identity; 1565 1566 if(axis.length != 1) { 1567 axis.normalize(); 1568 } 1569 1570 real cosa = cos(alpha); 1571 real sina = sin(alpha); 1572 1573 Vector!(mt, 3) temp = (1 - cosa)*axis; 1574 1575 mult.matrix[0][0] = to!mt(cosa + temp.x * axis.x); 1576 mult.matrix[0][1] = to!mt( temp.x * axis.y + sina * axis.z); 1577 mult.matrix[0][2] = to!mt( temp.x * axis.z - sina * axis.y); 1578 mult.matrix[1][0] = to!mt( temp.y * axis.x - sina * axis.z); 1579 mult.matrix[1][1] = to!mt(cosa + temp.y * axis.y); 1580 mult.matrix[1][2] = to!mt( temp.y * axis.z + sina * axis.x); 1581 mult.matrix[2][0] = to!mt( temp.z * axis.x + sina * axis.y); 1582 mult.matrix[2][1] = to!mt( temp.z * axis.y - sina * axis.x); 1583 mult.matrix[2][2] = to!mt(cosa + temp.z * axis.z); 1584 1585 return mult; 1586 } 1587 1588 /// ditto 1589 static Matrix rotation(real alpha, mt x, mt y, mt z) { 1590 return Matrix.rotation(alpha, Vector!(mt, 3)(x, y, z)); 1591 } 1592 1593 /// Returns an identity matrix with an applied rotation around the x-axis (nxn matrices, n >= 3). 1594 static Matrix xRotation(real alpha) { 1595 Matrix mult = Matrix.identity; 1596 1597 mt cosamt = to!mt(cos(alpha)); 1598 mt sinamt = to!mt(sin(alpha)); 1599 1600 mult.matrix[1][1] = cosamt; 1601 mult.matrix[1][2] = -sinamt; 1602 mult.matrix[2][1] = sinamt; 1603 mult.matrix[2][2] = cosamt; 1604 1605 return mult; 1606 } 1607 1608 /// Returns an identity matrix with an applied rotation around the y-axis (nxn matrices, n >= 3). 1609 static Matrix yRotation(real alpha) { 1610 Matrix mult = Matrix.identity; 1611 1612 mt cosamt = to!mt(cos(alpha)); 1613 mt sinamt = to!mt(sin(alpha)); 1614 1615 mult.matrix[0][0] = cosamt; 1616 mult.matrix[0][2] = sinamt; 1617 mult.matrix[2][0] = -sinamt; 1618 mult.matrix[2][2] = cosamt; 1619 1620 return mult; 1621 } 1622 1623 /// Returns an identity matrix with an applied rotation around the z-axis (nxn matrices, n >= 3). 1624 static Matrix zRotation(real alpha) { 1625 Matrix mult = Matrix.identity; 1626 1627 mt cosamt = to!mt(cos(alpha)); 1628 mt sinamt = to!mt(sin(alpha)); 1629 1630 mult.matrix[0][0] = cosamt; 1631 mult.matrix[0][1] = -sinamt; 1632 mult.matrix[1][0] = sinamt; 1633 mult.matrix[1][1] = cosamt; 1634 1635 return mult; 1636 } 1637 1638 Matrix rotate(real alpha, Vector!(mt, 3) axis) { 1639 this = rotation(alpha, axis) * this; 1640 return this; 1641 } 1642 1643 /// Rotates the current matrix around the x-axis and returns $(I this) (nxn matrices, n >= 3). 1644 Matrix rotateX(real alpha) { 1645 this = xRotation(alpha) * this; 1646 return this; 1647 } 1648 1649 /// Rotates the current matrix around the y-axis and returns $(I this) (nxn matrices, n >= 3). 1650 Matrix rotateY(real alpha) { 1651 this = yRotation(alpha) * this; 1652 return this; 1653 } 1654 1655 /// Rotates the current matrix around the z-axis and returns $(I this) (nxn matrices, n >= 3). 1656 Matrix rotateZ(real alpha) { 1657 this = zRotation(alpha) * this; 1658 return this; 1659 } 1660 1661 unittest { 1662 assert(mat4.xRotation(0).matrix == [[1.0f, 0.0f, 0.0f, 0.0f], 1663 [0.0f, 1.0f, -0.0f, 0.0f], 1664 [0.0f, 0.0f, 1.0f, 0.0f], 1665 [0.0f, 0.0f, 0.0f, 1.0f]]); 1666 assert(mat4.yRotation(0).matrix == [[1.0f, 0.0f, 0.0f, 0.0f], 1667 [0.0f, 1.0f, 0.0f, 0.0f], 1668 [0.0f, 0.0f, 1.0f, 0.0f], 1669 [0.0f, 0.0f, 0.0f, 1.0f]]); 1670 assert(mat4.zRotation(0).matrix == [[1.0f, -0.0f, 0.0f, 0.0f], 1671 [0.0f, 1.0f, 0.0f, 0.0f], 1672 [0.0f, 0.0f, 1.0f, 0.0f], 1673 [0.0f, 0.0f, 0.0f, 1.0f]]); 1674 mat4 xro = mat4.identity; 1675 xro.rotateX(0); 1676 assert(mat4.xRotation(0).matrix == xro.matrix); 1677 assert(xro.matrix == mat4.identity.rotateX(0).matrix); 1678 assert(xro.matrix == mat4.rotation(0, vec3(1.0f, 0.0f, 0.0f)).matrix); 1679 mat4 yro = mat4.identity; 1680 yro.rotateY(0); 1681 assert(mat4.yRotation(0).matrix == yro.matrix); 1682 assert(yro.matrix == mat4.identity.rotateY(0).matrix); 1683 assert(yro.matrix == mat4.rotation(0, vec3(0.0f, 1.0f, 0.0f)).matrix); 1684 mat4 zro = mat4.identity; 1685 xro.rotateZ(0); 1686 assert(mat4.zRotation(0).matrix == zro.matrix); 1687 assert(zro.matrix == mat4.identity.rotateZ(0).matrix); 1688 assert(zro.matrix == mat4.rotation(0, vec3(0.0f, 0.0f, 1.0f)).matrix); 1689 } 1690 } // isFloatingPoint 1691 1692 1693 /// Sets the translation of the matrix (nxn matrices, n >= 3). 1694 void translation(mt[] values...) // intended to be a property 1695 in { assert(values.length >= (rows-1)); } 1696 do { 1697 foreach(r; TupleRange!(0, rows-1)) { 1698 matrix[r][rows-1] = values[r]; 1699 } 1700 } 1701 1702 /// Copyies the translation from mat to the current matrix (nxn matrices, n >= 3). 1703 void translation(Matrix mat) { 1704 foreach(r; TupleRange!(0, rows-1)) { 1705 matrix[r][rows-1] = mat.matrix[r][rows-1]; 1706 } 1707 } 1708 1709 /// Returns an identity matrix with the current translation applied (nxn matrices, n >= 3).. 1710 Matrix translation() { 1711 Matrix ret = Matrix.identity; 1712 1713 foreach(r; TupleRange!(0, rows-1)) { 1714 ret.matrix[r][rows-1] = matrix[r][rows-1]; 1715 } 1716 1717 return ret; 1718 } 1719 1720 unittest { 1721 mat3 m3 = mat3(0.0f, 1.0f, 2.0f, 1722 3.0f, 4.0f, 5.0f, 1723 6.0f, 7.0f, 1.0f); 1724 assert(m3.translation().matrix == [[1.0f, 0.0f, 2.0f], [0.0f, 1.0f, 5.0f], [0.0f, 0.0f, 1.0f]]); 1725 m3.translation(mat3.identity); 1726 assert(mat3.identity.matrix == m3.translation().matrix); 1727 m3.translation([2.0f, 5.0f]); 1728 assert(m3.translation().matrix == [[1.0f, 0.0f, 2.0f], [0.0f, 1.0f, 5.0f], [0.0f, 0.0f, 1.0f]]); 1729 assert(mat3.identity.matrix == mat3.identity.translation().matrix); 1730 1731 mat4 m4 = mat4(0.0f, 1.0f, 2.0f, 3.0f, 1732 4.0f, 5.0f, 6.0f, 7.0f, 1733 8.0f, 9.0f, 10.0f, 11.0f, 1734 12.0f, 13.0f, 14.0f, 1.0f); 1735 assert(m4.translation().matrix == [[1.0f, 0.0f, 0.0f, 3.0f], 1736 [0.0f, 1.0f, 0.0f, 7.0f], 1737 [0.0f, 0.0f, 1.0f, 11.0f], 1738 [0.0f, 0.0f, 0.0f, 1.0f]]); 1739 m4.translation(mat4.identity); 1740 assert(mat4.identity.matrix == m4.translation().matrix); 1741 m4.translation([3.0f, 7.0f, 11.0f]); 1742 assert(m4.translation().matrix == [[1.0f, 0.0f, 0.0f, 3.0f], 1743 [0.0f, 1.0f, 0.0f, 7.0f], 1744 [0.0f, 0.0f, 1.0f, 11.0f], 1745 [0.0f, 0.0f, 0.0f, 1.0f]]); 1746 assert(mat4.identity.matrix == mat4.identity.translation().matrix); 1747 } 1748 1749 /// Sets the scale of the matrix (nxn matrices, n >= 3). 1750 void scale(mt[] values...) 1751 in { assert(values.length >= (rows-1)); } 1752 do { 1753 foreach(r; TupleRange!(0, rows-1)) { 1754 matrix[r][r] = values[r]; 1755 } 1756 } 1757 1758 /// Copyies the scale from mat to the current matrix (nxn matrices, n >= 3). 1759 void scale(Matrix mat) { 1760 foreach(r; TupleRange!(0, rows-1)) { 1761 matrix[r][r] = mat.matrix[r][r]; 1762 } 1763 } 1764 1765 /// Returns an identity matrix with the current scale applied (nxn matrices, n >= 3). 1766 Matrix scale() { 1767 Matrix ret = Matrix.identity; 1768 1769 foreach(r; TupleRange!(0, rows-1)) { 1770 ret.matrix[r][r] = matrix[r][r]; 1771 } 1772 1773 return ret; 1774 } 1775 1776 unittest { 1777 mat3 m3 = mat3(0.0f, 1.0f, 2.0f, 1778 3.0f, 4.0f, 5.0f, 1779 6.0f, 7.0f, 1.0f); 1780 assert(m3.scale().matrix == [[0.0f, 0.0f, 0.0f], [0.0f, 4.0f, 0.0f], [0.0f, 0.0f, 1.0f]]); 1781 m3.scale(mat3.identity); 1782 assert(mat3.identity.matrix == m3.scale().matrix); 1783 m3.scale([0.0f, 4.0f]); 1784 assert(m3.scale().matrix == [[0.0f, 0.0f, 0.0f], [0.0f, 4.0f, 0.0f], [0.0f, 0.0f, 1.0f]]); 1785 assert(mat3.identity.matrix == mat3.identity.scale().matrix); 1786 1787 mat4 m4 = mat4(0.0f, 1.0f, 2.0f, 3.0f, 1788 4.0f, 5.0f, 6.0f, 7.0f, 1789 8.0f, 9.0f, 10.0f, 11.0f, 1790 12.0f, 13.0f, 14.0f, 1.0f); 1791 assert(m4.scale().matrix == [[0.0f, 0.0f, 0.0f, 0.0f], 1792 [0.0f, 5.0f, 0.0f, 0.0f], 1793 [0.0f, 0.0f, 10.0f, 0.0f], 1794 [0.0f, 0.0f, 0.0f, 1.0f]]); 1795 m4.scale(mat4.identity); 1796 assert(mat4.identity.matrix == m4.scale().matrix); 1797 m4.scale([0.0f, 5.0f, 10.0f]); 1798 assert(m4.scale().matrix == [[0.0f, 0.0f, 0.0f, 0.0f], 1799 [0.0f, 5.0f, 0.0f, 0.0f], 1800 [0.0f, 0.0f, 10.0f, 0.0f], 1801 [0.0f, 0.0f, 0.0f, 1.0f]]); 1802 assert(mat4.identity.matrix == mat4.identity.scale().matrix); 1803 } 1804 1805 /// Copies rot into the upper left corner, the translation (nxn matrices, n >= 3). 1806 void rotation(Matrix!(mt, 3, 3) rot) { 1807 foreach(r; TupleRange!(0, 3)) { 1808 foreach(c; TupleRange!(0, 3)) { 1809 matrix[r][c] = rot[r][c]; 1810 } 1811 } 1812 } 1813 1814 /// Returns an identity matrix with the current rotation applied (nxn matrices, n >= 3). 1815 Matrix!(mt, 3, 3) rotation() { 1816 Matrix!(mt, 3, 3) ret = Matrix!(mt, 3, 3).identity; 1817 1818 foreach(r; TupleRange!(0, 3)) { 1819 foreach(c; TupleRange!(0, 3)) { 1820 ret.matrix[r][c] = matrix[r][c]; 1821 } 1822 } 1823 1824 return ret; 1825 } 1826 1827 unittest { 1828 mat3 m3 = mat3(0.0f, 1.0f, 2.0f, 1829 3.0f, 4.0f, 5.0f, 1830 6.0f, 7.0f, 1.0f); 1831 assert(m3.rotation().matrix == [[0.0f, 1.0f, 2.0f], [3.0f, 4.0f, 5.0f], [6.0f, 7.0f, 1.0f]]); 1832 m3.rotation(mat3.identity); 1833 assert(mat3.identity.matrix == m3.rotation().matrix); 1834 m3.rotation(mat3(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 1.0f)); 1835 assert(m3.rotation().matrix == [[0.0f, 1.0f, 2.0f], [3.0f, 4.0f, 5.0f], [6.0f, 7.0f, 1.0f]]); 1836 assert(mat3.identity.matrix == mat3.identity.rotation().matrix); 1837 1838 mat4 m4 = mat4(0.0f, 1.0f, 2.0f, 3.0f, 1839 4.0f, 5.0f, 6.0f, 7.0f, 1840 8.0f, 9.0f, 10.0f, 11.0f, 1841 12.0f, 13.0f, 14.0f, 1.0f); 1842 assert(m4.rotation().matrix == [[0.0f, 1.0f, 2.0f], [4.0f, 5.0f, 6.0f], [8.0f, 9.0f, 10.0f]]); 1843 m4.rotation(mat3.identity); 1844 assert(mat3.identity.matrix == m4.rotation().matrix); 1845 m4.rotation(mat3(0.0f, 1.0f, 2.0f, 4.0f, 5.0f, 6.0f, 8.0f, 9.0f, 10.0f)); 1846 assert(m4.rotation().matrix == [[0.0f, 1.0f, 2.0f], [4.0f, 5.0f, 6.0f], [8.0f, 9.0f, 10.0f]]); 1847 assert(mat3.identity.matrix == mat4.identity.rotation().matrix); 1848 } 1849 1850 } 1851 1852 static if((rows == cols) && (rows >= 2) && (rows <= 4)) { 1853 /// Returns an inverted copy of the current matrix (nxn matrices, 2 >= n <= 4). 1854 Matrix inverse() const { 1855 Matrix mat; 1856 invert(mat); 1857 return mat; 1858 } 1859 1860 /// Inverts the current matrix (nxn matrices, 2 >= n <= 4). 1861 void invert() { 1862 // workaround Issue #11238 1863 // uses a temporary instead of invert(this) 1864 Matrix temp; 1865 invert(temp); 1866 this.matrix = temp.matrix; 1867 } 1868 } 1869 1870 unittest { 1871 mat2 m2 = mat2(1.0f, 2.0f, vec2(3.0f, 4.0f)); 1872 assert(m2.det == -2.0f); 1873 assert(m2.inverse.matrix == [[-2.0f, 1.0f], [1.5f, -0.5f]]); 1874 1875 mat3 m3 = mat3(1.0f, -2.0f, 3.0f, 1876 7.0f, -1.0f, 0.0f, 1877 3.0f, 2.0f, -4.0f); 1878 assert(m3.det == -1.0f); 1879 assert(m3.inverse.matrix == [[-4.0f, 2.0f, -3.0f], 1880 [-28.0f, 13.0f, -21.0f], 1881 [-17.0f, 8.0f, -13.0f]]); 1882 1883 mat4 m4 = mat4(1.0f, 2.0f, 3.0f, 4.0f, 1884 -2.0f, 1.0f, 5.0f, -2.0f, 1885 2.0f, -1.0f, 7.0f, 1.0f, 1886 3.0f, -3.0f, 2.0f, 0.0f); 1887 assert(m4.det == -8.0f); 1888 assert(m4.inverse.matrix == [[6.875f, 7.875f, -11.75f, 11.125f], 1889 [6.625f, 7.625f, -11.25f, 10.375f], 1890 [-0.375f, -0.375f, 0.75f, -0.625f], 1891 [-4.5f, -5.5f, 8.0f, -7.5f]]); 1892 } 1893 1894 private void mms(mt inp, ref Matrix mat) const { // mat * scalar 1895 for(int r = 0; r < rows; r++) { 1896 for(int c = 0; c < cols; c++) { 1897 mat.matrix[r][c] = matrix[r][c] * inp; 1898 } 1899 } 1900 } 1901 1902 private void masm(string op)(Matrix inp, ref Matrix mat) const { // mat + or - mat 1903 foreach(r; TupleRange!(0, rows)) { 1904 foreach(c; TupleRange!(0, cols)) { 1905 mat.matrix[r][c] = mixin("inp.matrix[r][c]" ~ op ~ "matrix[r][c]"); 1906 } 1907 } 1908 } 1909 1910 Matrix!(mt, rows, T.cols) opBinary(string op : "*", T)(T inp) const if(isCompatibleMatrix!T && (T.rows == cols)) { 1911 Matrix!(mt, rows, T.cols) ret; 1912 1913 foreach(r; TupleRange!(0, rows)) { 1914 foreach(c; TupleRange!(0, T.cols)) { 1915 ret.matrix[r][c] = 0; 1916 1917 foreach(c2; TupleRange!(0, cols)) { 1918 ret.matrix[r][c] += matrix[r][c2] * inp.matrix[c2][c]; 1919 } 1920 } 1921 } 1922 1923 return ret; 1924 } 1925 1926 Vector!(mt, rows) opBinary(string op : "*", T : Vector!(mt, cols))(T inp) const { 1927 Vector!(mt, rows) ret; 1928 ret.clear(0); 1929 1930 foreach(c; TupleRange!(0, cols)) { 1931 foreach(r; TupleRange!(0, rows)) { 1932 ret.vector[r] += matrix[r][c] * inp.vector[c]; 1933 } 1934 } 1935 1936 return ret; 1937 } 1938 1939 Matrix opBinary(string op : "*")(mt inp) const { 1940 Matrix ret; 1941 mms(inp, ret); 1942 return ret; 1943 } 1944 1945 Matrix opBinaryRight(string op : "*")(mt inp) const { 1946 return this.opBinary!(op)(inp); 1947 } 1948 1949 Matrix opBinary(string op)(Matrix inp) const if((op == "+") || (op == "-")) { 1950 Matrix ret; 1951 masm!(op)(inp, ret); 1952 return ret; 1953 } 1954 1955 void opOpAssign(string op : "*")(mt inp) { 1956 mms(inp, this); 1957 } 1958 1959 void opOpAssign(string op)(Matrix inp) if((op == "+") || (op == "-")) { 1960 masm!(op)(inp, this); 1961 } 1962 1963 void opOpAssign(string op)(Matrix inp) if(op == "*") { 1964 this = this * inp; 1965 } 1966 1967 unittest { 1968 mat2 m2 = mat2(1.0f, 2.0f, 3.0f, 4.0f); 1969 vec2 v2 = vec2(2.0f, 2.0f); 1970 assert((m2*2).matrix == [[2.0f, 4.0f], [6.0f, 8.0f]]); 1971 assert((2*m2).matrix == (m2*2).matrix); 1972 m2 *= 2; 1973 assert(m2.matrix == [[2.0f, 4.0f], [6.0f, 8.0f]]); 1974 assert((m2*v2).vector == [12.0f, 28.0f]); 1975 assert((v2*m2).vector == [16.0f, 24.0f]); 1976 assert((m2*m2).matrix == [[28.0f, 40.0f], [60.0f, 88.0f]]); 1977 assert((m2-m2).matrix == [[0.0f, 0.0f], [0.0f, 0.0f]]); 1978 assert((m2+m2).matrix == [[4.0f, 8.0f], [12.0f, 16.0f]]); 1979 m2 += m2; 1980 assert(m2.matrix == [[4.0f, 8.0f], [12.0f, 16.0f]]); 1981 m2 -= m2; 1982 assert(m2.matrix == [[0.0f, 0.0f], [0.0f, 0.0f]]); 1983 1984 mat3 m3 = mat3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f); 1985 vec3 v3 = vec3(2.0f, 2.0f, 2.0f); 1986 assert((m3*2).matrix == [[2.0f, 4.0f, 6.0f], [8.0f, 10.0f, 12.0f], [14.0f, 16.0f, 18.0f]]); 1987 assert((2*m3).matrix == (m3*2).matrix); 1988 m3 *= 2; 1989 assert(m3.matrix == [[2.0f, 4.0f, 6.0f], [8.0f, 10.0f, 12.0f], [14.0f, 16.0f, 18.0f]]); 1990 assert((m3*v3).vector == [24.0f, 60.0f, 96.0f]); 1991 assert((v3*m3).vector == [48.0f, 60.0f, 72.0f]); 1992 assert((m3*m3).matrix == [[120.0f, 144.0f, 168.0f], [264.0f, 324.0f, 384.0f], [408.0f, 504.0f, 600.0f]]); 1993 assert((m3-m3).matrix == [[0.0f, 0.0f, 0.0f], [0.0f, 0.0f, 0.0f], [0.0f, 0.0f, 0.0f]]); 1994 assert((m3+m3).matrix == [[4.0f, 8.0f, 12.0f], [16.0f, 20.0f, 24.0f], [28.0f, 32.0f, 36.0f]]); 1995 m3 += m3; 1996 assert(m3.matrix == [[4.0f, 8.0f, 12.0f], [16.0f, 20.0f, 24.0f], [28.0f, 32.0f, 36.0f]]); 1997 m3 -= m3; 1998 assert(m3.matrix == [[0.0f, 0.0f, 0.0f], [0.0f, 0.0f, 0.0f], [0.0f, 0.0f, 0.0f]]); 1999 2000 // test opOpAssign for matrix multiplication 2001 auto m4 = mat4.translation(0,1,2); 2002 m4 *= mat4.translation(0,-1,2); 2003 assert(m4 == mat4.translation(0,0,4)); 2004 2005 //TODO: tests for mat4, mat34 2006 } 2007 2008 unittest { 2009 assert(mat2(1.0f, 2.0f, 1.0f, 1.0f) == mat2(1.0f, 2.0f, 1.0f, 1.0f)); 2010 assert(mat2(1.0f, 2.0f, 1.0f, 1.0f) != mat2(1.0f, 1.0f, 1.0f, 1.0f)); 2011 2012 assert(mat3(1.0f) == mat3(1.0f)); 2013 assert(mat3(1.0f) != mat3(2.0f)); 2014 2015 assert(mat4(1.0f) == mat4(1.0f)); 2016 assert(mat4(1.0f) != mat4(2.0f)); 2017 2018 assert(!mat4(float.nan).isFinite); 2019 if(mat4(1.0f).isFinite) { } 2020 else { assert(false); } 2021 } 2022 } 2023 2024 /// Pre-defined matrix types, the first number represents the number of rows 2025 /// and the second the number of columns, if there's just one it's a nxn matrix. 2026 /// All of these matrices are floating-point matrices. 2027 alias mat2 = Matrix!(float, 2, 2); 2028 alias mat3 = Matrix!(float, 3, 3); 2029 alias mat34 = Matrix!(float, 3, 4); 2030 alias mat4 = Matrix!(float, 4, 4); 2031 2032 private unittest { 2033 Matrix!(float, 1, 1) A = 1; 2034 Matrix!(double, 1, 1) B = 1; 2035 Matrix!(real, 1, 1) C = 1; 2036 Matrix!(int, 1, 1) D = 1; 2037 Matrix!(float, 5, 1) E = 1; 2038 Matrix!(double, 5, 1) F = 1; 2039 Matrix!(real, 5, 1) G = 1; 2040 Matrix!(int, 5, 1) H = 1; 2041 Matrix!(float, 1, 5) I = 1; 2042 Matrix!(double, 1, 5) J = 1; 2043 Matrix!(real, 1, 5) K = 1; 2044 Matrix!(int, 1, 5) L = 1; 2045 } 2046 2047 /// Base template for all quaternion-types. 2048 /// Params: 2049 /// type = all values get stored as this type 2050 struct Quaternion(type) { 2051 alias qt = type; /// Holds the internal type of the quaternion. 2052 2053 qt[4] quaternion; /// Holds the w, x, y and z coordinates. 2054 2055 /// Returns a pointer to the quaternion in memory, it starts with the w coordinate. 2056 auto ptr() const { return quaternion.ptr; } 2057 2058 /// Returns the current vector formatted as string, useful for printing the quaternion. 2059 string toString() const { 2060 return format("%s", quaternion); 2061 } 2062 2063 /// Gets a hash of this item 2064 size_t toHash() const { return typeid(this).getHash(&this); } 2065 2066 @safe pure nothrow: 2067 qt get_(char coord)() const { 2068 return quaternion[coordToIndex!coord]; 2069 } 2070 void set_(char coord)(qt value) { 2071 quaternion[coordToIndex!coord] = value; 2072 } 2073 2074 alias w = get_!'w'; /// static properties to access the values. 2075 alias w = set_!'w'; 2076 alias x = get_!'x'; /// ditto 2077 alias x = set_!'x'; 2078 alias y = get_!'y'; /// ditto 2079 alias y = set_!'y'; 2080 alias z = get_!'z'; /// ditto 2081 alias z = set_!'z'; 2082 2083 /// Constructs the quaternion. 2084 /// Takes a 4-dimensional vector, where vector.x = the quaternions w coordinate, 2085 /// or a w coordinate of type $(I qt) and a 3-dimensional vector representing the imaginary part, 2086 /// or 4 values of type $(I qt). 2087 this(qt w_, qt x_, qt y_, qt z_) { 2088 w = w_; 2089 x = x_; 2090 y = y_; 2091 z = z_; 2092 } 2093 2094 /// ditto 2095 this(qt w_, Vector!(qt, 3) vec) { 2096 w = w_; 2097 quaternion[1..4] = vec.vector[]; 2098 } 2099 2100 /// ditto 2101 this(Vector!(qt, 4) vec) { 2102 quaternion[] = vec.vector[]; 2103 } 2104 2105 /// Returns true if all values are not nan and finite, otherwise false. 2106 bool isFinite() const { 2107 foreach(q; quaternion) { 2108 if(isNaN(q) || isInfinity(q)) { 2109 return false; 2110 } 2111 } 2112 return true; 2113 } 2114 deprecated("Use isFinite instead of ok") alias ok = isFinite; 2115 2116 unittest { 2117 quat q1 = quat(0.0f, 0.0f, 0.0f, 1.0f); 2118 assert(q1.quaternion == [0.0f, 0.0f, 0.0f, 1.0f]); 2119 assert(q1.quaternion == quat(0.0f, 0.0f, 0.0f, 1.0f).quaternion); 2120 assert(q1.quaternion == quat(0.0f, vec3(0.0f, 0.0f, 1.0f)).quaternion); 2121 assert(q1.quaternion == quat(vec4(0.0f, 0.0f, 0.0f, 1.0f)).quaternion); 2122 2123 assert(q1.isFinite); 2124 q1.x = float.infinity; 2125 assert(!q1.isFinite); 2126 q1.x = float.nan; 2127 assert(!q1.isFinite); 2128 q1.x = 0.0f; 2129 assert(q1.isFinite); 2130 } 2131 2132 template coordToIndex(char c) { 2133 static if(c == 'w') { 2134 enum coordToIndex = 0; 2135 } else static if(c == 'x') { 2136 enum coordToIndex = 1; 2137 } else static if(c == 'y') { 2138 enum coordToIndex = 2; 2139 } else static if(c == 'z') { 2140 enum coordToIndex = 3; 2141 } else { 2142 static assert(false, "accepted coordinates are x, y, z and w not " ~ c ~ "."); 2143 } 2144 } 2145 2146 /// Returns the squared magnitude of the quaternion. 2147 real magnitudeSquared() const { 2148 return to!real(w^^2 + x^^2 + y^^2 + z^^2); 2149 } 2150 2151 /// Returns the magnitude of the quaternion. 2152 real magnitude() const { 2153 return sqrt(magnitudeSquared); 2154 } 2155 2156 /// Returns an identity quaternion (w=1, x=0, y=0, z=0). 2157 static Quaternion identity() { 2158 return Quaternion(1, 0, 0, 0); 2159 } 2160 2161 /// Makes the current quaternion an identity quaternion. 2162 void makeIdentity() { 2163 w = 1; 2164 x = 0; 2165 y = 0; 2166 z = 0; 2167 } 2168 2169 /// Inverts the quaternion. 2170 void invert() { 2171 x = -x; 2172 y = -y; 2173 z = -z; 2174 } 2175 alias conjugate = invert; /// ditto 2176 2177 /// Returns an inverted copy of the current quaternion. 2178 Quaternion inverse() const { 2179 return Quaternion(w, -x, -y, -z); 2180 } 2181 alias conjugated = inverse; /// ditto 2182 2183 unittest { 2184 quat q1 = quat(1.0f, 1.0f, 1.0f, 1.0f); 2185 2186 assert(q1.magnitude == 2.0f); 2187 assert(q1.magnitudeSquared == 4.0f); 2188 assert(q1.magnitude == quat(0.0f, 0.0f, 2.0f, 0.0f).magnitude); 2189 2190 quat q2 = quat.identity; 2191 assert(q2.quaternion == [1.0f, 0.0f, 0.0f, 0.0f]); 2192 assert(q2.x == 0.0f); 2193 assert(q2.y == 0.0f); 2194 assert(q2.z == 0.0f); 2195 assert(q2.w == 1.0f); 2196 2197 assert(q1.inverse.quaternion == [1.0f, -1.0f, -1.0f, -1.0f]); 2198 q1.invert(); 2199 assert(q1.quaternion == [1.0f, -1.0f, -1.0f, -1.0f]); 2200 2201 q1.makeIdentity(); 2202 assert(q1.quaternion == q2.quaternion); 2203 2204 } 2205 2206 /// Creates a quaternion from a 3x3 matrix. 2207 /// Params: 2208 /// matrix = 3x3 matrix (rotation) 2209 /// Returns: A quaternion representing the rotation (3x3 matrix) 2210 static Quaternion fromMatrix(Matrix!(qt, 3, 3) matrix) { 2211 Quaternion ret; 2212 2213 auto mat = matrix.matrix; 2214 qt trace = mat[0][0] + mat[1][1] + mat[2][2]; 2215 2216 if(trace > 0) { 2217 real s = 0.5 / sqrt(trace + 1.0f); 2218 2219 ret.w = to!qt(0.25 / s); 2220 ret.x = to!qt((mat[2][1] - mat[1][2]) * s); 2221 ret.y = to!qt((mat[0][2] - mat[2][0]) * s); 2222 ret.z = to!qt((mat[1][0] - mat[0][1]) * s); 2223 } else if((mat[0][0] > mat[1][1]) && (mat[0][0] > mat[2][2])) { 2224 real s = 2.0 * sqrt(1.0 + mat[0][0] - mat[1][1] - mat[2][2]); 2225 2226 ret.w = to!qt((mat[2][1] - mat[1][2]) / s); 2227 ret.x = to!qt(0.25f * s); 2228 ret.y = to!qt((mat[0][1] + mat[1][0]) / s); 2229 ret.z = to!qt((mat[0][2] + mat[2][0]) / s); 2230 } else if(mat[1][1] > mat[2][2]) { 2231 real s = 2.0 * sqrt(1 + mat[1][1] - mat[0][0] - mat[2][2]); 2232 2233 ret.w = to!qt((mat[0][2] - mat[2][0]) / s); 2234 ret.x = to!qt((mat[0][1] + mat[1][0]) / s); 2235 ret.y = to!qt(0.25f * s); 2236 ret.z = to!qt((mat[1][2] + mat[2][1]) / s); 2237 } else { 2238 real s = 2.0 * sqrt(1 + mat[2][2] - mat[0][0] - mat[1][1]); 2239 2240 ret.w = to!qt((mat[1][0] - mat[0][1]) / s); 2241 ret.x = to!qt((mat[0][2] + mat[2][0]) / s); 2242 ret.y = to!qt((mat[1][2] + mat[2][1]) / s); 2243 ret.z = to!qt(0.25f * s); 2244 } 2245 2246 return ret; 2247 } 2248 2249 /// Returns the quaternion as matrix. 2250 /// Params: 2251 /// rows = number of rows of the resulting matrix (min 3) 2252 /// cols = number of columns of the resulting matrix (min 3) 2253 Matrix!(qt, rows, cols) toMatrix(int rows, int cols)() const if((rows >= 3) && (cols >= 3)) { 2254 static if((rows == 3) && (cols == 3)) { 2255 Matrix!(qt, rows, cols) ret; 2256 } else { 2257 Matrix!(qt, rows, cols) ret = Matrix!(qt, rows, cols).identity; 2258 } 2259 2260 qt xx = x^^2; 2261 qt xy = x * y; 2262 qt xz = x * z; 2263 qt xw = x * w; 2264 qt yy = y^^2; 2265 qt yz = y * z; 2266 qt yw = y * w; 2267 qt zz = z^^2; 2268 qt zw = z * w; 2269 2270 ret.matrix[0][0] = 1 - 2 * (yy + zz); 2271 ret.matrix[0][1] = 2 * (xy - zw); 2272 ret.matrix[0][2] = 2 * (xz + yw); 2273 2274 ret.matrix[1][0] = 2 * (xy + zw); 2275 ret.matrix[1][1] = 1 - 2 * (xx + zz); 2276 ret.matrix[1][2] = 2 * (yz - xw); 2277 2278 ret.matrix[2][0] = 2 * (xz - yw); 2279 ret.matrix[2][1] = 2 * (yz + xw); 2280 ret.matrix[2][2] = 1 - 2 * (xx + yy); 2281 2282 return ret; 2283 } 2284 2285 unittest { 2286 quat q1 = quat(4.0f, 1.0f, 2.0f, 3.0f); 2287 2288 assert(q1.toMatrix!(3, 3).matrix == [[-25.0f, -20.0f, 22.0f], [28.0f, -19.0f, 4.0f], [-10.0f, 20.0f, -9.0f]]); 2289 assert(q1.toMatrix!(4, 4).matrix == [[-25.0f, -20.0f, 22.0f, 0.0f], 2290 [28.0f, -19.0f, 4.0f, 0.0f], 2291 [-10.0f, 20.0f, -9.0f, 0.0f], 2292 [0.0f, 0.0f, 0.0f, 1.0f]]); 2293 assert(quat.identity.toMatrix!(3, 3).matrix == Matrix!(qt, 3, 3).identity.matrix); 2294 assert(q1.quaternion == quat.fromMatrix(q1.toMatrix!(3, 3)).quaternion); 2295 2296 assert(quat(1.0f, 0.0f, 0.0f, 0.0f).quaternion == quat.fromMatrix(mat3.identity).quaternion); 2297 2298 quat q2 = quat.fromMatrix(mat3(1.0f, 3.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); 2299 assert(q2.x == 0.0f); 2300 assert(almostEqual(q2.y, 0.7071067f)); 2301 assert(almostEqual(q2.z, -1.060660)); 2302 assert(almostEqual(q2.w, 0.7071067f)); 2303 } 2304 2305 /// Returns the quaternion as a vec3 (axis / angle representation). 2306 vec3 toAxisAngle() { 2307 vec3 ret; 2308 quat this_normalized = this.normalized(); 2309 real angle = 2 * acos(this_normalized.w); 2310 real denominator = sqrt(1.0 - (this_normalized.w)^^2); 2311 2312 if (almostEqual(denominator, 0)) { // Avoid divide by 0 2313 ret.x = 1; 2314 ret.y = ret.z = 0; 2315 } 2316 else { 2317 ret.x = x / denominator; 2318 ret.y = y / denominator; 2319 ret.z = z / denominator; 2320 } 2321 2322 return ret * angle; 2323 } 2324 unittest { 2325 // See https://www.energid.com/resources/orientation-calculator 2326 2327 void testPair(quat q, vec3 v) { 2328 vec3 q2v = q.toAxisAngle(); 2329 assert(almostEqual(q2v.x, q2v.x) // epsilon = 0.000001f 2330 && almostEqual(q2v.y, q2v.y) 2331 && almostEqual(q2v.z, q2v.z), 2332 "to_axisAngle does not yield a correct vector."); 2333 } 2334 2335 quat q1 = quat(0.5, 0.5, 0.5, 0.5); 2336 vec3 v1 = vec3(1.2091996); 2337 testPair(q1, v1); 2338 2339 q1 = quat(0.1825742, 0.3651484, 0.5477226, 0.7302967); 2340 v1 = vec3(1.030380653, 1.54557098, 2.060761024); 2341 testPair(q1, v1); 2342 2343 q1 = quat(0, 0, 0, 1); 2344 v1 = vec3(0, 0, 0); 2345 testPair(q1, v1); 2346 2347 q1 = quat(-0.1961161, 0.4358136, 0.8716273, 0.1089534); 2348 v1 = vec3(-1.220800604, -2.441601488, -0.305200151); 2349 testPair(q1, v1); 2350 } 2351 2352 /// Normalizes the current quaternion. 2353 void normalize() { 2354 qt m = to!qt(magnitude); 2355 2356 if(m != 0) { 2357 w = w / m; 2358 x = x / m; 2359 y = y / m; 2360 z = z / m; 2361 } 2362 } 2363 2364 /// Returns a normalized copy of the current quaternion. 2365 Quaternion normalized() const { 2366 Quaternion ret; 2367 qt m = to!qt(magnitude); 2368 2369 if(m != 0) { 2370 ret.w = w / m; 2371 ret.x = x / m; 2372 ret.y = y / m; 2373 ret.z = z / m; 2374 } else { 2375 ret = Quaternion(w, x, y, z); 2376 } 2377 2378 return ret; 2379 } 2380 2381 unittest { 2382 quat q1 = quat(1.0f, 2.0f, 3.0f, 4.0f); 2383 quat q2 = quat(1.0f, 2.0f, 3.0f, 4.0f); 2384 2385 q1.normalize(); 2386 assert(q1.quaternion == q2.normalized.quaternion); 2387 //assert(q1.quaternion == q1.normalized.quaternion); 2388 assert(almostEqual(q1.magnitude, 1.0)); 2389 } 2390 2391 /// Returns the yaw. 2392 real yaw() const { 2393 return atan2(to!real(2.0*(w*z + x*y)), to!real(1.0 - 2.0*(y*y + z*z))); 2394 } 2395 2396 /// Returns the pitch. 2397 real pitch() const { 2398 return asin(to!real(2.0*(w*y - z*x))); 2399 } 2400 2401 /// Returns the roll. 2402 real roll() const { 2403 return atan2(to!real(2.0*(w*x + y*z)), to!real(1.0 - 2.0*(x*x + y*y))); 2404 } 2405 2406 unittest { 2407 quat q1 = quat.identity; 2408 assert(q1.pitch == 0.0f); 2409 assert(q1.yaw == 0.0f); 2410 assert(q1.roll == 0.0f); 2411 2412 quat q2 = quat(1.0f, 1.0f, 1.0f, 1.0f); 2413 assert(almostEqual(q2.yaw, q2.roll)); 2414 //assert(almostEqual(q2.yaw, 1.570796f)); 2415 assert(q2.pitch == 0.0f); 2416 2417 quat q3 = quat(0.1f, 1.9f, 2.1f, 1.3f); 2418 //assert(almostEqual(q3.yaw, 2.4382f)); 2419 assert(isNaN(q3.pitch)); 2420 //assert(almostEqual(q3.roll, 1.67719f)); 2421 } 2422 2423 /// Returns a quaternion with applied rotation around the x-axis. 2424 static Quaternion xRotation(real alpha) { 2425 Quaternion ret; 2426 2427 alpha /= 2; 2428 ret.w = to!qt(cos(alpha)); 2429 ret.x = to!qt(sin(alpha)); 2430 ret.y = 0; 2431 ret.z = 0; 2432 2433 return ret; 2434 } 2435 2436 /// Returns a quaternion with applied rotation around the y-axis. 2437 static Quaternion yRotation(real alpha) { 2438 Quaternion ret; 2439 2440 alpha /= 2; 2441 ret.w = to!qt(cos(alpha)); 2442 ret.x = 0; 2443 ret.y = to!qt(sin(alpha)); 2444 ret.z = 0; 2445 2446 return ret; 2447 } 2448 2449 /// Returns a quaternion with applied rotation around the z-axis. 2450 static Quaternion zRotation(real alpha) { 2451 Quaternion ret; 2452 2453 alpha /= 2; 2454 ret.w = to!qt(cos(alpha)); 2455 ret.x = 0; 2456 ret.y = 0; 2457 ret.z = to!qt(sin(alpha)); 2458 2459 return ret; 2460 } 2461 2462 /// Returns a quaternion with applied rotation around an axis. 2463 static Quaternion axisRotation(real alpha, Vector!(qt, 3) axis) { 2464 if(alpha == 0) { 2465 return Quaternion.identity; 2466 } 2467 Quaternion ret; 2468 2469 alpha /= 2; 2470 qt sinaqt = to!qt(sin(alpha)); 2471 2472 ret.w = to!qt(cos(alpha)); 2473 ret.x = axis.x * sinaqt; 2474 ret.y = axis.y * sinaqt; 2475 ret.z = axis.z * sinaqt; 2476 2477 return ret; 2478 } 2479 2480 /// Creates a quaternion from an euler rotation. 2481 static Quaternion eulerRotation(real roll, real pitch, real yaw) { 2482 Quaternion ret; 2483 2484 auto cr = cos(roll / 2.0); 2485 auto cp = cos(pitch / 2.0); 2486 auto cy = cos(yaw / 2.0); 2487 auto sr = sin(roll / 2.0); 2488 auto sp = sin(pitch / 2.0); 2489 auto sy = sin(yaw / 2.0); 2490 2491 ret.w = cr * cp * cy + sr * sp * sy; 2492 ret.x = sr * cp * cy - cr * sp * sy; 2493 ret.y = cr * sp * cy + sr * cp * sy; 2494 ret.z = cr * cp * sy - sr * sp * cy; 2495 2496 return ret; 2497 } 2498 2499 @("quat eulerRotation") 2500 unittest { 2501 enum startPitch = 0.1; 2502 enum startYaw = -0.2; 2503 enum startRoll = 0.6; 2504 2505 auto q = quat.eulerRotation(startRoll,startPitch,startYaw); 2506 2507 assert(almostEqual(q.pitch,startPitch)); 2508 assert(almostEqual(q.yaw,startYaw)); 2509 assert(almostEqual(q.roll,startRoll)); 2510 } 2511 2512 /** 2513 Makes a quaternion with the specified forward and upwards directions in a Unity compatible manner. 2514 Implementation from http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html 2515 */ 2516 static Quaternion lookRotation(vec3 forward, vec3 up) { 2517 forward = forward.normalized; 2518 vec3 right = cross(up, forward).normalized; 2519 up = cross(forward, right); 2520 2521 qt m00 = right.x; 2522 qt m01 = right.y; 2523 qt m02 = right.z; 2524 qt m10 = up.x; 2525 qt m11 = up.y; 2526 qt m12 = up.z; 2527 qt m20 = forward.x; 2528 qt m21 = forward.y; 2529 qt m22 = forward.z; 2530 2531 qt num8 = (m00 + m11) + m22; 2532 Quaternion!qt quat = Quaternion!qt.identity; 2533 if (num8 > 0) { 2534 qt num = sqrt(num8 + 1.0); 2535 quat.w = num * 0.5f; 2536 num = 0.5f/num; 2537 quat.x = (m12 - m21) * num; 2538 quat.y = (m20 - m02) * num; 2539 quat.z = (m01 - m10) * num; 2540 return quat; 2541 } 2542 if ((m00 >= m11) && (m00 >= m22)) { 2543 qt num7 = sqrt(((1f + m00) - m11) - m22); 2544 qt num4 = 0.5f / num7; 2545 quat.x = 0.5f * num7; 2546 quat.y = (m01 + m10) * num4; 2547 quat.z = (m02 + m20) * num4; 2548 quat.w = (m12 - m21) * num4; 2549 return quat; 2550 } 2551 if (m11 > m22) 2552 { 2553 qt num6 = sqrt(((1f + m11) - m00) - m22); 2554 qt num3 = 0.5f / num6; 2555 quat.x = (m10 + m01) * num3; 2556 quat.y = 0.5f * num6; 2557 quat.z = (m21 + m12) * num3; 2558 quat.w = (m20 - m02) * num3; 2559 return quat; 2560 } 2561 qt num5 = sqrt(((1f + m22) - m00) - m11); 2562 qt num2 = 0.5f / num5; 2563 quat.x = (m20 + m02) * num2; 2564 quat.y = (m21 + m12) * num2; 2565 quat.z = 0.5f * num5; 2566 quat.w = (m01 - m10) * num2; 2567 return quat; 2568 } 2569 2570 @("quat lookRotation") 2571 unittest { 2572 // TODO: add unittest 2573 } 2574 2575 /// Rotates the current quaternion around the x-axis and returns $(I this). 2576 Quaternion rotateX(real alpha) { 2577 this = xRotation(alpha) * this; 2578 return this; 2579 } 2580 2581 /// Rotates the current quaternion around the y-axis and returns $(I this). 2582 Quaternion rotateY(real alpha) { 2583 this = yRotation(alpha) * this; 2584 return this; 2585 } 2586 2587 /// Rotates the current quaternion around the z-axis and returns $(I this). 2588 Quaternion rotateZ(real alpha) { 2589 this = zRotation(alpha) * this; 2590 return this; 2591 } 2592 2593 /// Rotates the current quaternion around an axis and returns $(I this). 2594 Quaternion rotateAxis(real alpha, Vector!(qt, 3) axis) { 2595 this = axisRotation(alpha, axis) * this; 2596 return this; 2597 } 2598 2599 /// Applies an euler rotation to the current quaternion and returns $(I this). 2600 Quaternion rotateEuler(real heading, real attitude, real bank) { 2601 this = eulerRotation(heading, attitude, bank) * this; 2602 return this; 2603 } 2604 2605 @("quat x/y/z/axis rotation") 2606 unittest { 2607 assert(quat.xRotation(PI).quaternion[1..4] == [1.0f, 0.0f, 0.0f]); 2608 assert(quat.yRotation(PI).quaternion[1..4] == [0.0f, 1.0f, 0.0f]); 2609 assert(quat.zRotation(PI).quaternion[1..4] == [0.0f, 0.0f, 1.0f]); 2610 assert((quat.xRotation(PI).w == quat.yRotation(PI).w) && (quat.yRotation(PI).w == quat.zRotation(PI).w)); 2611 //assert(quat.rotateX(PI).w == to!(quat.qt)(cos(PI))); 2612 assert(quat.xRotation(PI).quaternion == quat.identity.rotateX(PI).quaternion); 2613 assert(quat.yRotation(PI).quaternion == quat.identity.rotateY(PI).quaternion); 2614 assert(quat.zRotation(PI).quaternion == quat.identity.rotateZ(PI).quaternion); 2615 2616 assert(quat.axisRotation(PI, vec3(1.0f, 1.0f, 1.0f)).quaternion[1..4] == [1.0f, 1.0f, 1.0f]); 2617 assert(quat.axisRotation(PI, vec3(1.0f, 1.0f, 1.0f)).w == quat.xRotation(PI).w); 2618 assert(quat.axisRotation(PI, vec3(1.0f, 1.0f, 1.0f)).quaternion == 2619 quat.identity.rotateAxis(PI, vec3(1.0f, 1.0f, 1.0f)).quaternion); 2620 2621 quat q1 = quat.eulerRotation(PI, PI, PI); 2622 assert((q1.x > -1e-16) && (q1.x < 1e-16)); 2623 assert((q1.y > -1e-16) && (q1.y < 1e-16)); 2624 assert((q1.z > -1e-16) && (q1.z < 1e-16)); 2625 //assert(q1.w == -1.0f); 2626 assert(quat.eulerRotation(PI, PI, PI).quaternion == quat.identity.rotateEuler(PI, PI, PI).quaternion); 2627 } 2628 2629 Quaternion opBinary(string op : "*")(Quaternion inp) const { 2630 Quaternion ret; 2631 2632 ret.w = -x * inp.x - y * inp.y - z * inp.z + w * inp.w; 2633 ret.x = x * inp.w + y * inp.z - z * inp.y + w * inp.x; 2634 ret.y = -x * inp.z + y * inp.w + z * inp.x + w * inp.y; 2635 ret.z = x * inp.y - y * inp.x + z * inp.w + w * inp.z; 2636 2637 return ret; 2638 } 2639 2640 auto opBinaryRight(string op, T)(T inp) const if(!isQuaternion!T) { 2641 return this.opBinary!(op)(inp); 2642 } 2643 2644 Quaternion opBinary(string op)(Quaternion inp) const if((op == "+") || (op == "-")) { 2645 Quaternion ret; 2646 2647 mixin("ret.w = w" ~ op ~ "inp.w;"); 2648 mixin("ret.x = x" ~ op ~ "inp.x;"); 2649 mixin("ret.y = y" ~ op ~ "inp.y;"); 2650 mixin("ret.z = z" ~ op ~ "inp.z;"); 2651 2652 return ret; 2653 } 2654 2655 Vector!(qt, 3) opBinary(string op : "*")(Vector!(qt, 3) inp) const { 2656 Vector!(qt, 3) ret; 2657 2658 qt ww = w^^2; 2659 qt w2 = w * 2; 2660 qt wx2 = w2 * x; 2661 qt wy2 = w2 * y; 2662 qt wz2 = w2 * z; 2663 qt xx = x^^2; 2664 qt x2 = x * 2; 2665 qt xy2 = x2 * y; 2666 qt xz2 = x2 * z; 2667 qt yy = y^^2; 2668 qt yz2 = 2 * y * z; 2669 qt zz = z * z; 2670 2671 ret.vector = [ww * inp.x + wy2 * inp.z - wz2 * inp.y + xx * inp.x + 2672 xy2 * inp.y + xz2 * inp.z - zz * inp.x - yy * inp.x, 2673 xy2 * inp.x + yy * inp.y + yz2 * inp.z + wz2 * inp.x - 2674 zz * inp.y + ww * inp.y - wx2 * inp.z - xx * inp.y, 2675 xz2 * inp.x + yz2 * inp.y + zz * inp.z - wy2 * inp.x - 2676 yy * inp.z + wx2 * inp.y - xx * inp.z + ww * inp.z]; 2677 2678 return ret; 2679 } 2680 2681 Quaternion opBinary(string op : "*")(qt inp) const { 2682 return Quaternion(w*inp, x*inp, y*inp, z*inp); 2683 } 2684 2685 void opOpAssign(string op : "*")(Quaternion inp) { 2686 qt w2 = -x * inp.x - y * inp.y - z * inp.z + w * inp.w; 2687 qt x2 = x * inp.w + y * inp.z - z * inp.y + w * inp.x; 2688 qt y2 = -x * inp.z + y * inp.w + z * inp.x + w * inp.y; 2689 qt z2 = x * inp.y - y * inp.x + z * inp.w + w * inp.z; 2690 w = w2; x = x2; y = y2; z = z2; 2691 } 2692 2693 void opOpAssign(string op)(Quaternion inp) if((op == "+") || (op == "-")) { 2694 mixin("w = w" ~ op ~ "inp.w;"); 2695 mixin("x = x" ~ op ~ "inp.x;"); 2696 mixin("y = y" ~ op ~ "inp.y;"); 2697 mixin("z = z" ~ op ~ "inp.z;"); 2698 } 2699 2700 void opOpAssign(string op : "*")(qt inp) { 2701 quaternion[0] *= inp; 2702 quaternion[1] *= inp; 2703 quaternion[2] *= inp; 2704 quaternion[3] *= inp; 2705 } 2706 2707 unittest { 2708 quat q1 = quat.identity; 2709 quat q2 = quat(3.0f, 0.0f, 1.0f, 2.0f); 2710 quat q3 = quat(3.4f, 0.1f, 1.2f, 2.3f); 2711 2712 assert((q1 * q1).quaternion == q1.quaternion); 2713 assert((q1 * q2).quaternion == q2.quaternion); 2714 assert((q2 * q1).quaternion == q2.quaternion); 2715 quat q4 = q3 * q2; 2716 assert((q2 * q3).quaternion != q4.quaternion); 2717 q3 *= q2; 2718 assert(q4.quaternion == q3.quaternion); 2719 assert(almostEqual(q4.x, 0.4f)); 2720 assert(almostEqual(q4.y, 6.8f)); 2721 assert(almostEqual(q4.z, 13.8f)); 2722 assert(almostEqual(q4.w, 4.4f)); 2723 2724 quat q5 = quat(1.0f, 2.0f, 3.0f, 4.0f); 2725 quat q6 = quat(3.0f, 1.0f, 6.0f, 2.0f); 2726 2727 assert((q5 - q6).quaternion == [-2.0f, 1.0f, -3.0f, 2.0f]); 2728 assert((q5 + q6).quaternion == [4.0f, 3.0f, 9.0f, 6.0f]); 2729 assert((q6 - q5).quaternion == [2.0f, -1.0f, 3.0f, -2.0f]); 2730 assert((q6 + q5).quaternion == [4.0f, 3.0f, 9.0f, 6.0f]); 2731 q5 += q6; 2732 assert(q5.quaternion == [4.0f, 3.0f, 9.0f, 6.0f]); 2733 q6 -= q6; 2734 assert(q6.quaternion == [0.0f, 0.0f, 0.0f, 0.0f]); 2735 2736 quat q7 = quat(2.0f, 2.0f, 2.0f, 2.0f); 2737 assert((q7 * 2).quaternion == [4.0f, 4.0f, 4.0f, 4.0f]); 2738 assert((2 * q7).quaternion == (q7 * 2).quaternion); 2739 q7 *= 2; 2740 assert(q7.quaternion == [4.0f, 4.0f, 4.0f, 4.0f]); 2741 2742 vec3 v1 = vec3(1.0f, 2.0f, 3.0f); 2743 assert((q1 * v1).vector == v1.vector); 2744 assert((v1 * q1).vector == (q1 * v1).vector); 2745 assert((q2 * v1).vector == [-2.0f, 36.0f, 38.0f]); 2746 } 2747 2748 int opCmp(ref const Quaternion qua) const { 2749 foreach(i, a; quaternion) { 2750 if(a < qua.quaternion[i]) { 2751 return -1; 2752 } else if(a > qua.quaternion[i]) { 2753 return 1; 2754 } 2755 } 2756 2757 // Quaternions are the same 2758 return 0; 2759 } 2760 2761 bool opEquals(const Quaternion qu) const { 2762 return quaternion == qu.quaternion; 2763 } 2764 2765 unittest { 2766 assert(quat(1.0f, 2.0f, 3.0f, 4.0f) == quat(1.0f, 2.0f, 3.0f, 4.0f)); 2767 assert(quat(1.0f, 2.0f, 3.0f, 4.0f) != quat(1.0f, 2.0f, 3.0f, 3.0f)); 2768 2769 assert(!quat(float.nan, float.nan, float.nan, float.nan).isFinite); 2770 if(quat(1.0f, 1.0f, 1.0f, 1.0f).isFinite) { } 2771 else { assert(false); } 2772 } 2773 } 2774 2775 /// Pre-defined quaternion of type float. 2776 alias quat = Quaternion!(float); 2777 2778 struct Rect(type) { 2779 alias rt = type; /// Holds the internal type of the rect. 2780 alias RectT = Rect!type; 2781 2782 union { 2783 struct { 2784 rt x; 2785 rt y; 2786 rt width; 2787 rt height; 2788 } 2789 rt[4] elements; /// Holds the x, y, width and height 2790 } 2791 2792 /// Returns a pointer to the quaternion in memory, it starts with the w coordinate. 2793 auto ptr() const { return elements.ptr; } 2794 @safe pure nothrow: 2795 2796 /** 2797 returns identity rect 2798 */ 2799 static auto identity() { 2800 return RectT(0, 0, 0, 0); 2801 } 2802 2803 /** 2804 Left coordinate of the rect 2805 */ 2806 rt left() const { 2807 return x; 2808 } 2809 2810 /** 2811 Right coordinate of the rect 2812 */ 2813 rt right() const { 2814 return x+width; 2815 } 2816 2817 /** 2818 Top coordinate of the rect 2819 */ 2820 rt top() const { 2821 return y; 2822 } 2823 2824 /** 2825 Bottom coordinate of the rect 2826 */ 2827 rt bottom() const { 2828 return y+height; 2829 } 2830 2831 /** 2832 Gets the center of a rect 2833 */ 2834 Vector!(rt, 2) center() const { 2835 return Vector!(rt, 2)(this.x + (this.width/2), this.y + (this.height/2)); 2836 } 2837 2838 /** 2839 Gets the corner of a rect 2840 */ 2841 Vector!(rt, 2) corner() const { 2842 return Vector!(rt, 2)(this.x, this.y); 2843 } 2844 2845 /** 2846 Gets the dimensions of a rect 2847 */ 2848 Vector!(rt, 2) dimensions() const { 2849 return Vector!(rt, 2)(this.width, this.height); 2850 } 2851 2852 /** 2853 Gets whether this rect intersects another rect 2854 */ 2855 bool intersects(rtype)(rtype other) const if (isRect!rtype) { 2856 return !(other.left >= this.right || other.right <= this.left || other.top >= this.bottom || other.bottom <= this.top); 2857 } 2858 2859 /** 2860 Gets whether this rect intersects a vector 2861 */ 2862 bool intersects(vtype)(vtype other) const if (isVector!vtype) { 2863 return !(other.x >= this.right || other.x <= this.left || other.y >= this.bottom || other.y <= this.top); 2864 } 2865 2866 /** 2867 Displaces the rect by the specified amount 2868 */ 2869 void displace(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2870 this.x += other.x; 2871 this.y += other.y; 2872 } 2873 2874 /** 2875 Gets a rect that has been displaced by the specified amount 2876 */ 2877 RectT displaced(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2878 return RectT(this.x+other.x, this.y+other.y, this.width, this.height); 2879 } 2880 2881 /** 2882 Expands the rect by the specified amount from the center 2883 */ 2884 void expand(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2885 this.x -= other.x; 2886 this.y -= other.y; 2887 this.width += other.x*2; 2888 this.height += other.y*2; 2889 } 2890 2891 /** 2892 Expands the rect by the specified amount from the corner 2893 */ 2894 void expandSize(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2895 this.width += other.x; 2896 this.height += other.y; 2897 } 2898 2899 /** 2900 Gets a rect that has been expanded by the specified amount from the center 2901 */ 2902 RectT expanded(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2903 return RectT(this.x-other.x, this.y-other.y, this.width+(other.x*2), this.height+(other.y*2)); 2904 } 2905 2906 /** 2907 Expands the rect by the specified amount from the corner 2908 */ 2909 RectT expandedSize(vtype)(vtype other) const if (isVector!vtype && vtype.dimension == 2) { 2910 return RectT(this.x, this.y, this.width+other.x, this.height+other.y); 2911 } 2912 2913 /** 2914 Gets the UV coordinates of each corner and returns them as a vec4 of the rect's type 2915 */ 2916 Vector!(rt, 4) uvs() const { 2917 return Vector!(rt, 4)(this.left, this.top, this.right, this.bottom); 2918 } 2919 } 2920 2921 alias rect = Rect!(float); 2922 alias rectd = Rect!(double); 2923 2924 @("rect intersects") 2925 unittest { 2926 rect a = rect(0, 0, 32, 32); 2927 rectd b = rectd(16, 16, 32, 32); 2928 rect c = rect(0, 32, 32, 32); 2929 vec2 p = vec2(8, 8); 2930 2931 assert(a.intersects(b)); 2932 assert(b.intersects(c)); 2933 assert(!a.intersects(c)); 2934 2935 assert(a.intersects(p)); 2936 assert(!b.intersects(p)); 2937 assert(!c.intersects(p)); 2938 } 2939 2940 @("rect uvs") 2941 unittest { 2942 assert(rect(16, 16, 32, 64).uvs == vec4(16, 16, 16+32, 16+64)); 2943 } 2944 2945 @("rect center") 2946 unittest { 2947 rect a = rect(0, 0, 32, 32); 2948 assert(a.center == vec2(16, 16)); 2949 }