1 /* 2 Copyright (c) 2013-2021 Timur Gafarov, Martin Cejp 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Square matrices with static memory allocation 31 * 32 * Copyright: Timur Gafarov, Martin Cejp 2013-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov, Martin Cejp 35 */ 36 module dlib.math.matrix; 37 38 import std.math; 39 import std.range; 40 import std.format; 41 import std.conv; 42 import std..string; 43 44 import dlib.math.vector; 45 import dlib.math.utils; 46 import dlib.math.decomposition; 47 import dlib.math.linsolve; 48 49 /*********************************** 50 * Square (NxN) matrix. 51 * 52 * Implementation notes: 53 * 54 * - The storage order is column-major. 55 * 56 * - Affine vector of 4x4 matrix is in the 4th column (as in OpenGL). 57 * 58 * - Elements are stored in a fixed manner, so it is impossible to change 59 * matrix size once it's created. 60 * 61 * - Actual data is allocated as a static array, so no references, no GC touching. 62 * When you pass a Matrix by value, it will be safely copied. 63 * 64 * - This implementation is not perfect (as for now) for dealing with really 65 * big matrices, but ideal for smaller ones, e.g. those which are meant to be 66 * manipulated in real-time (in game engines, rendering pipelines etc). 67 */ 68 struct Matrix(T, size_t N) 69 { 70 /** 71 * Compare two matrices. 72 * 73 * Params: 74 * that = The matrix to compare with. 75 * 76 * Returns: $(D_KEYWORD true) if dimensions are equal, $(D_KEYWORD false) otherwise. 77 */ 78 bool opEquals(Matrix!(T, N) that) const 79 { 80 return arrayof == that.arrayof; 81 } 82 83 /** 84 * Return zero matrix 85 */ 86 static zero() 87 do 88 { 89 Matrix!(T,N) res; 90 foreach (ref v; res.arrayof) 91 v = 0; 92 return res; 93 } 94 95 /** 96 * Return identity matrix 97 */ 98 static identity() 99 do 100 { 101 Matrix!(T,N) res; 102 res.setIdentity(); 103 return res; 104 } 105 106 /** 107 * Set to identity 108 */ 109 void setIdentity() 110 do 111 { 112 foreach(y; 0..N) 113 foreach(x; 0..N) 114 { 115 if (y == x) 116 arrayof[y * N + x] = 1; 117 else 118 arrayof[y * N + x] = 0; 119 } 120 } 121 122 /** 123 * Create matrix from array. 124 * This is a convenient way to deal with arrays of "classic" layout: 125 * the storage order in an array should be row-major 126 */ 127 this(F)(F[] arr) 128 in 129 { 130 assert (arr.length == N * N, 131 "Matrix!(T,N): wrong array length in constructor"); 132 } 133 do 134 { 135 foreach (i, ref v; arrayof) 136 { 137 auto i2 = i / N + N * (i - N * (i / N)); 138 v = arr[i2]; 139 } 140 } 141 142 /** 143 * T = Matrix[i, j] 144 */ 145 T opIndex(in size_t i, in size_t j) const 146 do 147 { 148 return arrayof[j * N + i]; 149 } 150 151 /** 152 * Matrix[i, j] = T 153 */ 154 T opIndexAssign(in T t, in size_t i, in size_t j) 155 do 156 { 157 return (arrayof[j * N + i] = t); 158 } 159 160 /** 161 * T = Matrix[index] 162 * Indices start with 0 163 */ 164 T opIndex(in size_t index) const 165 in 166 { 167 assert ((0 <= index) && (index < N * N), 168 "Matrix.opIndex(int index): array index out of bounds"); 169 } 170 do 171 { 172 return arrayof[index]; 173 } 174 175 /** 176 * Matrix[index] = T 177 * Indices start with 0 178 */ 179 T opIndexAssign(in T t, in size_t index) 180 in 181 { 182 assert ((0 <= index) && (index < N * N), 183 "Matrix.opIndexAssign(T t, int index): array index out of bounds"); 184 } 185 do 186 { 187 return (arrayof[index] = t); 188 } 189 190 /** 191 * Matrix4x4!(T)[index1..index2] = T 192 */ 193 T[] opSliceAssign(in T t, in size_t index1, in size_t index2) 194 in 195 { 196 assert ((0 <= index1) && (index1 < N * N), 197 "Matrix.opSliceAssign(T t, int index1, int index2): index1 out of bounds"); 198 assert ((0 <= index2) && (index2 < N * N), 199 "Matrix.opSliceAssign(T t, int index1, int index2): index2 out of bounds"); 200 } 201 do 202 { 203 return (arrayof[index1..index2] = t); 204 } 205 206 /** 207 * Matrix[] = T 208 */ 209 T[] opSliceAssign(in T t) 210 do 211 { 212 return (arrayof[] = t); 213 } 214 215 /** 216 * Matrix + Matrix 217 */ 218 Matrix!(T,N) opBinary(string op)(Matrix!(T,N) mat) const if (op == "+") 219 do 220 { 221 auto res = Matrix!(T,N)(); 222 foreach (i; 0..N) 223 foreach (j; 0..N) 224 { 225 res[i, j] = this[i, j] + mat[i, j]; 226 } 227 return res; 228 } 229 230 /** 231 * Matrix - Matrix 232 */ 233 Matrix!(T,N) opBinary(string op)(Matrix!(T,N) mat) const if (op == "-") 234 do 235 { 236 auto res = Matrix!(T,N)(); 237 foreach (i; 0..N) 238 foreach (j; 0..N) 239 { 240 res[i, j] = this[i, j] - mat[i, j]; 241 } 242 return res; 243 } 244 245 /** 246 * Matrix * Matrix 247 */ 248 Matrix!(T,N) opBinary(string op)(Matrix!(T,N) mat) const if (op == "*") 249 do 250 { 251 static if (N == 2) 252 { 253 Matrix!(T,N) res; 254 255 res.a11 = (a11 * mat.a11) + (a12 * mat.a21); 256 res.a12 = (a11 * mat.a12) + (a12 * mat.a22); 257 258 res.a21 = (a21 * mat.a11) + (a22 * mat.a21); 259 res.a22 = (a21 * mat.a12) + (a22 * mat.a22); 260 261 return res; 262 } 263 else static if (N == 3) 264 { 265 Matrix!(T,N) res; 266 267 res.a11 = (a11 * mat.a11) + (a12 * mat.a21) + (a13 * mat.a31); 268 res.a12 = (a11 * mat.a12) + (a12 * mat.a22) + (a13 * mat.a32); 269 res.a13 = (a11 * mat.a13) + (a12 * mat.a23) + (a13 * mat.a33); 270 271 res.a21 = (a21 * mat.a11) + (a22 * mat.a21) + (a23 * mat.a31); 272 res.a22 = (a21 * mat.a12) + (a22 * mat.a22) + (a23 * mat.a32); 273 res.a23 = (a21 * mat.a13) + (a22 * mat.a23) + (a23 * mat.a33); 274 275 res.a31 = (a31 * mat.a11) + (a32 * mat.a21) + (a33 * mat.a31); 276 res.a32 = (a31 * mat.a12) + (a32 * mat.a22) + (a33 * mat.a32); 277 res.a33 = (a31 * mat.a13) + (a32 * mat.a23) + (a33 * mat.a33); 278 279 return res; 280 } 281 else static if (N == 4) 282 { 283 Matrix!(T,N) res; 284 285 res.a11 = (a11 * mat.a11) + (a12 * mat.a21) + (a13 * mat.a31) + (a14 * mat.a41); 286 res.a12 = (a11 * mat.a12) + (a12 * mat.a22) + (a13 * mat.a32) + (a14 * mat.a42); 287 res.a13 = (a11 * mat.a13) + (a12 * mat.a23) + (a13 * mat.a33) + (a14 * mat.a43); 288 res.a14 = (a11 * mat.a14) + (a12 * mat.a24) + (a13 * mat.a34) + (a14 * mat.a44); 289 290 res.a21 = (a21 * mat.a11) + (a22 * mat.a21) + (a23 * mat.a31) + (a24 * mat.a41); 291 res.a22 = (a21 * mat.a12) + (a22 * mat.a22) + (a23 * mat.a32) + (a24 * mat.a42); 292 res.a23 = (a21 * mat.a13) + (a22 * mat.a23) + (a23 * mat.a33) + (a24 * mat.a43); 293 res.a24 = (a21 * mat.a14) + (a22 * mat.a24) + (a23 * mat.a34) + (a24 * mat.a44); 294 295 res.a31 = (a31 * mat.a11) + (a32 * mat.a21) + (a33 * mat.a31) + (a34 * mat.a41); 296 res.a32 = (a31 * mat.a12) + (a32 * mat.a22) + (a33 * mat.a32) + (a34 * mat.a42); 297 res.a33 = (a31 * mat.a13) + (a32 * mat.a23) + (a33 * mat.a33) + (a34 * mat.a43); 298 res.a34 = (a31 * mat.a14) + (a32 * mat.a24) + (a33 * mat.a34) + (a34 * mat.a44); 299 300 res.a41 = (a41 * mat.a11) + (a42 * mat.a21) + (a43 * mat.a31) + (a44 * mat.a41); 301 res.a42 = (a41 * mat.a12) + (a42 * mat.a22) + (a43 * mat.a32) + (a44 * mat.a42); 302 res.a43 = (a41 * mat.a13) + (a42 * mat.a23) + (a43 * mat.a33) + (a44 * mat.a43); 303 res.a44 = (a41 * mat.a14) + (a42 * mat.a24) + (a43 * mat.a34) + (a44 * mat.a44); 304 305 return res; 306 } 307 else 308 { 309 auto res = Matrix!(T,N)(); 310 311 foreach (i; 0..N) 312 foreach (j; 0..N) 313 { 314 T sumProduct = 0; 315 foreach (k; 0..N) 316 sumProduct += this[i, k] * mat[k, j]; 317 res[i, j] = sumProduct; 318 } 319 320 return res; 321 } 322 } 323 324 /** 325 * Matrix += Matrix 326 */ 327 Matrix!(T,N) opOpAssign(string op)(Matrix!(T,N) mat) if (op == "+") 328 do 329 { 330 this = this + mat; 331 return this; 332 } 333 334 /** 335 * Matrix -= Matrix 336 */ 337 Matrix!(T,N) opOpAssign(string op)(Matrix!(T,N) mat) if (op == "-") 338 do 339 { 340 this = this - mat; 341 return this; 342 } 343 344 /** 345 * Matrix *= Matrix 346 */ 347 Matrix!(T,N) opOpAssign(string op)(Matrix!(T,N) mat) if (op == "*") 348 do 349 { 350 this = this * mat; 351 return this; 352 } 353 354 /** 355 * Matrix * T 356 */ 357 Matrix!(T,N) opBinary(string op)(T k) const if (op == "*") 358 do 359 { 360 auto res = Matrix!(T,N)(); 361 foreach(i, v; arrayof) 362 res.arrayof[i] = v * k; 363 return res; 364 } 365 366 /** 367 * Matrix *= T 368 */ 369 Matrix!(T,N) opOpAssign(string op)(T k) if (op == "*") 370 do 371 { 372 foreach(ref v; arrayof) 373 v *= k; 374 return this; 375 } 376 377 /** 378 * Multiply column vector by the matrix 379 */ 380 static if (N == 2) 381 { 382 Vector!(T,2) opBinaryRight(string op) (Vector!(T,2) v) const if (op == "*") 383 do 384 { 385 return Vector!(T,2) 386 ( 387 (v.x * a11) + (v.y * a12), 388 (v.x * a21) + (v.y * a22) 389 ); 390 } 391 } 392 else 393 static if (N == 3) 394 { 395 Vector!(T,3) opBinaryRight(string op) (Vector!(T,3) v) const if (op == "*") 396 do 397 { 398 return Vector!(T,3) 399 ( 400 (v.x * a11) + (v.y * a12) + (v.z * a13), 401 (v.x * a21) + (v.y * a22) + (v.z * a23), 402 (v.x * a31) + (v.y * a32) + (v.z * a33) 403 ); 404 } 405 } 406 else 407 { 408 Vector!(T,N) opBinaryRight(string op) (Vector!(T,N) v) const if (op == "*") 409 do 410 { 411 Vector!(T,N) res; 412 foreach(x; 0..N) 413 { 414 T n = 0; 415 foreach(y; 0..N) 416 n += v.arrayof[y] * arrayof[y * N + x]; 417 res.arrayof[x] = n; 418 } 419 return res; 420 } 421 } 422 423 /** 424 * Multiply column 3D vector by the affine 4x4 matrix 425 */ 426 static if (N == 4) 427 { 428 Vector!(T,3) opBinaryRight(string op) (Vector!(T,3) v) const if (op == "*") 429 do 430 { 431 return Vector!(T,3) 432 ( 433 (v.x * a11) + (v.y * a12) + (v.z * a13) + a14, 434 (v.x * a21) + (v.y * a22) + (v.z * a23) + a24, 435 (v.x * a31) + (v.y * a32) + (v.z * a33) + a34 436 ); 437 } 438 } 439 440 static if (N == 3 || N == 4) 441 { 442 /** 443 * Rotate a vector by the 3x3 upper-left portion of the matrix 444 */ 445 Vector!(T,3) rotate(Vector!(T,3) v) const 446 do 447 { 448 return Vector!(T,3) 449 ( 450 (v.x * a11) + (v.y * a12) + (v.z * a13), 451 (v.x * a21) + (v.y * a22) + (v.z * a23), 452 (v.x * a31) + (v.y * a32) + (v.z * a33) 453 ); 454 } 455 456 /** 457 * Rotate a vector by the inverse 3x3 upper-left portion of the matrix 458 */ 459 Vector!(T,3) invRotate(Vector!(T,3) v) const 460 do 461 { 462 return Vector!(T,3) 463 ( 464 (v.x * a11) + (v.y * a21) + (v.z * a31), 465 (v.x * a12) + (v.y * a22) + (v.z * a32), 466 (v.x * a13) + (v.y * a23) + (v.z * a33) 467 ); 468 } 469 } 470 471 static if (N == 4 || N == 3) 472 { 473 T determinant3x3() const 474 do 475 { 476 return a11 * (a33 * a22 - a32 * a23) 477 - a21 * (a33 * a12 - a32 * a13) 478 + a31 * (a23 * a12 - a22 * a13); 479 } 480 } 481 482 static if (N == 1) 483 { 484 /** 485 * Determinant (of upper-left 3x3 portion for 4x4 matrices) 486 */ 487 T determinant() const 488 do 489 { 490 return a11; 491 } 492 } 493 else 494 static if (N == 2) 495 { 496 T determinant() const 497 do 498 { 499 return a11 * a22 - a12 * a21; 500 } 501 } 502 else 503 static if (N == 3) 504 { 505 alias determinant = determinant3x3; 506 } 507 else 508 { 509 // Determinant of a given upper-left portion 510 T determinant(size_t n = N) const 511 do 512 { 513 T d = 0; 514 515 if (n == 1) 516 d = this[0,0]; 517 else if (n == 2) 518 d = this[0,0] * this[1,1] - this[1,0] * this[0,1]; 519 else 520 { 521 auto submat = Matrix!(T,N)(); 522 523 for (uint c = 0; c < n; c++) 524 { 525 uint subi = 0; 526 for (uint i = 1; i < n; i++) 527 { 528 uint subj = 0; 529 for (uint j = 0; j < n; j++) 530 { 531 if (j == c) 532 continue; 533 submat[subi, subj] = this[i, j]; 534 subj++; 535 } 536 subi++; 537 } 538 539 d += pow(-1, c + 2.0) * this[0, c] * submat.determinant(n-1); 540 } 541 } 542 543 return d; 544 } 545 } 546 547 alias det = determinant; 548 549 /** 550 * Return true if matrix is singular 551 */ 552 bool isSingular() @property 553 do 554 { 555 return (determinant == 0); 556 } 557 558 alias singular = isSingular; 559 560 /** 561 * Check if matrix represents affine transformation 562 */ 563 static if (N == 4) 564 { 565 bool isAffine() @property 566 do 567 { 568 return (a41 == 0.0 569 && a42 == 0.0 570 && a43 == 0.0 571 && a44 == 1.0); 572 } 573 574 alias affine = isAffine; 575 } 576 577 /** 578 * Transpose 579 */ 580 void transpose() 581 do 582 { 583 this = transposed; 584 } 585 586 /** 587 * Return the transposed matrix 588 */ 589 Matrix!(T,N) transposed() @property 590 do 591 { 592 Matrix!(T,N) res; 593 594 foreach(y; 0..N) 595 foreach(x; 0..N) 596 res.arrayof[y * N + x] = arrayof[x * N + y]; 597 598 return res; 599 } 600 601 /** 602 * Invert 603 */ 604 void invert() 605 do 606 { 607 this = inverse; 608 } 609 610 /** 611 * Inverse of a matrix 612 */ 613 static if (N == 1) 614 { 615 Matrix!(T,N) inverse() @property 616 do 617 { 618 Matrix!(T,N) res; 619 res.a11 = 1.0 / a11; 620 return res; 621 } 622 } 623 else 624 static if (N == 2) 625 { 626 Matrix!(T,N) inverse() @property 627 do 628 { 629 Matrix!(T,N) res; 630 631 T invd = 1.0 / (a11 * a22 - a12 * a21); 632 633 res.a11 = a22 * invd; 634 res.a12 = -a12 * invd; 635 res.a22 = a11 * invd; 636 res.a21 = -a21 * invd; 637 638 return res; 639 } 640 } 641 else 642 static if (N == 3) 643 { 644 Matrix!(T,N) inverse() @property 645 do 646 { 647 T d = determinant; 648 649 T oneOverDet = 1.0 / d; 650 651 Matrix!(T,N) res; 652 653 res.a11 = (a33 * a22 - a32 * a23) * oneOverDet; 654 res.a12 = -(a33 * a12 - a32 * a13) * oneOverDet; 655 res.a13 = (a23 * a12 - a22 * a13) * oneOverDet; 656 657 res.a21 = -(a33 * a21 - a31 * a23) * oneOverDet; 658 res.a22 = (a33 * a11 - a31 * a13) * oneOverDet; 659 res.a23 = -(a23 * a11 - a21 * a13) * oneOverDet; 660 661 res.a31 = (a32 * a21 - a31 * a22) * oneOverDet; 662 res.a32 = -(a32 * a11 - a31 * a12) * oneOverDet; 663 res.a33 = (a22 * a11 - a21 * a12) * oneOverDet; 664 665 return res; 666 } 667 } 668 else 669 { 670 Matrix!(T,N) inverse() @property 671 do 672 { 673 Matrix!(T,N) res; 674 675 // Inversion via LU decomposition 676 enum inv = q{{ 677 Matrix!(T,N) l, u, p; 678 decomposeLUP(this, l, u, p); 679 foreach(j; 0..N) 680 { 681 Vector!(T,N) b = p.getColumn(j); 682 Vector!(T,N) x; 683 solveLU(l, u, x, b); 684 res.setColumn(j, x); 685 } 686 }}; 687 688 // Inverse of a 4x4 affine matrix is a special case 689 enum affineInv = q{{ 690 auto m3inv = matrix4x4to3x3(this).inverse; 691 res = matrix3x3to4x4(m3inv); 692 Vector!(T,3) t = -(getColumn(3).xyz * m3inv); 693 res.setColumn(3, Vector!(T,4)(t.x, t.y, t.z, 1.0f)); 694 }}; 695 696 static if (N == 4) 697 { 698 if (affine) 699 mixin(affineInv); 700 else 701 mixin(inv); 702 } 703 else 704 mixin(inv); 705 706 return res; 707 } 708 } 709 710 /** 711 * Adjugate and cofactor matrices 712 */ 713 static if (N == 1) 714 { 715 Matrix!(T,N) adjugate() @property 716 do 717 { 718 Matrix!(T,N) res; 719 res.arrayof[0] = 1; 720 return res; 721 } 722 723 Matrix!(T,N) cofactor() @property 724 { 725 Matrix!(T,N) res; 726 res.arrayof[0] = 1; 727 return res; 728 } 729 } 730 else 731 static if (N == 2) 732 { 733 Matrix!(T,N) adjugate() @property 734 do 735 { 736 Matrix!(T,N) res; 737 res.arrayof[0] = arrayof[3]; 738 res.arrayof[1] = -arrayof[1]; 739 res.arrayof[2] = -arrayof[2]; 740 res.arrayof[3] = arrayof[0]; 741 return res; 742 } 743 744 Matrix!(T,N) cofactor() @property 745 { 746 Matrix!(T,N) res; 747 res.arrayof[0] = arrayof[3]; 748 res.arrayof[1] = -arrayof[2]; 749 res.arrayof[2] = -arrayof[1]; 750 res.arrayof[3] = arrayof[0]; 751 return res; 752 } 753 } 754 else 755 { 756 Matrix!(T,N) adjugate() @property 757 do 758 { 759 return cofactor.transposed; 760 } 761 762 Matrix!(T,N) cofactor() @property 763 do 764 { 765 Matrix!(T,N) res; 766 767 foreach(y; 0..N) 768 foreach(x; 0..N) 769 { 770 auto submat = Matrix!(T,N-1)(); 771 772 uint suby = 0; 773 foreach(yy; 0..N) 774 if (yy != y) 775 { 776 uint subx = 0; 777 foreach(xx; 0..N) 778 if (xx != x) 779 { 780 submat[subx, suby] = this[xx, yy]; 781 subx++; 782 } 783 suby++; 784 } 785 786 res[x, y] = submat.determinant * (((x + y) % 2)? -1:1); 787 } 788 789 return res; 790 } 791 } 792 793 /** 794 * Negative matrix 795 */ 796 Matrix!(T,N) negative() @property 797 do 798 { 799 return this * -1; 800 } 801 802 /** 803 * Convert to string 804 */ 805 string toString() @property 806 do 807 { 808 return matrixToStr(this); 809 } 810 811 /** 812 * Symbolic element access 813 */ 814 private static string elements(string letter) @property 815 do 816 { 817 string res; 818 foreach (x; 0..N) 819 foreach (y; 0..N) 820 { 821 res ~= "T " ~ letter ~ to!string(y+1) ~ to!string(x+1) ~ ";"; 822 } 823 return res; 824 } 825 826 /** 827 * Row/column manipulations 828 */ 829 Vector!(T,N) getRow(size_t i) 830 { 831 Vector!(T,N) res; 832 for (size_t j = 0; j < N; j++) 833 res.arrayof[j] = this[i, j]; 834 return res; 835 } 836 837 void setRow(size_t i, Vector!(T,N) v) 838 { 839 for (size_t j = 0; j < N; j++) 840 this[i, j] = v.arrayof[j]; 841 } 842 843 Vector!(T,N) getColumn(size_t j) 844 { 845 Vector!(T,N) res; 846 for (size_t i = 0; i < N; i++) 847 res.arrayof[i] = this[i, j]; 848 return res; 849 } 850 851 void setColumn(size_t j, Vector!(T,N) v) 852 { 853 for (size_t i = 0; i < N; i++) 854 this[i, j] = v.arrayof[i]; 855 } 856 857 void swapRows(size_t r1, size_t r2) 858 { 859 for (size_t j = 0; j < N; j++) 860 { 861 T t = this[r1, j]; 862 this[r1, j] = this[r2, j]; 863 this[r2, j] = t; 864 } 865 } 866 867 void swapColumns(size_t c1, size_t c2) 868 { 869 for (size_t i = 0; i < N; i++) 870 { 871 T t = this[i, c1]; 872 this[i, c1] = this[i, c2]; 873 this[i, c2] = t; 874 } 875 } 876 877 auto flatten() 878 { 879 return transposed.arrayof; 880 } 881 882 /** 883 * Matrix elements 884 */ 885 union 886 { 887 /** 888 * This auto-generated structure provides symbolic access 889 * to matrix elements, like in standard mathematic notation: 890 * --- 891 * a11 a12 a13 a14 .. a1N 892 * a21 a22 a23 a24 .. a2N 893 * a31 a32 a33 a34 .. a3N 894 * a41 a42 a43 a44 .. a4N 895 * : : : : . 896 * aN1 aN2 aN3 aN4 ' aNN 897 * --- 898 */ 899 struct { mixin(elements("a")); } 900 901 /** 902 * Linear array representing elements column by column 903 */ 904 T[N * N] arrayof; 905 } 906 } 907 908 /// 909 unittest 910 { 911 auto m1 = matrixf( 912 1, 2, 0, 6, 913 4, 6, 3, 1, 914 2, 7, 8, 2, 915 0, 5, 2, 1 916 ); 917 auto m2 = matrixf( 918 0, 3, 7, 1, 919 1, 0, 2, 5, 920 1, 9, 2, 6, 921 5, 2, 0, 0 922 ); 923 assert(m1 * m2 == matrixf( 924 32, 15, 11, 11, 925 14, 41, 46, 52, 926 25, 82, 44, 85, 927 12, 20, 14, 37) 928 ); 929 930 auto m3 = Matrix4f.identity; 931 assert(m3 == matrixf( 932 1, 0, 0, 0, 933 0, 1, 0, 0, 934 0, 0, 1, 0, 935 0, 0, 0, 1) 936 ); 937 938 m3[12] = 1; 939 m3.a24 = 2; 940 m3.a34 = 3; 941 m3[1..4] = 0; 942 943 assert(m3[12] == 1); 944 945 assert(m1.determinant3x3 == -25); 946 assert(m1.determinant == 567); 947 948 assert(m1.singular == false); 949 950 assert(m1.affine == false); 951 952 assert(m1.transposed == matrixf( 953 1, 4, 2, 0, 954 2, 6, 7, 5, 955 0, 3, 8, 2, 956 6, 1, 2, 1) 957 ); 958 959 auto m4 = matrixf( 960 0, 3, 2, 961 1, 0, 8, 962 0, 1, 0 963 ); 964 965 assert(m4.inverse == matrixf( 966 -4, 1, 12, 967 -0, 0, 1, 968 0.5, 0, -1.5) 969 ); 970 971 assert(m1.adjugate == matrixf( 972 7, 148, -16, -158, 973 -14, 28, -49, 154, 974 -14, -53, 113, -89, 975 98, -34, 19, -25) 976 ); 977 978 assert(m1.cofactor == matrixf( 979 7, -14, -14, 98, 980 148, 28, -53, -34, 981 -16, -49, 113, 19, 982 -158, 154, -89, -25) 983 ); 984 985 m1.transpose(); 986 assert(m1 == matrixf( 987 1, 4, 2, 0, 988 2, 6, 7, 5, 989 0, 3, 8, 2, 990 6, 1, 2, 1) 991 ); 992 993 Matrix2f m5; 994 m5[] = 1.0f; 995 m5 += matrixf( 996 2, 2, 997 2, 2 998 ); 999 m5 *= m5; 1000 assert(m5 == matrixf( 1001 18, 18, 1002 18, 18) 1003 ); 1004 1005 m5 = m5 - m5; 1006 assert(m5 == Matrix2f.zero); 1007 1008 Matrix2f m6 = matrixf( 1009 2, 2, 1010 2, 2 1011 ); 1012 m6 = m6 * m6; 1013 m6 = m6 * 2; 1014 m6 *= 2; 1015 assert(m6 == matrixf( 1016 32, 32, 1017 32, 32) 1018 ); 1019 assert(m6.determinant == 0); 1020 1021 Matrix3f m7 = matrixf( 1022 3, 3, 3, 1023 3, 3, 3, 1024 3, 3, 3 1025 ); 1026 m7 = m7 * m7; 1027 assert(m7 == matrixf( 1028 27, 27, 27, 1029 27, 27, 27, 1030 27, 27, 27) 1031 ); 1032 1033 Matrix2f m8 = matrixf( 1034 1, 0, 1035 0, 1 1036 ); 1037 m8.invert(); 1038 assert(m8 == matrixf( 1039 1, 0, 1040 0, 1) 1041 ); 1042 assert(m8.negative == matrixf( 1043 -1, 0, 1044 0, -1) 1045 ); 1046 1047 auto m9 = matrixf( 1048 1, 0, 0, 2, 1049 0, 1, 0, 3, 1050 0, 0, 1, 4, 1051 0, 0, 0, 1 1052 ); 1053 assert(m9.affine == true); 1054 assert(m9.inverse == matrixf( 1055 1, 0, 0, -2, 1056 0, 1, 0, -3, 1057 0, 0, 1, -4, 1058 0, 0, 0, 1) 1059 ); 1060 1061 bool isAlmostZero3(Vector3f v) 1062 { 1063 float e = 0.002f; 1064 return abs(v.x) < e && 1065 abs(v.y) < e && 1066 abs(v.z) < e; 1067 } 1068 1069 Vector3f v1 = Vector3f(1, 0, 0); 1070 v1 = v1 * matrixf( 1071 1, 0, 0, 2, 1072 0, 1, 0, 3, 1073 0, 0, 1, 4, 1074 0, 0, 0, 1 1075 ); 1076 assert(v1 == Vector3f(3, 3, 4)); 1077 1078 Vector3f v2 = Vector3f(0, 1, 0); 1079 const float a1 = PI * 0.5f; 1080 v2 = matrixf( 1081 1, 0, 0, 0, 1082 0, cos(a1), -sin(a1), 0, 1083 0, sin(a1), cos(a1), 0, 1084 0, 0, 0, 1 1085 ).rotate(v2); 1086 assert(isAlmostZero3(v2 - Vector3f(0, 0, 1))); 1087 1088 Vector3f v3 = Vector3f(0, 1, 0); 1089 v3 = matrixf( 1090 1, 0, 0, 0, 1091 0, cos(a1), -sin(a1), 0, 1092 0, sin(a1), cos(a1), 0, 1093 0, 0, 0, 1 1094 ).invRotate(v3); 1095 assert(isAlmostZero3(v3 - Vector3f(0, 0, -1))); 1096 1097 auto m10 = matrixf( 1098 1, 2, 3, 1099 3, 2, 1, 1100 2, 3, 1 1101 ); 1102 1103 Vector3f r0 = m10.getRow(0); 1104 assert(isAlmostZero3(r0 - Vector3f(1, 2, 3))); 1105 1106 Vector3f c0 = m10.getColumn(0); 1107 assert(isAlmostZero3(c0 - Vector3f(1, 3, 2))); 1108 1109 m10.setRow(2, Vector3f(1, 1, 1)); 1110 Vector3f r2 = m10.getRow(2); 1111 assert(isAlmostZero3(r2 - Vector3f(1, 1, 1))); 1112 1113 m10.setColumn(2, Vector3f(1, 1, 1)); 1114 Vector3f c2 = m10.getColumn(2); 1115 assert(isAlmostZero3(c2 - Vector3f(1, 1, 1))); 1116 1117 m10.swapRows(0, 1); 1118 Vector3f r1 = m10.getRow(1); 1119 assert(isAlmostZero3(r1 - Vector3f(1, 2, 1))); 1120 1121 m10.swapColumns(1, 2); 1122 Vector3f c1 = m10.getColumn(1); 1123 assert(isAlmostZero3(c1 - Vector3f(1, 1, 1))); 1124 1125 Matrix2f m11 = matrixf( 1126 2, 1, 1127 2, 1 1128 ); 1129 assert(m11.adjugate == matrixf( 1130 1, -1, 1131 -2, 2) 1132 ); 1133 assert(m11.cofactor == matrixf( 1134 1, -2, 1135 -1, 2) 1136 ); 1137 assert(m11.flatten == [2, 1, 2, 1]); 1138 1139 assert(m11.elements("a") == "T a11;T a21;T a12;T a22;"); 1140 1141 Matrix!(float, 1) m12 = matrixf(1); 1142 assert(m12.determinant == 1); 1143 assert(m12.inverse == matrixf(1)); 1144 assert(m12.adjugate == matrixf(1)); 1145 assert(m12.cofactor == matrixf(1)); 1146 } 1147 1148 /* 1149 * Predefined matrix type aliases 1150 */ 1151 /// Alias for single precision 2x2 Matrix 1152 alias Matrix!(float, 2) Matrix2x2f, Matrix2f; 1153 /// Alias for single precision 3x3 Matrix 1154 alias Matrix!(float, 3) Matrix3x3f, Matrix3f; 1155 /// Alias for single precision 4x4 Matrix 1156 alias Matrix!(float, 4) Matrix4x4f, Matrix4f; 1157 /// Alias for double precision 2x2 Matrix 1158 alias Matrix!(double, 2) Matrix2x2d, Matrix2d; 1159 /// Alias for double precision 3x3 Matrix 1160 alias Matrix!(double, 3) Matrix3x3d, Matrix3d; 1161 /// Alias for double precision 4x4 Matrix 1162 alias Matrix!(double, 4) Matrix4x4d, Matrix4d; 1163 1164 /** 1165 * Short aliases 1166 */ 1167 alias mat2 = Matrix2x2f; 1168 alias mat3 = Matrix3x3f; 1169 alias mat4 = Matrix4x4f; 1170 1171 /** 1172 * Matrix factory function 1173 */ 1174 auto matrixf(A...)(A arr) 1175 { 1176 static assert(isPerfectSquare(arr.length), 1177 "matrixf(A): input array length is not perfect square integer"); 1178 return Matrix!(float, cast(size_t)sqrt(cast(float)arr.length))([arr]); 1179 } 1180 1181 /** 1182 * Converts 3x3 matrix to 4x4 matrix. 1183 * 4x4 matrix defaults to identity 1184 */ 1185 Matrix!(T,4) matrix3x3to4x4(T) (Matrix!(T,3) m) 1186 { 1187 auto res = Matrix!(T,4).identity; 1188 res.a11 = m.a11; res.a12 = m.a12; res.a13 = m.a13; 1189 res.a21 = m.a21; res.a22 = m.a22; res.a23 = m.a23; 1190 res.a31 = m.a31; res.a32 = m.a32; res.a33 = m.a33; 1191 return res; 1192 } 1193 1194 /// 1195 unittest 1196 { 1197 Matrix3f m1 = matrixf( 1198 1, 0, 0, 1199 0, 1, 0, 1200 0, 0, 1 1201 ); 1202 Matrix4f m2 = matrix3x3to4x4(m1); 1203 assert(m2 == matrixf( 1204 1, 0, 0, 0, 1205 0, 1, 0, 0, 1206 0, 0, 1, 0, 1207 0, 0, 0, 1) 1208 ); 1209 } 1210 1211 /** 1212 * Converts 4x4 matrix to 3x3 matrix. 1213 * 3x3 matrix defaults to identity 1214 */ 1215 Matrix!(T,3) matrix4x4to3x3(T) (Matrix!(T,4) m) 1216 { 1217 auto res = Matrix!(T,3).identity; 1218 res.a11 = m.a11; res.a12 = m.a12; res.a13 = m.a13; 1219 res.a21 = m.a21; res.a22 = m.a22; res.a23 = m.a23; 1220 res.a31 = m.a31; res.a32 = m.a32; res.a33 = m.a33; 1221 return res; 1222 } 1223 1224 /// 1225 unittest 1226 { 1227 Matrix4f m1 = matrixf( 1228 1, 0, 0, 0, 1229 0, 1, 0, 0, 1230 0, 0, 1, 0, 1231 0, 0, 0, 1 1232 ); 1233 Matrix3f m2 = matrix4x4to3x3(m1); 1234 assert(m2 == matrixf( 1235 1, 0, 0, 1236 0, 1, 0, 1237 0, 0, 1) 1238 ); 1239 } 1240 1241 /** 1242 * Formatted matrix printer 1243 */ 1244 string matrixToStr(T, size_t N)(Matrix!(T, N) m) 1245 { 1246 uint width = 8; 1247 string maxnum; 1248 foreach(x; m.arrayof) 1249 { 1250 string num; 1251 real frac, integ; 1252 frac = modf(x, integ); 1253 if (frac == 0.0f) 1254 { 1255 num = format("% s", to!long(integ)); 1256 if (num.length > width) 1257 width = cast(uint)num.length; 1258 } 1259 else 1260 { 1261 num = format("% .4f", x); 1262 } 1263 } 1264 1265 auto writer = appender!string(); 1266 foreach (x; 0..N) 1267 { 1268 foreach (y; 0..N) 1269 { 1270 string s = format("% -*.4f", width, m.arrayof[y * N + x]); 1271 uint n = 0; 1272 foreach(i, c; s) 1273 { 1274 if (i < width) 1275 { 1276 formattedWrite(writer, c.to!string); 1277 n++; 1278 } 1279 } 1280 1281 if (y < N-1) 1282 formattedWrite(writer, " "); 1283 } 1284 1285 if (x < N-1) 1286 formattedWrite(writer, "\n"); 1287 } 1288 1289 return writer.data; 1290 } 1291 1292 /// 1293 unittest 1294 { 1295 import std..string; 1296 Matrix2f m1 = matrixf( 1297 1, 0, 1298 0, 1 1299 ); 1300 string s = m1.toString; 1301 assert(s.startsWith(" 1.0000 0.0000 \n 0.0000 1.0000")); 1302 }