1 /** 2 * Custom sized 2D Matrices. 3 * 4 * Copyright: Copyright Guillaume Piolat 2015-2021. 5 * Copyright Aleksandr Druzhinin 2016-2020. 6 * Copyright Nathan Sashihara 2018. 7 * Copyright Thibaut Charles 2018. 8 * 9 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 10 */ 11 module dplug.math.matrix; 12 13 import std.math, 14 std.typetuple, 15 std.traits, 16 std.string, 17 std.typecons, 18 std.conv; 19 20 import dplug.math.vector; 21 22 /// Generic non-resizeable matrix with R rows and C columns. 23 /// Intended for 3D use (size 3x3 and 4x4). 24 /// Important: <b>Matrices here are in row-major order whereas OpenGL is column-major.</b> 25 /// Params: 26 /// T = type of elements 27 /// R = number of rows 28 /// C = number of columns 29 struct Matrix(T, int R, int C) 30 { 31 public 32 { 33 static assert(R >= 1 && C >= 1); 34 35 alias Vector!(T, C) row_t; 36 alias Vector!(T, R) column_t; 37 38 enum bool isSquare = (R == C); 39 40 // fields definition 41 union 42 { 43 T[C*R] v; // all elements 44 row_t[R] rows; // all rows 45 T[C][R] c; // components 46 } 47 48 @nogc this(U...)(U values) pure nothrow 49 { 50 static if ((U.length == C*R) && allSatisfy!(isTAssignable, U)) 51 { 52 // construct with components 53 foreach(int i, x; values) 54 v[i] = x; 55 } 56 else static if ((U.length == 1) && (isAssignable!(U[0])) && (!is(U[0] : Matrix))) 57 { 58 // construct with assignment 59 opAssign!(U[0])(values[0]); 60 } 61 else static assert(false, "cannot create a matrix from given arguments"); 62 } 63 64 /// Construct a matrix from columns. 65 @nogc static Matrix fromColumns(column_t[] columns) pure nothrow 66 { 67 assert(columns.length == C); 68 Matrix res; 69 for (int i = 0; i < R; ++i) 70 for (int j = 0; j < C; ++j) 71 { 72 res.c[i][j] = columns[j][i]; 73 } 74 return res; 75 } 76 77 /// Construct a matrix from rows. 78 @nogc static Matrix fromRows(row_t[] rows) pure nothrow 79 { 80 assert(rows.length == R); 81 Matrix res; 82 res.rows[] = rows[]; 83 return res; 84 } 85 86 /// Construct matrix with a scalar. 87 @nogc this(U)(T x) pure nothrow 88 { 89 for (int i = 0; i < _N; ++i) 90 v[i] = x; 91 } 92 93 /// Assign with a scalar. 94 @nogc ref Matrix opAssign(U : T)(U x) pure nothrow 95 { 96 for (int i = 0; i < R * C; ++i) 97 v[i] = x; 98 return this; 99 } 100 101 /// Assign with a samey matrice. 102 @nogc ref Matrix opAssign(U : Matrix)(U x) pure nothrow 103 { 104 for (int i = 0; i < R * C; ++i) 105 v[i] = x.v[i]; 106 return this; 107 } 108 109 /// Assign from other small matrices (same size, compatible type). 110 @nogc ref Matrix opAssign(U)(U x) pure nothrow 111 if (isMatrixInstantiation!U 112 && is(U._T : _T) 113 && (!is(U: Matrix)) 114 && (U._R == R) && (U._C == C)) 115 { 116 for (int i = 0; i < R * C; ++i) 117 v[i] = x.v[i]; 118 return this; 119 } 120 121 /// Assign with a static array of size R * C. 122 @nogc ref Matrix opAssign(U)(U x) pure nothrow 123 if ((isStaticArray!U) 124 && is(typeof(x[0]) : T) 125 && (U.length == R * C)) 126 { 127 for (int i = 0; i < R * C; ++i) 128 v[i] = x[i]; 129 return this; 130 } 131 132 /// Assign with a static array of shape (R, C). 133 @nogc ref Matrix opAssign(U)(U x) pure nothrow 134 if ((isStaticArray!U) && isStaticArray!(typeof(x[0])) 135 && is(typeof(x[0][0]) : T) 136 && (U.length == R) 137 && (x[0].length == C)) 138 { 139 foreach (i; 0..R) 140 rows[i] = x[i]; 141 return this; 142 } 143 144 /// Assign with a dynamic array of size R * C. 145 @nogc ref Matrix opAssign(U)(U x) pure nothrow 146 if ((isDynamicArray!U) 147 && is(typeof(x[0]) : T)) 148 { 149 assert(x.length == R * C); 150 for (int i = 0; i < R * C; ++i) 151 v[i] = x[i]; 152 return this; 153 } 154 155 /// Assign with a dynamic array of shape (R, C). 156 @nogc ref Matrix opAssign(U)(U x) pure nothrow 157 if ((isDynamicArray!U) && isDynamicArray!(typeof(x[0])) 158 && is(typeof(x[0][0]) : T)) 159 { 160 assert(x.length == R); 161 foreach (i; 0..R) 162 { 163 assert(x[i].length == C); 164 rows[i] = x[i]; 165 } 166 return this; 167 } 168 169 /// Return a pointer to content. 170 @nogc inout(T)* ptr() pure inout nothrow @property 171 { 172 return v.ptr; 173 } 174 175 /// Returns a column as a vector 176 /// Returns: column j as a vector. 177 @nogc column_t column(int j) pure const nothrow 178 { 179 column_t res = void; 180 for (int i = 0; i < R; ++i) 181 res.v[i] = c[i][j]; 182 return res; 183 } 184 185 /// Returns a row as a vector 186 /// Returns: row i as a vector. 187 @nogc row_t row(int i) pure const nothrow 188 { 189 return rows[i]; 190 } 191 192 /// Covnerts to pretty string. 193 string toString() const nothrow 194 { 195 try 196 return format("%s", v); 197 catch (Exception e) 198 assert(false); // should not happen since format is right 199 } 200 201 /// Matrix * scalar multiplication. 202 @nogc Matrix opBinary(string op)(T factor) pure const nothrow if (op == "*") 203 { 204 Matrix result = void; 205 206 for (int i = 0; i < R; ++i) 207 { 208 for (int j = 0; j < C; ++j) 209 { 210 result.c[i][j] = c[i][j] * factor; 211 } 212 } 213 return result; 214 } 215 216 /// Matrix * vector multiplication. 217 @nogc column_t opBinary(string op)(row_t x) pure const nothrow if (op == "*") 218 { 219 column_t res = void; 220 for (int i = 0; i < R; ++i) 221 { 222 T sum = 0; 223 for (int j = 0; j < C; ++j) 224 { 225 sum += c[i][j] * x.v[j]; 226 } 227 res.v[i] = sum; 228 } 229 return res; 230 } 231 232 /// Matrix * matrix multiplication. 233 @nogc auto opBinary(string op, U)(U x) pure const nothrow 234 if (isMatrixInstantiation!U && (U._R == C) && (op == "*")) 235 { 236 Matrix!(T, R, U._C) result = void; 237 238 for (int i = 0; i < R; ++i) 239 { 240 for (int j = 0; j < U._C; ++j) 241 { 242 T sum = 0; 243 for (int k = 0; k < C; ++k) 244 sum += c[i][k] * x.c[k][j]; 245 result.c[i][j] = sum; 246 } 247 } 248 return result; 249 } 250 251 /// Matrix add and substraction. 252 @nogc Matrix opBinary(string op, U)(U other) pure const nothrow 253 if (is(U : Matrix) && (op == "+" || op == "-")) 254 { 255 Matrix result = void; 256 257 for (int i = 0; i < R; ++i) 258 { 259 for (int j = 0; j < C; ++j) 260 { 261 mixin("result.c[i][j] = c[i][j] " ~ op ~ " other.c[i][j];"); 262 } 263 } 264 return result; 265 } 266 267 // matrix *= scalar 268 @nogc ref Matrix opOpAssign(string op, U : T)(U x) pure nothrow if (op == "*") 269 { 270 for (int i = 0; i < R * C; ++i) 271 v[i] *= x; 272 return this; 273 } 274 275 /// Assignment operator with another samey matrix. 276 @nogc ref Matrix opOpAssign(string op, U)(U operand) pure nothrow 277 if (is(U : Matrix) && (op == "*" || op == "+" || op == "-")) 278 { 279 mixin("Matrix result = this " ~ op ~ " operand;"); 280 return opAssign!Matrix(result); 281 } 282 283 /// Matrix += <something convertible to a Matrix> 284 /// Matrix -= <something convertible to a Matrix> 285 @nogc ref Matrix opOpAssign(string op, U)(U operand) pure nothrow 286 if ((isConvertible!U) && (op == "*" || op == "+" || op == "-")) 287 { 288 Matrix conv = operand; 289 return opOpAssign!op(conv); 290 } 291 292 /// Cast to other matrix types. 293 /// If the size are different, the resulting matrix is truncated 294 /// and/or filled with identity coefficients. 295 @nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U) 296 { 297 U res = U.identity(); 298 enum minR = R < U._R ? R : U._R; 299 enum minC = C < U._C ? C : U._C; 300 for (int i = 0; i < minR; ++i) 301 for (int j = 0; j < minC; ++j) 302 { 303 res.c[i][j] = cast(U._T)(c[i][j]); 304 } 305 return res; 306 } 307 308 @nogc bool opEquals(U)(U other) pure const nothrow if (is(U : Matrix)) 309 { 310 for (int i = 0; i < R * C; ++i) 311 if (v[i] != other.v[i]) 312 return false; 313 return true; 314 } 315 316 @nogc bool opEquals(U)(U other) pure const nothrow 317 if ((isAssignable!U) && (!is(U: Matrix))) 318 { 319 Matrix conv = other; 320 return opEquals(conv); 321 } 322 323 // +matrix, -matrix, ~matrix, !matrix 324 @nogc Matrix opUnary(string op)() pure const nothrow if (op == "+" || op == "-" || op == "~" || op == "!") 325 { 326 Matrix res = void; 327 for (int i = 0; i < N; ++i) 328 mixin("res.v[i] = " ~ op ~ "v[i];"); 329 return res; 330 } 331 332 static if (isSquare && isFloatingPoint!T && R == 1) 333 { 334 /// Returns an inverted copy of this matrix 335 /// Returns: inverse of matrix. 336 /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. 337 @nogc Matrix inverse() pure const nothrow 338 { 339 assert(c[0][0] != 0); // Programming error if matrix is not invertible. 340 return Matrix( 1 / c[0][0]); 341 } 342 } 343 344 static if (isSquare && isFloatingPoint!T && R == 2) 345 { 346 /// Returns an inverted copy of this matrix 347 /// Returns: inverse of matrix. 348 /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. 349 @nogc Matrix inverse() pure const nothrow 350 { 351 T det = (c[0][0] * c[1][1] - c[0][1] * c[1][0]); 352 assert(det != 0); // Programming error if matrix is not invertible. 353 T invDet = 1 / det; 354 return Matrix( c[1][1] * invDet, -c[0][1] * invDet, 355 -c[1][0] * invDet, c[0][0] * invDet); 356 } 357 } 358 359 static if (isSquare && isFloatingPoint!T && R == 3) 360 { 361 /// Returns an inverted copy of this matrix 362 /// Returns: inverse of matrix. 363 /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. 364 @nogc Matrix inverse() pure const nothrow 365 { 366 T det = c[0][0] * (c[1][1] * c[2][2] - c[2][1] * c[1][2]) 367 - c[0][1] * (c[1][0] * c[2][2] - c[1][2] * c[2][0]) 368 + c[0][2] * (c[1][0] * c[2][1] - c[1][1] * c[2][0]); 369 assert(det != 0); // Programming error if matrix is not invertible. 370 T invDet = 1 / det; 371 372 Matrix res = void; 373 res.c[0][0] = (c[1][1] * c[2][2] - c[2][1] * c[1][2]) * invDet; 374 res.c[0][1] = -(c[0][1] * c[2][2] - c[0][2] * c[2][1]) * invDet; 375 res.c[0][2] = (c[0][1] * c[1][2] - c[0][2] * c[1][1]) * invDet; 376 res.c[1][0] = -(c[1][0] * c[2][2] - c[1][2] * c[2][0]) * invDet; 377 res.c[1][1] = (c[0][0] * c[2][2] - c[0][2] * c[2][0]) * invDet; 378 res.c[1][2] = -(c[0][0] * c[1][2] - c[1][0] * c[0][2]) * invDet; 379 res.c[2][0] = (c[1][0] * c[2][1] - c[2][0] * c[1][1]) * invDet; 380 res.c[2][1] = -(c[0][0] * c[2][1] - c[2][0] * c[0][1]) * invDet; 381 res.c[2][2] = (c[0][0] * c[1][1] - c[1][0] * c[0][1]) * invDet; 382 return res; 383 } 384 } 385 386 static if (isSquare && isFloatingPoint!T && R == 4) 387 { 388 /// Returns an inverted copy of this matrix 389 /// Returns: inverse of matrix. 390 /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. 391 @nogc Matrix inverse() pure const nothrow 392 { 393 T det2_01_01 = c[0][0] * c[1][1] - c[0][1] * c[1][0]; 394 T det2_01_02 = c[0][0] * c[1][2] - c[0][2] * c[1][0]; 395 T det2_01_03 = c[0][0] * c[1][3] - c[0][3] * c[1][0]; 396 T det2_01_12 = c[0][1] * c[1][2] - c[0][2] * c[1][1]; 397 T det2_01_13 = c[0][1] * c[1][3] - c[0][3] * c[1][1]; 398 T det2_01_23 = c[0][2] * c[1][3] - c[0][3] * c[1][2]; 399 400 T det3_201_012 = c[2][0] * det2_01_12 - c[2][1] * det2_01_02 + c[2][2] * det2_01_01; 401 T det3_201_013 = c[2][0] * det2_01_13 - c[2][1] * det2_01_03 + c[2][3] * det2_01_01; 402 T det3_201_023 = c[2][0] * det2_01_23 - c[2][2] * det2_01_03 + c[2][3] * det2_01_02; 403 T det3_201_123 = c[2][1] * det2_01_23 - c[2][2] * det2_01_13 + c[2][3] * det2_01_12; 404 405 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]; 406 assert(det != 0); // Programming error if matrix is not invertible. 407 T invDet = 1 / det; 408 409 T det2_03_01 = c[0][0] * c[3][1] - c[0][1] * c[3][0]; 410 T det2_03_02 = c[0][0] * c[3][2] - c[0][2] * c[3][0]; 411 T det2_03_03 = c[0][0] * c[3][3] - c[0][3] * c[3][0]; 412 T det2_03_12 = c[0][1] * c[3][2] - c[0][2] * c[3][1]; 413 T det2_03_13 = c[0][1] * c[3][3] - c[0][3] * c[3][1]; 414 T det2_03_23 = c[0][2] * c[3][3] - c[0][3] * c[3][2]; 415 T det2_13_01 = c[1][0] * c[3][1] - c[1][1] * c[3][0]; 416 T det2_13_02 = c[1][0] * c[3][2] - c[1][2] * c[3][0]; 417 T det2_13_03 = c[1][0] * c[3][3] - c[1][3] * c[3][0]; 418 T det2_13_12 = c[1][1] * c[3][2] - c[1][2] * c[3][1]; 419 T det2_13_13 = c[1][1] * c[3][3] - c[1][3] * c[3][1]; 420 T det2_13_23 = c[1][2] * c[3][3] - c[1][3] * c[3][2]; 421 422 T det3_203_012 = c[2][0] * det2_03_12 - c[2][1] * det2_03_02 + c[2][2] * det2_03_01; 423 T det3_203_013 = c[2][0] * det2_03_13 - c[2][1] * det2_03_03 + c[2][3] * det2_03_01; 424 T det3_203_023 = c[2][0] * det2_03_23 - c[2][2] * det2_03_03 + c[2][3] * det2_03_02; 425 T det3_203_123 = c[2][1] * det2_03_23 - c[2][2] * det2_03_13 + c[2][3] * det2_03_12; 426 427 T det3_213_012 = c[2][0] * det2_13_12 - c[2][1] * det2_13_02 + c[2][2] * det2_13_01; 428 T det3_213_013 = c[2][0] * det2_13_13 - c[2][1] * det2_13_03 + c[2][3] * det2_13_01; 429 T det3_213_023 = c[2][0] * det2_13_23 - c[2][2] * det2_13_03 + c[2][3] * det2_13_02; 430 T det3_213_123 = c[2][1] * det2_13_23 - c[2][2] * det2_13_13 + c[2][3] * det2_13_12; 431 432 T det3_301_012 = c[3][0] * det2_01_12 - c[3][1] * det2_01_02 + c[3][2] * det2_01_01; 433 T det3_301_013 = c[3][0] * det2_01_13 - c[3][1] * det2_01_03 + c[3][3] * det2_01_01; 434 T det3_301_023 = c[3][0] * det2_01_23 - c[3][2] * det2_01_03 + c[3][3] * det2_01_02; 435 T det3_301_123 = c[3][1] * det2_01_23 - c[3][2] * det2_01_13 + c[3][3] * det2_01_12; 436 437 Matrix res = void; 438 res.c[0][0] = - det3_213_123 * invDet; 439 res.c[1][0] = + det3_213_023 * invDet; 440 res.c[2][0] = - det3_213_013 * invDet; 441 res.c[3][0] = + det3_213_012 * invDet; 442 443 res.c[0][1] = + det3_203_123 * invDet; 444 res.c[1][1] = - det3_203_023 * invDet; 445 res.c[2][1] = + det3_203_013 * invDet; 446 res.c[3][1] = - det3_203_012 * invDet; 447 448 res.c[0][2] = + det3_301_123 * invDet; 449 res.c[1][2] = - det3_301_023 * invDet; 450 res.c[2][2] = + det3_301_013 * invDet; 451 res.c[3][2] = - det3_301_012 * invDet; 452 453 res.c[0][3] = - det3_201_123 * invDet; 454 res.c[1][3] = + det3_201_023 * invDet; 455 res.c[2][3] = - det3_201_013 * invDet; 456 res.c[3][3] = + det3_201_012 * invDet; 457 return res; 458 } 459 } 460 461 /// Returns a transposed copy of this matrix 462 /// Returns: transposed matrice. 463 @nogc Matrix!(T, C, R) transposed() pure const nothrow 464 { 465 Matrix!(T, C, R) res; 466 for (int i = 0; i < C; ++i) 467 for (int j = 0; j < R; ++j) 468 res.c[i][j] = c[j][i]; 469 return res; 470 } 471 472 static if (isSquare && R > 1) 473 { 474 /// Makes a diagonal matrix from a vector. 475 @nogc static Matrix diag(Vector!(T, R) v) pure nothrow 476 { 477 Matrix res = void; 478 for (int i = 0; i < R; ++i) 479 for (int j = 0; j < C; ++j) 480 res.c[i][j] = (i == j) ? v.v[i] : 0; 481 return res; 482 } 483 484 /// In-place translate by (v, 1) 485 @nogc void translate(Vector!(T, R-1) v) pure nothrow 486 { 487 for (int i = 0; i < R; ++i) 488 { 489 T dot = 0; 490 for (int j = 0; j + 1 < C; ++j) 491 dot += v.v[j] * c[i][j]; 492 493 c[i][C-1] += dot; 494 } 495 } 496 497 /// Make a translation matrix. 498 @nogc static Matrix translation(Vector!(T, R-1) v) pure nothrow 499 { 500 Matrix res = identity(); 501 for (int i = 0; i + 1 < R; ++i) 502 res.c[i][C-1] += v.v[i]; 503 return res; 504 } 505 506 /// In-place matrix scaling. 507 void scale(Vector!(T, R-1) v) pure nothrow 508 { 509 for (int i = 0; i < R; ++i) 510 for (int j = 0; j + 1 < C; ++j) 511 c[i][j] *= v.v[j]; 512 } 513 514 /// Make a scaling matrix. 515 @nogc static Matrix scaling(Vector!(T, R-1) v) pure nothrow 516 { 517 Matrix res = identity(); 518 for (int i = 0; i + 1 < R; ++i) 519 res.c[i][i] = v.v[i]; 520 return res; 521 } 522 } 523 524 // rotations are implemented for 3x3 and 4x4 matrices. 525 static if (isSquare && (R == 3 || R == 4) && isFloatingPoint!T) 526 { 527 @nogc public static Matrix rotateAxis(int i, int j)(T angle) pure nothrow 528 { 529 Matrix res = identity(); 530 const T cosa = cos(angle); 531 const T sina = sin(angle); 532 res.c[i][i] = cosa; 533 res.c[i][j] = -sina; 534 res.c[j][i] = sina; 535 res.c[j][j] = cosa; 536 return res; 537 } 538 539 /// Rotate along X axis 540 /// Returns: rotation matrix along axis X 541 alias rotateAxis!(1, 2) rotateX; 542 543 /// Rotate along Y axis 544 /// Returns: rotation matrix along axis Y 545 alias rotateAxis!(2, 0) rotateY; 546 547 /// Rotate along Z axis 548 /// Returns: rotation matrix along axis Z 549 alias rotateAxis!(0, 1) rotateZ; 550 551 /// Similar to the glRotate matrix, however the angle is expressed in radians 552 /// See_also: $(LINK http://www.cs.rutgers.edu/~decarlo/428/gl_man/rotate.html) 553 @nogc static Matrix rotation(T angle, vec3!T axis) pure nothrow 554 { 555 Matrix res = identity(); 556 const T c = cos(angle); 557 const oneMinusC = 1 - c; 558 const T s = sin(angle); 559 axis = axis.normalized(); 560 T x = axis.x, 561 y = axis.y, 562 z = axis.z; 563 T xy = x * y, 564 yz = y * z, 565 xz = x * z; 566 567 res.c[0][0] = x * x * oneMinusC + c; 568 res.c[0][1] = x * y * oneMinusC - z * s; 569 res.c[0][2] = x * z * oneMinusC + y * s; 570 res.c[1][0] = y * x * oneMinusC + z * s; 571 res.c[1][1] = y * y * oneMinusC + c; 572 res.c[1][2] = y * z * oneMinusC - x * s; 573 res.c[2][0] = z * x * oneMinusC - y * s; 574 res.c[2][1] = z * y * oneMinusC + x * s; 575 res.c[2][2] = z * z * oneMinusC + c; 576 return res; 577 } 578 } 579 580 // 4x4 specific transformations for 3D usage 581 static if (isSquare && R == 4 && isFloatingPoint!T) 582 { 583 /// Orthographic projection 584 /// Returns: orthographic projection. 585 @nogc static Matrix orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow 586 { 587 T dx = right - left, 588 dy = top - bottom, 589 dz = far - near; 590 591 T tx = -(right + left) / dx; 592 T ty = -(top + bottom) / dy; 593 T tz = -(far + near) / dz; 594 595 return Matrix(2 / dx, 0, 0, tx, 596 0, 2 / dy, 0, ty, 597 0, 0, -2 / dz, tz, 598 0, 0, 0, 1); 599 } 600 601 /// Perspective projection 602 /// Returns: perspective projection. 603 @nogc static Matrix perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow 604 { 605 T f = 1 / tan(FOVInRadians / 2); 606 T d = 1 / (zNear - zFar); 607 608 return Matrix(f / aspect, 0, 0, 0, 609 0, f, 0, 0, 610 0, 0, (zFar + zNear) * d, 2 * d * zFar * zNear, 611 0, 0, -1, 0); 612 } 613 614 /// Look At projection 615 /// Returns: "lookAt" projection. 616 /// Thanks to vuaru for corrections. 617 @nogc static Matrix lookAt(vec3!T eye, vec3!T target, vec3!T up) pure nothrow 618 { 619 vec3!T Z = (eye - target).normalized(); 620 vec3!T X = cross(-up, Z).normalized(); 621 vec3!T Y = cross(Z, -X); 622 623 return Matrix(-X.x, -X.y, -X.z, dot(X, eye), 624 Y.x, Y.y, Y.z, -dot(Y, eye), 625 Z.x, Z.y, Z.z, -dot(Z, eye), 626 0, 0, 0, 1); 627 } 628 } 629 } 630 631 package 632 { 633 alias T _T; 634 enum _R = R; 635 enum _C = C; 636 } 637 638 private 639 { 640 template isAssignable(T) 641 { 642 enum bool isAssignable = std.traits.isAssignable!(Matrix, T); 643 } 644 645 template isConvertible(T) 646 { 647 enum bool isConvertible = (!is(T : Matrix)) && isAssignable!T; 648 } 649 650 template isTAssignable(U) 651 { 652 enum bool isTAssignable = std.traits.isAssignable!(T, U); 653 } 654 655 template isRowConvertible(U) 656 { 657 enum bool isRowConvertible = is(U : row_t); 658 } 659 660 template isColumnConvertible(U) 661 { 662 enum bool isColumnConvertible = is(U : column_t); 663 } 664 } 665 666 public 667 { 668 /// Construct an identity matrix 669 /// Returns: an identity matrix. 670 /// Note: the identity matrix, while only meaningful for square matrices, 671 /// is also defined for non-square ones. 672 @nogc static Matrix identity() pure nothrow 673 { 674 Matrix res = void; 675 for (int i = 0; i < R; ++i) 676 for (int j = 0; j < C; ++j) 677 res.c[i][j] = (i == j) ? 1 : 0; 678 return res; 679 } 680 681 /// Construct an constant matrix 682 /// Returns: a constant matrice. 683 @nogc static Matrix constant(U)(U x) pure nothrow 684 { 685 Matrix res = void; 686 687 for (int i = 0; i < R * C; ++i) 688 res.v[i] = cast(T)x; 689 return res; 690 } 691 } 692 } 693 694 template isMatrixInstantiation(U) 695 { 696 private static void isMatrix(T, int R, int C)(Matrix!(T, R, C) x) 697 { 698 } 699 700 enum bool isMatrixInstantiation = is(typeof(isMatrix(U.init))); 701 } 702 703 // GLSL is a big inspiration here 704 // we defines types with more or less the same names 705 706 /// 707 template mat2x2(T) { alias Matrix!(T, 2, 2) mat2x2; } 708 /// 709 template mat3x3(T) { alias Matrix!(T, 3, 3) mat3x3; } 710 /// 711 template mat4x4(T) { alias Matrix!(T, 4, 4) mat4x4; } 712 713 // WARNING: in GLSL, first number is _columns_, second is rows 714 // It is the opposite here: first number is rows, second is columns 715 // With this convention mat2x3 * mat3x4 -> mat2x4. 716 717 /// 718 template mat2x3(T) { alias Matrix!(T, 2, 3) mat2x3; } 719 /// 720 template mat2x4(T) { alias Matrix!(T, 2, 4) mat2x4; } 721 /// 722 template mat3x2(T) { alias Matrix!(T, 3, 2) mat3x2; } 723 /// 724 template mat3x4(T) { alias Matrix!(T, 3, 4) mat3x4; } 725 /// 726 template mat4x2(T) { alias Matrix!(T, 4, 2) mat4x2; } 727 /// 728 template mat4x3(T) { alias Matrix!(T, 4, 3) mat4x3; } 729 730 // shorter names for most common matrices 731 alias mat2x2 mat2;/// 732 alias mat3x3 mat3;/// 733 alias mat4x4 mat4;/// 734 735 // Define a lot of type names 736 // Most useful are probably mat4f and mat4d 737 738 alias mat2!float mat2f;/// 739 alias mat2!double mat2d;/// 740 741 alias mat3!float mat3f;/// 742 alias mat3!double mat3d;/// 743 744 alias mat4!float mat4f;/// 745 alias mat4!double mat4d;/// 746 747 alias mat2x2!float mat2x2f;/// 748 alias mat2x2!double mat2x2d;/// 749 750 alias mat3x3!float mat3x3f;/// 751 alias mat3x3!double mat3x3d;/// 752 753 alias mat4x4!float mat4x4f;/// 754 alias mat4x4!double mat4x4d;/// 755 756 unittest 757 { 758 alias mat2i = mat2!int; 759 alias mat2x3f = mat2x3!float; 760 alias mat3x4f = mat3x4!float; 761 alias mat2x4f = mat2x4!float; 762 763 mat2i x = mat2i(0, 1, 764 2, 3); 765 assert(x.c[0][0] == 0 && x.c[0][1] == 1 && x.c[1][0] == 2 && x.c[1][1] == 3); 766 767 vec2i[2] cols = [vec2i(0, 2), vec2i(1, 3)]; 768 mat2i y = mat2i.fromColumns(cols[]); 769 assert(y.c[0][0] == 0 && y.c[0][1] == 1 && y.c[1][0] == 2 && y.c[1][1] == 3); 770 y = mat2i.fromRows(cols[]); 771 assert(y.c[0][0] == 0 && y.c[1][0] == 1 && y.c[0][1] == 2 && y.c[1][1] == 3); 772 y = y.transposed(); 773 774 assert(x == y); 775 x = [0, 1, 2, 3]; 776 assert(x == y); 777 778 mat2i z = x * y; 779 assert(z == mat2i([2, 3, 6, 11])); 780 vec2i vz = z * vec2i(2, -1); 781 assert(vz == vec2i(1, 1)); 782 783 mat2f a = z; 784 mat2d ad = a; 785 ad += a; 786 mat2f w = [4, 5, 6, 7]; 787 z = cast(mat2i)w; 788 assert(w == z); 789 790 { 791 mat2x3f A; 792 mat3x4f B; 793 mat2x4f C = A * B; 794 } 795 796 assert(mat2i.diag(vec2i(1, 2)) == mat2i(1, 0, 797 0, 2)); 798 799 // Construct with a single scalar 800 auto D = mat4f(1.0f); 801 assert(D.v[] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]); 802 803 { 804 double[4][3] starray = [ 805 [ 0, 1, 2, 3], 806 [ 4, 5, 6, 7,], 807 [ 8, 9, 10, 11,], 808 ]; 809 810 // starray has the shape 3x4 811 assert(starray.length == 3); 812 assert(starray[0].length == 4); 813 814 auto m = mat3x4!double(starray); 815 assert(m.v[] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ]); 816 } 817 818 { 819 auto dynarray = [ 820 [ 0, 1, 2, 3], 821 [ 4, 5, 6, 7,], 822 [ 8, 9, 10, 11,], 823 ]; 824 825 // dynarray has the shape 3x4 826 assert(dynarray.length == 3); 827 assert(dynarray[0].length == 4); 828 829 auto m = mat3x4!double(dynarray); 830 assert(m.v[] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ]); 831 } 832 } 833 834 // Issue #206 (matrix *= scalar) not yielding matrix * scalar but matrix * matrix(scalar) 835 unittest 836 { 837 mat4f mvp = mat4f.identity; 838 mvp *= 2; 839 assert(mvp == mat4f(2, 0, 0, 0, 840 0, 2, 0, 0, 841 0, 0, 2, 0, 842 0, 0, 0, 2)); 843 844 mvp = mat4f.identity * 2; 845 assert(mvp == mat4f(2, 0, 0, 0, 846 0, 2, 0, 0, 847 0, 0, 2, 0, 848 0, 0, 0, 2)); 849 850 851 mvp = mat4f(1) * mat4f(1); 852 assert(mvp == mat4f(4, 4, 4, 4, 853 4, 4, 4, 4, 854 4, 4, 4, 4, 855 4, 4, 4, 4)); 856 857 mvp = mat4f(1); 858 mvp *= mat4f(1); 859 assert(mvp == mat4f(4, 4, 4, 4, 860 4, 4, 4, 4, 861 4, 4, 4, 4, 862 4, 4, 4, 4)); 863 }