1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/math/matrix.d, _matrix.d) 6 * Documentation: 7 * Coverage: 8 */ 9 module liberty.math.matrix; 10 import liberty.math.vector : Vector; 11 import liberty.core.config : __RowMajor__, __ColumnMajor__; 12 import std.traits : isFloatingPoint; 13 /// 14 enum MatrixOrder : ubyte { 15 /// 16 RowMajor = 0x00, 17 /// 18 ColumnMajor = 0x01 19 } 20 static if (__RowMajor__) { 21 enum CurrentMatrixOrder = MatrixOrder.RowMajor; 22 } else { 23 enum CurrentMatrixOrder = MatrixOrder.ColumnMajor; 24 } 25 /// T = Type of elements. 26 /// R = Number of rows. 27 /// C = Number of columns. 28 /// O = Matrix order. It can be RowMajor or ColumnMajor. 29 struct Matrix(T, ubyte R, ubyte C = R, MatrixOrder O = CurrentMatrixOrder) if (R >= 2 && R <= 4 && C >= 2 && C <= 4) { 30 /// 31 alias type = T; 32 /// 33 enum rowCount = R; 34 /// 35 enum columnCount = C; 36 /// 37 enum order = 0; 38 /// 39 enum bool isSquare = (R == C); 40 static if (O == MatrixOrder.RowMajor) { 41 /// 42 alias RowType = Vector!(T, C) ; 43 /// 44 alias ColumnType = Vector!(T, R); 45 /// Fields definition. 46 union { 47 /// All elements. 48 T[C * R] v; 49 /// All rows. 50 RowType[R] row; 51 /// Components. 52 T[C][R] c; 53 } 54 /// 55 this(U...)(U values) pure nothrow @safe @nogc { 56 import std.meta : allSatisfy; 57 import std.traits : isAssignable; 58 enum bool isAsgn(U) = isAssignable!(T, U); 59 static if ((U.length == C * R) && allSatisfy!(isAsgn, U)) { 60 static foreach (i, x; values) { 61 v[i] = x; 62 } 63 } else static if ((U.length == 1) && isAsgn!U && (!is(U[0] : Matrix))) { 64 v[] = values[0]; 65 } else static if (U.length == 0) { 66 v[] = cast(T)0; 67 } else { 68 static assert(false, "Cannot create a matrix from given arguments!"); 69 } 70 } 71 /// 72 pure nothrow @safe unittest { 73 // Create a matrix using all values. 74 auto matrix1 = Matrix!(int, 2)(1, 2, 3, 4); 75 assert (matrix1.v == [1, 2, /**/ 3, 4]); 76 // Create a matrix from one value. 77 auto matrix2 = Matrix!(float, 2)(5.2f); 78 assert (matrix2.v == [5.2f, 5.2f, /**/ 5.2f, 5.2f]); 79 // Create a matrix with no value (implicit 0). 80 auto matrix3 = Matrix!(short, 2)(); 81 assert (matrix3.v == [0, 0, /**/ 0, 0]); 82 } 83 /// Construct a matrix from columns. 84 static Matrix fromColumns(ColumnType[] columns) pure nothrow @safe @nogc 85 in { 86 assert(columns.length == C); 87 } do { 88 Matrix ret = void; 89 // todo 90 return ret; 91 } 92 /// 93 pure nothrow @safe unittest { 94 // todo 95 } 96 /// Construct a matrix from rows. 97 static Matrix fromRows(RowType[] rows) pure nothrow @safe @nogc 98 in { 99 assert(rows.length == R); 100 } do { 101 Matrix ret = void; 102 ret.row[] = rows[]; 103 return ret; 104 } 105 /// 106 pure nothrow @safe unittest { 107 auto row = Vector!(double, 2)(2.0, 4.5); 108 auto matrix = Matrix!(double, 2).fromRows([row, row]); 109 assert (matrix.v == [2.0, 4.5, /**/ 2.0, 4.5]); 110 } 111 /// Returns an identity matrix. 112 static Matrix identity() pure nothrow @safe @nogc { 113 Matrix ret = void; 114 static foreach (i; 0 .. R) { 115 static foreach (j; 0 .. C) { 116 static if (i == j) { 117 ret.c[i][j] = 1; 118 } else { 119 ret.c[i][j] = 0; 120 } 121 } 122 } 123 return ret; 124 } 125 /// 126 pure nothrow @safe unittest { 127 auto matrix = Matrix!(uint, 3).identity(); 128 assert (matrix.v == [1, 0, 0, /**/ 0, 1, 0, /**/ 0, 0, 1]); 129 } 130 /// Returns a constant matrix. 131 static Matrix constant(U)(U x) pure nothrow @safe @nogc { 132 Matrix ret = void; 133 static foreach (i; 0 .. R * C) { 134 ret.v[i] = cast(T)x; 135 } 136 return ret; 137 } 138 /// 139 pure nothrow @safe unittest { 140 auto matrix = Matrix!(int, 2, 3).constant(7); 141 assert (matrix.v == [7, 7, 7, /**/ 7, 7, 7]); 142 } 143 /// 144 ColumnType column(int j) pure nothrow const @safe @nogc { 145 ColumnType ret = void; 146 static foreach (i; 0..R) { 147 ret.v[i] = c[i][j]; 148 } 149 return ret; 150 } 151 /// 152 Matrix opBinary(string op)(T factor) pure nothrow const @safe @nogc if (op == "*") { 153 Matrix ret = void; 154 static foreach (i; 0..R) { 155 static foreach (j; 0..C) { 156 ret.c[i][j] = c[i][j] * factor; 157 } 158 } 159 return ret; 160 } 161 /// 162 pure nothrow @safe @nogc unittest { 163 auto m1 = Matrix2I(3); 164 auto m2 = m1 * 4; 165 assert (m2.v == [12, 12, /**/ 12, 12]); 166 } 167 /// 168 ColumnType opBinary(string op)(RowType x) pure nothrow const @safe @nogc if (op == "*") { 169 ColumnType ret = void; 170 static foreach (i; 0..R) { 171 T sum = 0; 172 static foreach (j; 0..C) { 173 sum += c[i][j] * x.v[j]; 174 } 175 res.v[i] = sum; 176 } 177 return ret; 178 } 179 /// 180 auto opBinary(string op, U)(U x) pure nothrow const @safe @nogc if (isMatrixInstance!U && (U.rowCount == C) && (op == "*")) { 181 Matrix!(T, R, U.columnCount) ret = void; 182 T sum = 0; 183 static foreach (i; 0..R) { 184 static foreach (j; 0..U.columnCount) { 185 static foreach (k; 0..C) { 186 sum += c[i][k] * x.c[k][j]; 187 } 188 ret.c[i][j] = sum; 189 sum = 0; 190 } 191 } 192 return ret; 193 } 194 /// 195 pure nothrow @safe @nogc unittest { 196 auto m1 = Matrix2I(2, 5, /**/ -1, 2); 197 auto m2 = Matrix2x3I(3, 3, -2, /**/ 3, 4, 3); 198 auto m3 = m1 * m2; 199 assert (m3.v == [21, 26, 11, /**/ 3, 5, 8]); 200 } 201 /// 202 Matrix opBinary(string op, U)(U rhs) pure nothrow const @safe @nogc if (op == "+" || op == "-") { 203 Matrix ret = void; 204 static if (is (U : Matrix)) { 205 static foreach (i; 0..R) { 206 static foreach (j; 0..C) { 207 mixin("ret.c[i][j] = c[i][j] " ~ op ~ " rhs.c[i][j];"); 208 } 209 } 210 } else static if (is (U : T)) { 211 mixin("ret.v[] = this.v[] " ~ op ~ " rhs;"); 212 } else { 213 static assert (0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof); 214 } 215 return ret; 216 } 217 /// 218 pure nothrow @safe @nogc unittest { 219 auto m1 = Matrix2I(1, 3, /**/ -1, 4); 220 auto m2 = Matrix2I(5, 2, /**/ 1, 0); 221 auto m3 = m1 + m2; 222 assert (m3.v == [6, 5, /**/ 0, 4]); 223 auto m4 = m1 - 7; 224 assert (m4.v == [-6, -4, /**/ -8, -3]); 225 } 226 /// 227 ref Matrix opOpAssign(string op, U)(U rhs) pure nothrow @safe @nogc { 228 static if (is(U : Matrix) || is(U : T)) { 229 mixin("this = this " ~ op ~ " rhs;"); 230 } else { 231 static assert (0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof); 232 } 233 return this; 234 } 235 /// 236 pure nothrow @safe @nogc unittest { 237 auto m1 = Matrix2I(1, 3, /**/ -1, 4); 238 auto m2 = Matrix2I(5, 2, /**/ 1, 0); 239 m1 += m2; 240 assert (m1.v == [6, 5, /**/ 0, 4]); 241 m1 -= 2; 242 assert (m1.v == [4, 3, /**/ -2, 2]); 243 } 244 /// 245 Matrix opUnary(string op)() pure nothrow const @safe @nogc if (op == "+" || op == "-" || op == "~" || op == "!") { 246 Matrix ret = void; 247 static foreach (i; 0..R * C) { 248 mixin("ret.v[i] = " ~ op ~ "v[i];"); 249 } 250 return ret; 251 } 252 /// 253 pure nothrow @safe @nogc unittest { 254 auto m1 = Matrix2I(1, 3, /**/ -1, 0); 255 assert ((+m1).v == [1, 3, /**/ -1, 0]); 256 assert ((-m1).v == [-1, -3, /**/ 1, 0]); 257 assert ((~m1).v == [-2, -4, /**/ 0, -1]); 258 // *** BUG *** 259 //auto m2 = Matrix!(bool, 2)(true, true, /**/ false, false); 260 //assert ((!m2).v == [false, false, /**/ true, true]); 261 } 262 /// 263 U opCast(U)() pure nothrow const @safe @nogc if (isMatrixInstance!U && U.rowCount == rowCount && U.columnCount == columnCount) { 264 U ret = U.identity(); 265 enum min_r = R < U.rowCount ? R : U.rowCount; 266 enum min_c = C < U.columnCount ? C : U.columnCount; 267 static foreach (i; 0..min_r) { 268 static foreach (j; 0..min_c) { 269 ret.c[i][j] = cast(U.type)(c[i][j]); 270 } 271 } 272 return ret; 273 } 274 /// 275 pure nothrow @safe @nogc unittest { 276 auto m1 = Matrix2F(3.2f, 4.5f, /**/ 3.8f, -7.2f); 277 Matrix2I m2 = cast(Matrix2I)m1; 278 assert (m2.v == [3, 4, /**/ 3, -7]); 279 } 280 /// 281 bool opEquals(U)(U rhs) pure nothrow const @safe @nogc { 282 static if (is(U : Matrix)) { 283 static foreach (i; 0..R * C) { 284 if (v[i] != rhs.v[i]) { 285 return false; 286 } 287 } 288 } else static if (is(U : T)) { 289 static foreach (i; 0..R * C) { 290 if (v[i] != rhs) { 291 return false; 292 } 293 } 294 } else { 295 static assert (0, "Cannot compare a variable of type " ~ U.stringof ~ " with a variable of type " ~ Matrix.stringof); 296 } 297 return true; 298 } 299 /// 300 pure nothrow @safe @nogc unittest { 301 auto m1 = Matrix2I(6, 3, /**/ -1, 0); 302 auto m2 = Matrix2I(6, 3, /**/ -1, 0); 303 auto m3 = Matrix2I(1, 4, /**/ -1, 7); 304 assert (m1 == m2); 305 assert (m1 != m3); 306 auto m4 = Matrix2I(3, 3, /**/ 3, 3); 307 assert (m4 == 3); 308 assert (m4 != -3); 309 } 310 /// Returns a pointer to content. 311 inout(T)* ptr() pure nothrow inout @nogc @property { 312 return v.ptr; 313 } 314 static if (isFloatingPoint!T && R == 2 && C == 2) { 315 /// Returns inverse of matrix 2x2. 316 Matrix inverse() pure nothrow const @safe @nogc { 317 T invDet = 1 / (c[0][0] * c[1][1] - c[0][1] * c[1][0]); 318 return Matrix(c[1][1] * invDet, -c[0][1] * invDet, -c[1][0] * invDet, c[0][0] * invDet); 319 } 320 /// 321 pure nothrow @safe @nogc unittest { 322 import std.math : approxEqual; 323 auto m1 = Matrix2D(2.0, 3.0, /**/ -1.0, 5.0); 324 auto m2 = m1.inverse(); 325 assert (m2.v[0].approxEqual(0.384615)); 326 assert (m2.v[1].approxEqual(-0.230769)); 327 assert (m2.v[2].approxEqual(0.0769231)); 328 assert (m2.v[3].approxEqual(0.153846)); 329 } 330 } 331 static if (isFloatingPoint!T && R == 3 && C == 3) { 332 /// Returns inverse of matrix 3x3. 333 Matrix inverse() pure nothrow const @safe @nogc { 334 T det = c[0][0] * (c[1][1] * c[2][2] - c[2][1] * c[1][2]) - c[0][1] * (c[1][0] * c[2][2] - c[1][2] * c[2][0]) + c[0][2] * (c[1][0] * c[2][1] - c[1][1] * c[2][0]); 335 T invDet = 1 / det; 336 Matrix ret = void; 337 ret.c[0][0] = (c[1][1] * c[2][2] - c[2][1] * c[1][2]) * invDet; 338 ret.c[0][1] = -(c[0][1] * c[2][2] - c[0][2] * c[2][1]) * invDet; 339 ret.c[0][2] = (c[0][1] * c[1][2] - c[0][2] * c[1][1]) * invDet; 340 ret.c[1][0] = -(c[1][0] * c[2][2] - c[1][2] * c[2][0]) * invDet; 341 ret.c[1][1] = (c[0][0] * c[2][2] - c[0][2] * c[2][0]) * invDet; 342 ret.c[1][2] = -(c[0][0] * c[1][2] - c[1][0] * c[0][2]) * invDet; 343 ret.c[2][0] = (c[1][0] * c[2][1] - c[2][0] * c[1][1]) * invDet; 344 ret.c[2][1] = -(c[0][0] * c[2][1] - c[2][0] * c[0][1]) * invDet; 345 ret.c[2][2] = (c[0][0] * c[1][1] - c[1][0] * c[0][1]) * invDet; 346 return ret; 347 } 348 /// 349 pure nothrow @safe @nogc unittest { 350 } 351 } 352 static if (isFloatingPoint!T && R == 4 && C == 4) { 353 /// Returns inverse of matrix 4x4. 354 Matrix inverse() pure nothrow const @safe @nogc { 355 T det2_01_01 = c[0][0] * c[1][1] - c[0][1] * c[1][0]; 356 T det2_01_02 = c[0][0] * c[1][2] - c[0][2] * c[1][0]; 357 T det2_01_03 = c[0][0] * c[1][3] - c[0][3] * c[1][0]; 358 T det2_01_12 = c[0][1] * c[1][2] - c[0][2] * c[1][1]; 359 T det2_01_13 = c[0][1] * c[1][3] - c[0][3] * c[1][1]; 360 T det2_01_23 = c[0][2] * c[1][3] - c[0][3] * c[1][2]; 361 T det3_201_012 = c[2][0] * det2_01_12 - c[2][1] * det2_01_02 + c[2][2] * det2_01_01; 362 T det3_201_013 = c[2][0] * det2_01_13 - c[2][1] * det2_01_03 + c[2][3] * det2_01_01; 363 T det3_201_023 = c[2][0] * det2_01_23 - c[2][2] * det2_01_03 + c[2][3] * det2_01_02; 364 T det3_201_123 = c[2][1] * det2_01_23 - c[2][2] * det2_01_13 + c[2][3] * det2_01_12; 365 T det = - det3_201_123 * c[3][0] + det3_201_023 * c[3][1] - det3_201_013 * c[3][2] + det3_201_012 * c[3][3]; 366 T invDet = 1 / det; 367 T det2_03_01 = c[0][0] * c[3][1] - c[0][1] * c[3][0]; 368 T det2_03_02 = c[0][0] * c[3][2] - c[0][2] * c[3][0]; 369 T det2_03_03 = c[0][0] * c[3][3] - c[0][3] * c[3][0]; 370 T det2_03_12 = c[0][1] * c[3][2] - c[0][2] * c[3][1]; 371 T det2_03_13 = c[0][1] * c[3][3] - c[0][3] * c[3][1]; 372 T det2_03_23 = c[0][2] * c[3][3] - c[0][3] * c[3][2]; 373 T det2_13_01 = c[1][0] * c[3][1] - c[1][1] * c[3][0]; 374 T det2_13_02 = c[1][0] * c[3][2] - c[1][2] * c[3][0]; 375 T det2_13_03 = c[1][0] * c[3][3] - c[1][3] * c[3][0]; 376 T det2_13_12 = c[1][1] * c[3][2] - c[1][2] * c[3][1]; 377 T det2_13_13 = c[1][1] * c[3][3] - c[1][3] * c[3][1]; 378 T det2_13_23 = c[1][2] * c[3][3] - c[1][3] * c[3][2]; 379 T det3_203_012 = c[2][0] * det2_03_12 - c[2][1] * det2_03_02 + c[2][2] * det2_03_01; 380 T det3_203_013 = c[2][0] * det2_03_13 - c[2][1] * det2_03_03 + c[2][3] * det2_03_01; 381 T det3_203_023 = c[2][0] * det2_03_23 - c[2][2] * det2_03_03 + c[2][3] * det2_03_02; 382 T det3_203_123 = c[2][1] * det2_03_23 - c[2][2] * det2_03_13 + c[2][3] * det2_03_12; 383 T det3_213_012 = c[2][0] * det2_13_12 - c[2][1] * det2_13_02 + c[2][2] * det2_13_01; 384 T det3_213_013 = c[2][0] * det2_13_13 - c[2][1] * det2_13_03 + c[2][3] * det2_13_01; 385 T det3_213_023 = c[2][0] * det2_13_23 - c[2][2] * det2_13_03 + c[2][3] * det2_13_02; 386 T det3_213_123 = c[2][1] * det2_13_23 - c[2][2] * det2_13_13 + c[2][3] * det2_13_12; 387 T det3_301_012 = c[3][0] * det2_01_12 - c[3][1] * det2_01_02 + c[3][2] * det2_01_01; 388 T det3_301_013 = c[3][0] * det2_01_13 - c[3][1] * det2_01_03 + c[3][3] * det2_01_01; 389 T det3_301_023 = c[3][0] * det2_01_23 - c[3][2] * det2_01_03 + c[3][3] * det2_01_02; 390 T det3_301_123 = c[3][1] * det2_01_23 - c[3][2] * det2_01_13 + c[3][3] * det2_01_12; 391 Matrix ret = void; 392 ret.c[0][0] = - det3_213_123 * invDet; 393 ret.c[1][0] = + det3_213_023 * invDet; 394 ret.c[2][0] = - det3_213_013 * invDet; 395 ret.c[3][0] = + det3_213_012 * invDet; 396 ret.c[0][1] = + det3_203_123 * invDet; 397 ret.c[1][1] = - det3_203_023 * invDet; 398 ret.c[2][1] = + det3_203_013 * invDet; 399 ret.c[3][1] = - det3_203_012 * invDet; 400 ret.c[0][2] = + det3_301_123 * invDet; 401 ret.c[1][2] = - det3_301_023 * invDet; 402 ret.c[2][2] = + det3_301_013 * invDet; 403 ret.c[3][2] = - det3_301_012 * invDet; 404 ret.c[0][3] = - det3_201_123 * invDet; 405 ret.c[1][3] = + det3_201_023 * invDet; 406 ret.c[2][3] = - det3_201_013 * invDet; 407 ret.c[3][3] = + det3_201_012 * invDet; 408 return ret; 409 } 410 /// 411 pure nothrow @safe @nogc unittest { 412 } 413 } 414 /// Returns transposed matrix. 415 Matrix!(T, C, R) transposed() pure nothrow const @safe @nogc { 416 Matrix!(T, C, R) ret = void; 417 static foreach (i; 0..C) { 418 static foreach (j; 0..R) { 419 ret.c[i][j] = c[j][i]; 420 } 421 } 422 return ret; 423 } 424 /// 425 pure nothrow @safe @nogc unittest { 426 auto m1 = Matrix2I(4, 5, /**/ -1, 0); 427 assert (m1.transposed().v == [4, -1, /**/ 5, 0]); 428 } 429 static if (isSquare && R > 2) { 430 /// Makes a diagonal matrix from a vector. 431 static Matrix diag(Vector!(T, R) v) pure nothrow @safe @nogc { 432 Matrix ret = void; 433 static foreach (i; 0..R) { 434 static foreach (j; 0..C) { 435 ret.c[i][j] = (i == j) ? v.v[i] : 0; 436 } 437 } 438 return ret; 439 } 440 /// 441 pure nothrow @safe @nogc unittest { 442 auto v1 = Vector!(int, 3)(4, 5, -1); 443 auto m1 = Matrix!(int, 3).diag(v1); 444 assert (m1.v == [4, 0, 0, /**/ 0, 5, 0, /**/ 0, 0, -1]); 445 } 446 /// In-place translate by (v, 1). 447 void translate(Vector!(T, R-1) v) pure nothrow @safe @nogc { 448 T _dot = 0; 449 static foreach (i; 0..R) { 450 static foreach (j; 0..C - 1) { 451 _dot += v.v[j] * c[i][j]; 452 } 453 c[i][C - 1] += _dot; 454 _dot = 0; 455 } 456 } 457 /// 458 pure nothrow @safe @nogc unittest { 459 } 460 /// Make a translation matrix. 461 static Matrix translation(Vector!(T, R-1) v) pure nothrow @safe @nogc { 462 Matrix ret = identity(); 463 static foreach (i; 0..R - 1) { 464 ret.c[i][C - 1] += v.v[i]; 465 } 466 return ret; 467 } 468 /// 469 pure nothrow @safe @nogc unittest { 470 } 471 /// In-place matrix scaling. 472 void scale(Vector!(T, R-1) v) pure nothrow @safe @nogc { 473 static foreach (i; 0..R) { 474 static foreach (j; 0..C - 1) { 475 c[i][j] *= v.v[j]; 476 } 477 } 478 } 479 /// 480 pure nothrow @safe @nogc unittest { 481 } 482 /// Make a scaling matrix. 483 static Matrix scaling(Vector!(T, R-1) v) pure nothrow @safe @nogc { 484 Matrix ret = identity(); 485 static foreach (i; 0..R - 1) { 486 ret.c[i][i] = v.v[i]; 487 } 488 return ret; 489 } 490 /// 491 pure nothrow @safe @nogc unittest { 492 } 493 } 494 static if (isSquare && (R == 3 || R == 4) && isFloatingPoint!T) { 495 /// 496 static Matrix rotateAxis(int i, int j)(T angle) pure nothrow @safe @nogc { 497 import liberty.math.functions : sin, cos; 498 Matrix ret = identity(); 499 const T cosa = cos(angle); 500 const T sina = sin(angle); 501 ret.c[i][i] = cosa; 502 ret.c[i][j] = -sina; 503 ret.c[j][i] = sina; 504 ret.c[j][j] = cosa; 505 return ret; 506 } 507 /// 508 pure nothrow @safe @nogc unittest { 509 } 510 /// Returns rotation matrix along axis X. 511 alias rotateXAxix = rotateAxis!(1, 2); 512 /// Returns rotation matrix along axis Y. 513 alias rotateYAxis = rotateAxis!(2, 0); 514 /// Returns rotation matrix along axis Z. 515 alias rotateZAxis = rotateAxis!(0, 1); 516 /// In-place rotation by (v, 1) 517 void rotate(T angle, Vector!(T, 3) axis) pure nothrow @safe @nogc { 518 this = rotation(angle, axis, this); 519 } 520 /// 521 pure nothrow @safe @nogc unittest { 522 } 523 /// 524 void rotateX(T angle) pure nothrow @safe @nogc { 525 this = rotation(angle, Vector!(T, 3)(1, 0, 0), this); 526 } 527 /// 528 pure nothrow @safe @nogc unittest { 529 } 530 /// 531 void rotateY(T angle) pure nothrow @safe @nogc { 532 this = rotation(angle, Vector!(T, 3)(0, 1, 0), this); 533 } 534 /// 535 pure nothrow @safe @nogc unittest { 536 } 537 /// 538 void rotateZ(T angle) pure nothrow @safe @nogc { 539 this = rotation(angle, Vector!(T, 3)(0, 0, 1), this); 540 } 541 /// 542 pure nothrow @safe @nogc unittest { 543 } 544 /// 545 static Matrix rotation(T angle, Vector!(T, 3) axis, Matrix m = identity()) pure nothrow @safe @nogc { 546 import liberty.math.functions : sin, cos; 547 Matrix ret = m; 548 const T c = cos(angle); 549 const oneMinusC = 1 - c; 550 const T s = sin(angle); 551 axis = axis.normalized(); 552 T x = axis.x, 553 y = axis.y, 554 z = axis.z; 555 T xy = x * y, 556 yz = y * z, 557 xz = x * z; 558 ret.c[0][0] = x * x * oneMinusC + c; 559 ret.c[0][1] = x * y * oneMinusC - z * s; 560 ret.c[0][2] = x * z * oneMinusC + y * s; 561 ret.c[1][0] = y * x * oneMinusC + z * s; 562 ret.c[1][1] = y * y * oneMinusC + c; 563 ret.c[1][2] = y * z * oneMinusC - x * s; 564 ret.c[2][0] = z * x * oneMinusC - y * s; 565 ret.c[2][1] = z * y * oneMinusC + x * s; 566 ret.c[2][2] = z * z * oneMinusC + c; 567 return ret; 568 } 569 /// 570 pure nothrow @safe @nogc unittest { 571 } 572 } 573 static if (isSquare && R == 4 && isFloatingPoint!T) { 574 /// Returns orthographic projection. 575 static Matrix orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow @safe @nogc { 576 T dx = right - left, 577 dy = top - bottom, 578 dz = far - near; 579 T tx = -(right + left) / dx; 580 T ty = -(top + bottom) / dy; 581 T tz = -(far + near) / dz; 582 return Matrix(2 / dx, 0, 0, tx, 0, 2 / dy, 0, ty, 0, 0, -2 / dz, tz, 0, 0, 0, 1); 583 } 584 /// 585 pure nothrow @safe @nogc unittest { 586 } 587 /// Returns perspective projection. 588 static Matrix perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow @safe @nogc { 589 import liberty.math.functions : tan; 590 T f = 1 / tan(FOVInRadians / 2); 591 T d = 1 / (zNear - zFar); 592 return Matrix(f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (zFar + zNear) * d, 2 * d * zFar * zNear, 0, 0, -1, 0); 593 } 594 /// 595 pure nothrow @safe @nogc unittest { 596 } 597 /// Returns lookAt projection. 598 static Matrix lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) pure nothrow @safe @nogc { 599 import liberty.math.vector : dot, cross; 600 Vector!(T, 3) Z = (eye - target).normalized(); 601 Vector!(T, 3) X = cross(-up, Z).normalized(); 602 Vector!(T, 3) Y = cross(Z, -X); 603 return Matrix(-X.x, -X.y, -X.z, dot(X, eye), Y.x, Y.y, Y.z, -dot(Y, eye), Z.x, Z.y, Z.z, -dot(Z, eye), 0, 0, 0, 1); 604 } 605 /// 606 pure nothrow @safe @nogc unittest { 607 } 608 } 609 } else { 610 611 } 612 } 613 /// 614 template isMatrixInstance(U) { 615 private static void isMatrix(T, int R, int C, MatrixOrder O)(Matrix!(T, R, C, O) x) {} 616 enum bool isMatrixInstance = is(typeof(isMatrix(U.init))); 617 } 618 /// 619 template Matrix2(T) { 620 alias Matrix2 = Matrix!(T, 2); 621 } 622 /// 623 template Matrix3(T) { 624 alias Matrix3 = Matrix!(T, 3); 625 } 626 /// 627 template Matrix4(T) { 628 alias Matrix4 = Matrix!(T, 4); 629 } 630 /// 631 template Matrix2x3(T) { 632 alias Matrix2x3 = Matrix!(T, 2, 3); 633 } 634 /// 635 template Matrix2x4(T) { 636 alias Matrix2x4 = Matrix!(T, 2, 4); 637 } 638 /// 639 template Matrix3x2(T) { 640 alias Matrix3x2 = Matrix!(T, 3, 2); 641 } 642 /// 643 template Matrix3x4(T) { 644 alias Matrix3x4 = Matrix!(T, 3, 4); 645 } 646 /// 647 template Matrix4x2(T) { 648 alias Matrix4x2 = Matrix!(T, 4, 2); 649 } 650 /// 651 template Matrix4x3(T) { 652 alias Matrix4x3 = Matrix!(T, 4, 3); 653 } 654 /// 655 alias Matrix2I = Matrix2!int; 656 /// 657 alias Matrix3I = Matrix3!int; 658 /// 659 alias Matrix4I = Matrix4!int; 660 /// 661 alias Matrix2F = Matrix2!float; 662 /// 663 alias Matrix3F = Matrix3!float; 664 /// 665 alias Matrix4F = Matrix4!float; 666 /// 667 alias Matrix2D = Matrix2!double; 668 /// 669 alias Matrix3D = Matrix3!double; 670 /// 671 alias Matrix4D = Matrix4!double; 672 /// 673 alias Matrix2x3I = Matrix2x3!int; 674 /// 675 alias Matrix2x4I = Matrix2x4!int; 676 /// 677 alias Matrix3x2I = Matrix3x2!int; 678 /// 679 alias Matrix2x3F = Matrix2x3!float; 680 /// 681 alias Matrix2x4F = Matrix2x4!float; 682 /// 683 alias Matrix3x2F = Matrix3x2!float; 684 /// 685 alias Matrix2x3D = Matrix2x3!double; 686 /// 687 alias Matrix2x4D = Matrix2x4!double; 688 /// 689 alias Matrix3x2D = Matrix3x2!double; 690 /// 691 alias Matrix3x4I = Matrix3x4!int; 692 /// 693 alias Matrix4x2I = Matrix4x2!int; 694 /// 695 alias Matrix4x3I = Matrix4x3!int; 696 /// 697 alias Matrix3x4F = Matrix3x4!float; 698 /// 699 alias Matrix4x2F = Matrix4x2!float; 700 /// 701 alias Matrix4x3F = Matrix4x3!float; 702 /// 703 alias Matrix3x4D = Matrix3x4!double; 704 /// 705 alias Matrix4x2D = Matrix4x2!double; 706 /// 707 alias Matrix4x3D = Matrix4x3!double; 708 /// 709 final class MatrixStack(int R, T) if (R == 3 || R == 4) { 710 private { 711 size_t _top; 712 size_t _depth; 713 MatrixType* _matrices; 714 MatrixType* _invMatrices; 715 } 716 /// 717 alias MatrixType = Matrix!(T, R, R); 718 /// Creates a matrix stack. 719 this(size_t depth = 32) nothrow @trusted @nogc 720 in { 721 assert(depth > 0); 722 } do { 723 import core.stdc.stdlib : malloc; 724 size_t memory_needed = MatrixType.sizeof * depth * 2; 725 void* data = malloc(memory_needed * 2); 726 _matrices = cast(MatrixType*)data; 727 _invMatrices = cast(MatrixType*)(data + memory_needed); 728 _top = 0; 729 _depth = depth; 730 loadIdentity(); 731 } 732 /// Relases the matrix stack memory. 733 ~this() { 734 if (_matrices !is null) { 735 import core.stdc.stdlib : free; 736 import liberty.core.memory : ensureNotInGC; 737 ensureNotInGC("MatrixStack"); 738 free(_matrices); 739 _matrices = null; 740 } 741 } 742 /// 743 void loadIdentity() pure nothrow @trusted @nogc { 744 _matrices[_top] = MatrixType.identity(); 745 _invMatrices[_top] = MatrixType.identity(); 746 } 747 /// 748 void push() pure nothrow @trusted @nogc { 749 if(_top + 1 >= _depth) { 750 assert(false, "Matrix stack is full!"); 751 } 752 _matrices[_top + 1] = _matrices[_top]; 753 _invMatrices[_top + 1] = _invMatrices[_top]; 754 _top++; 755 } 756 /// Replacement for $(D glPopMatrix). 757 void pop() pure nothrow @safe @nogc { 758 if (_top <= 0) { 759 assert(false, "Matrix stack is empty!"); 760 } 761 _top--; 762 } 763 764 /// 765 MatrixType top() pure const nothrow @trusted @nogc { 766 return _matrices[_top]; 767 } 768 /// 769 MatrixType invTop() pure const nothrow @trusted @nogc { 770 return _invMatrices[_top]; 771 } 772 /// 773 void top(MatrixType m) pure nothrow @trusted @nogc { 774 _matrices[_top] = m; 775 _invMatrices[_top] = m.inverse(); 776 } 777 /// 778 void mult(MatrixType m) pure nothrow @trusted @nogc { 779 mult(m, m.inverse()); 780 } 781 /// 782 void mult(MatrixType m, MatrixType invM) pure nothrow @trusted @nogc { 783 _matrices[_top] = _matrices[_top] * m; 784 _invMatrices[_top] = invM *_invMatrices[_top]; 785 } 786 /// 787 void translate(Vector!(T, R-1) v) pure nothrow @safe @nogc { 788 mult(MatrixType.translation(v), MatrixType.translation(-v)); 789 } 790 /// 791 void scale(Vector!(T, R-1) v) pure nothrow @safe @nogc { 792 mult(MatrixType.scaling(v), MatrixType.scaling(1 / v)); 793 } 794 static if (R == 4) { 795 /// 796 void rotate(T angle, Vector!(T, 3) axis) pure nothrow @safe @nogc { 797 MatrixType rot = MatrixType.rotation(angle, axis); 798 mult(rot, rot.transposed()); 799 } 800 /// 801 void perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow @safe @nogc { 802 mult(MatrixType.perspective(FOVInRadians, aspect, zNear, zFar)); 803 } 804 /// 805 void orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow @safe @nogc { 806 mult(MatrixType.orthographic(left, right, bottom, top, near, far)); 807 } 808 /// 809 void lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) pure nothrow @safe @nogc { 810 mult(MatrixType.lookAt(eye, target, up)); 811 } 812 } 813 } 814 /// 815 @trusted unittest { 816 auto m = new MatrixStack!(4, double)(); 817 scope(exit) destroy(m); 818 m.loadIdentity(); 819 m.push(); 820 m.pop(); 821 m.translate(Vector!(double, 3)(2.4, 3.3, 7.5)); 822 m.scale(Vector!(double, 3)(0.7)); 823 auto t = new MatrixStack!(3, float)(); 824 scope(exit) destroy(t); 825 t.loadIdentity(); 826 t.push(); 827 t.pop(); 828 t.translate(Vector!(float, 2)(1, -8)); 829 t.scale(Vector!(float, 2)(0.2)); 830 }