1 /+ 2 The MIT License (MIT) 3 4 Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)> 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 +/ 24 25 module des.math.linear.matrix; 26 27 import std.math; 28 import std.traits; 29 import std.algorithm; 30 import std.range; 31 import std.array; 32 import std.exception; 33 34 import des.util.testsuite; 35 36 import des.math.util; 37 import des.math.linear.vector; 38 import des.math.linear.quaterni; 39 import des.math.basic.traits; 40 41 /// 42 template isMatrix(E) 43 { 44 enum isMatrix = is( typeof( impl(E.init) ) ); 45 void impl(size_t H,size_t W,T)( Matrix!(H,W,T) ){} 46 } 47 48 /// 49 template isStaticMatrix(E) 50 { 51 static if( !isMatrix!E ) 52 enum isStaticMatrix = false; 53 else enum isStaticMatrix = E.isStatic; 54 } 55 56 /// 57 template isDynamicMatrix(E) 58 { 59 static if( !isMatrix!E ) 60 enum isDynamicMatrix = false; 61 else enum isDynamicMatrix = E.isDynamic; 62 } 63 64 unittest 65 { 66 static assert( !isStaticMatrix!float ); 67 static assert( !isDynamicMatrix!float ); 68 } 69 70 private @property 71 { 72 import std.string; 73 74 string identityMatrixDataString(size_t S)() 75 { return diagMatrixDataString!(S,S)(1); } 76 77 string zerosMatrixDataString(size_t H, size_t W)() 78 { return diagMatrixDataString!(H,W)(0); } 79 80 string diagMatrixDataString(size_t H, size_t W)( size_t val ) 81 { 82 string[] ret; 83 foreach( i; 0 .. H ) 84 { 85 string[] buf; 86 foreach( j; 0 .. W ) 87 buf ~= format( "%d", i == j ? val : 0 ); 88 ret ~= "[" ~ buf.join(",") ~ "]"; 89 } 90 return "[" ~ ret.join(",") ~ "]"; 91 } 92 93 unittest 94 { 95 assert( identityMatrixDataString!3 == "[[1,0,0],[0,1,0],[0,0,1]]" ); 96 assert( zerosMatrixDataString!(3,3) == "[[0,0,0],[0,0,0],[0,0,0]]" ); 97 } 98 99 string castArrayString(string type, size_t H, size_t W)() 100 { 101 return format( "cast(%s[%d][%d])", type, W, H ); 102 } 103 104 } 105 106 /++ 107 +/ 108 struct Matrix(size_t H, size_t W, E) 109 { 110 /// 111 alias selftype = Matrix!(H,W,E); 112 113 /// 114 alias datatype = E; 115 116 /// `H == 0 || W == 0` 117 enum bool isDynamic = H == 0 || W == 0; 118 /// `H != 0 && W != 0` 119 enum bool isStatic = H != 0 && W != 0; 120 121 /// `H == 0` 122 enum bool isDynamicHeight = H == 0; 123 /// `H != 0` 124 enum bool isStaticHeight = H != 0; 125 126 /// `W == 0` 127 enum bool isDynamicWidth = W == 0; 128 /// `W != 0` 129 enum bool isStaticWidth = W != 0; 130 131 /// `isStaticHeight && isDynamicWidth` 132 enum bool isStaticHeightOnly = isStaticHeight && isDynamicWidth; 133 /// `isStaticWidth && isDynamicHeight` 134 enum bool isStaticWidthOnly = isStaticWidth && isDynamicHeight; 135 136 /// `isDynamicHeight && isDynamicWidth` 137 enum bool isDynamicAll = isDynamicHeight && isDynamicWidth; 138 /// `isStaticWidthOnly || isStaticHeightOnly` 139 enum bool isDynamicOne = isStaticWidthOnly || isStaticHeightOnly; 140 141 static if( isStatic ) 142 { 143 static if( isNumeric!E ) 144 { 145 static if( H == W ) 146 /// static data ( if isNumeric!E fills valid numbers: if squred then identity matrix else zeros ) 147 E[W][H] data = mixin( castArrayString!("E",H,H) ~ identityMatrixDataString!H ); 148 else 149 E[W][H] data = mixin( castArrayString!("E",H,W) ~ zerosMatrixDataString!(H,W) ); 150 } 151 else E[W][H] data; 152 } 153 else static if( isStaticWidthOnly ) 154 /// static width only 155 E[W][] data; 156 else static if( isStaticHeightOnly ) 157 /// static height only 158 E[][H] data; 159 else 160 /// full dynamic 161 E[][] data; 162 163 /// 164 alias data this; 165 166 pure: 167 168 private static bool allowSomeOp(size_t A, size_t B ) 169 { return A==B || A==0 || B==0; } 170 171 static if( isStatic || isDynamicOne ) 172 { 173 /++ fill data and set dynamic size for `isDynamicOne` matrix 174 175 only: 176 if( isStatic || isDynamicOne ) 177 +/ 178 this(X...)( in X vals ) 179 if( is(typeof(flatData!E(vals))) ) 180 { 181 static if( isStatic ) 182 { 183 static if( hasNoDynamic!X ) 184 { 185 static if( X.length > 1 ) 186 { 187 static assert( getElemCount!X == W*H, "wrong args count" ); 188 static assert( isConvertable!(E,X), "wrong args type" ); 189 mixin( matrixStaticFill!("E","data","vals",W,E,X) ); 190 } 191 else static if( X.length == 1 && isStaticMatrix!(X[0]) ) 192 { 193 static assert( X[0].width == W && X[0].height == H ); 194 foreach( y; 0 .. H ) 195 foreach( x; 0 .. W ) 196 data[y][x] = vals[0][y][x]; 197 } 198 else enum __DF=true; 199 } 200 else enum __DF=true; 201 202 static if( is(typeof(__DF)) ) 203 _fillData( flatData!E(vals) ); 204 } 205 else static if( isStaticWidthOnly ) 206 { 207 auto buf = flatData!E(vals); 208 enforce( !(buf.length%W), "wrong args length" ); 209 resize( buf.length/W, W ); 210 _fillData( buf ); 211 } 212 else static if( isStaticHeightOnly ) 213 { 214 auto buf = flatData!E(vals); 215 enforce( !(buf.length%H), "wrong args length" ); 216 resize( H, buf.length/H ); 217 _fillData( buf ); 218 } 219 } 220 } 221 else 222 { 223 /++ set size and fill data 224 225 only: 226 if( isDynamicAll ) 227 +/ 228 this(X...)( size_t iH, size_t iW, in X vals ) 229 { 230 auto buf = flatData!E(vals); 231 enforce( buf.length == iH * iW || buf.length == 1, "wrong args length" ); 232 resize( iH, iW ); 233 _fillData( buf ); 234 } 235 236 /++ set size 237 238 only: 239 if( isDynamicAll ) 240 +/ 241 this( size_t iH, size_t iW ) { resize( iH, iW ); } 242 } 243 244 /// 245 this(size_t oH, size_t oW, X)( in Matrix!(oH,oW,X) mtr ) 246 if( is( typeof( E(X.init) ) ) ) 247 { 248 static if( isDynamic ) 249 resize( mtr.height, mtr.width ); 250 foreach( i; 0 .. mtr.height ) 251 foreach( j; 0 .. mtr.width ) 252 data[i][j] = E(mtr[i][j]); 253 } 254 255 static if( isDynamic ) 256 { 257 /++ 258 only: 259 if( isDynamic ) 260 +/ 261 this(this) 262 { 263 data = data.dup; 264 foreach( ref row; data ) 265 row = row.dup; 266 } 267 268 /++ 269 only: 270 if( isDynamic ) 271 +/ 272 ref typeof(this) opAssign(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) b ) 273 if( allowSomeOp(H,bH) && allowSomeOp(W,bW) && is(typeof(E(X.init))) ) 274 { 275 static if( isStaticHeight && b.isDynamicHeight ) enforce( height == b.height, "wrong height" ); 276 static if( isStaticWidth && b.isDynamicWidth ) enforce( width == b.width, "wrong width" ); 277 static if( isDynamic ) resize(b.height,b.width); 278 _fillData(flatData!E(b.data)); 279 return this; 280 } 281 } 282 283 private void _fillData( in E[] vals... ) 284 { 285 enforce( vals.length > 0, "no vals to fill" ); 286 if( vals.length > 1 ) 287 { 288 enforce( vals.length == height * width, "wrong vals length" ); 289 size_t k = 0; 290 foreach( ref row; data ) 291 foreach( ref v; row ) 292 v = vals[k++]; 293 } 294 else foreach( ref row; data ) row[] = vals[0]; 295 } 296 297 /// fill data 298 ref typeof(this) fill( in E[] vals... ) 299 { 300 _fillData( vals ); 301 return this; 302 } 303 304 static if( (W==H && isStatic) || isDynamicOne ) 305 { 306 /++ get diagonal matrix, diagonal elements fills cyclically 307 308 only: 309 if( (W==H && isStatic) || isDynamicOne ) 310 +/ 311 static auto diag(X...)( in X vals ) 312 if( X.length > 0 && is(typeof(E(0))) && is(typeof(flatData!E(vals))) ) 313 { 314 selftype ret; 315 static if( isStaticHeight ) auto L = H; 316 else auto L = W; 317 318 static if( ret.isDynamic ) 319 ret.resize(L,L); 320 321 ret._fillData( E(0) ); 322 ret.fillDiag( flatData!E(vals) ); 323 324 return ret; 325 } 326 } 327 328 /// 329 ref typeof(this) fillDiag( in E[] vals... ) 330 { 331 enforce( vals.length > 0, "no vals to fill" ); 332 enforce( height == width, "not squared" ); 333 size_t k = 0; 334 foreach( i; 0 .. height ) 335 data[i][i] = vals[k++%$]; 336 return this; 337 } 338 339 static if( isStaticHeight ) 340 /++ 341 only: 342 if( isStaticHeight ) 343 +/ 344 enum height = H; 345 else 346 { 347 /// if isStaticHeight it's enum (not available to set) 348 @property size_t height() const { return data.length; } 349 /// if isStaticHeight it's enum (not available to set) 350 @property size_t height( size_t nh ) 351 { 352 resize( nh, width ); 353 return nh; 354 } 355 } 356 357 static if( isStaticWidth ) 358 /++ 359 only: 360 if( isStaticWidth ) 361 +/ 362 enum width = W; 363 else 364 { 365 private size_t _width = W; 366 /// if isStaticWidth it's enum (not available to set) 367 @property size_t width() const { return _width; } 368 /// if isStaticWidth it's enum (not available to set) 369 @property size_t width( size_t nw ) 370 { 371 resize( height, nw ); 372 return nw; 373 } 374 } 375 376 static if( isDynamic ) 377 { 378 /++ resize dynamic matrix, new height/width must equals static height/width 379 380 only: 381 if( isDynamic ) 382 +/ 383 ref typeof(this) resize( size_t nh, size_t nw ) 384 { 385 static if( isStaticHeight ) enforce( nh == H, "height is static" ); 386 static if( isStaticWidth ) enforce( nw == W, "width is static" ); 387 388 static if( isDynamicHeight ) data.length = nh; 389 static if( isDynamicWidth ) 390 { 391 _width = nw; 392 foreach( i; 0 .. height ) 393 data[i].length = nw; 394 } 395 return this; 396 } 397 } 398 399 /// 400 auto expandHeight(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const 401 if( (bW==W||W==0||bW==0) && is(typeof(E(X.init))) ) 402 { 403 static if( isDynamicWidth || mtr.isDynamicWidth ) 404 enforce( mtr.width == width, "wrong width" ); 405 406 auto ret = Matrix!(0,W,E)(this); 407 auto last_height = height; 408 ret.resize( ret.height + mtr.height, width ); 409 foreach( i; 0 .. mtr.height ) 410 foreach( j; 0 .. width ) 411 ret.data[last_height+i][j] = E(mtr.data[i][j]); 412 return ret; 413 } 414 415 /// 416 auto expandHeight(X...)( in X vals ) const 417 if( is(typeof(Matrix!(1,W,E)(vals))) ) 418 { return expandHeight( Matrix!(1,W,E)(vals) ); } 419 420 /// 421 auto expandWidth(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const 422 if( (bH==H||H==0||bH==0) && is(typeof(E(X.init))) ) 423 { 424 static if( isDynamicHeight || mtr.isDynamicHeight ) 425 enforce( mtr.height == height, "wrong height" ); 426 auto ret = Matrix!(H,0,E)(this); 427 auto last_width = width; 428 ret.resize( height, ret.width + mtr.width ); 429 foreach( i; 0 .. height ) 430 foreach( j; 0 .. mtr.width ) 431 ret.data[i][last_width+j] = E(mtr.data[i][j]); 432 return ret; 433 } 434 435 /// 436 auto expandWidth(X...)( in X vals ) const 437 if( is(typeof(Matrix!(H,1,E)(vals))) ) 438 { return expandWidth( Matrix!(H,1,E)(vals) ); } 439 440 /// 441 auto asArray() const @property 442 { 443 auto ret = new E[](width*height); 444 foreach( i; 0 .. height ) 445 foreach( j; 0 .. width ) 446 ret[i*width+j] = data[i][j]; 447 return ret; 448 } 449 450 /// 451 auto sliceHeight( size_t start, size_t count=0 ) const 452 { 453 enforce( start < height ); 454 count = count ? count : height - start; 455 auto ret = Matrix!(0,W,E)(this); 456 ret.resize( count, width ); 457 foreach( i; 0 .. count ) 458 ret.data[i][] = data[start+i][]; 459 return ret; 460 } 461 462 /// 463 auto sliceWidth( size_t start, size_t count=0 ) const 464 { 465 enforce( start < width ); 466 count = count ? count : width - start; 467 auto ret = Matrix!(H,0,E)(this); 468 ret.resize( height, count ); 469 foreach( i; 0 .. height ) 470 foreach( j; 0 .. count ) 471 ret.data[i][j] = data[i][start+j]; 472 return ret; 473 } 474 475 /// 476 auto opUnary(string op)() const 477 if( op == "-" && is( typeof( E.init * (-1) ) ) ) 478 { 479 auto ret = selftype(this); 480 foreach( ref row; ret.data ) 481 foreach( ref v; row ) 482 v = v * -1; 483 return ret; 484 } 485 486 private void checkCompatible(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const 487 { 488 static if( isDynamicHeight || mtr.isDynamicHeight ) 489 enforce( height == mtr.height, "wrong height" ); 490 static if( isDynamicWidth || mtr.isDynamicWidth ) 491 enforce( width == mtr.width, "wrong width" ); 492 } 493 494 /// 495 auto opBinary(string op, size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const 496 if( (op=="+"||op=="-") && allowSomeOp(H,bH) && allowSomeOp(W,bW) ) 497 { 498 checkCompatible( mtr ); 499 500 auto ret = selftype(this); 501 502 foreach( i; 0 .. height ) 503 foreach( j; 0 .. width ) 504 mixin( `ret[i][j] = E(data[i][j] ` ~ op ~ ` mtr[i][j]);` ); 505 return ret; 506 } 507 508 /// 509 auto opBinary(string op,X)( in X b ) const 510 if( (op!="+" && op!="-") && isValidOp!(op,E,X) ) 511 { 512 auto ret = selftype(this); 513 foreach( i; 0 .. height ) 514 foreach( j; 0 .. width ) 515 mixin( `ret[i][j] = E(data[i][j] ` ~ op ~ ` b);` ); 516 return ret; 517 } 518 519 /// 520 auto opBinary(string op,size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const 521 if( op=="*" && allowSomeOp(W,bH) && isValidOp!("*",E,X) ) 522 { 523 static if( isDynamic || mtr.isDynamic ) 524 enforce( width == mtr.height, "incompatible sizes for mul" ); 525 Matrix!(H,bW,E) ret; 526 static if( ret.isDynamic ) ret.resize(height,mtr.width); 527 528 foreach( i; 0 .. height ) 529 foreach( j; 0 .. mtr.width ) 530 { 531 ret[i][j] = E( data[i][0] * mtr.data[0][j] ); 532 foreach( k; 1 .. width ) 533 ret[i][j] = ret[i][j] + E( data[i][k] * mtr.data[k][j] ); 534 } 535 536 return ret; 537 } 538 539 /// 540 ref typeof(this) opOpAssign(string op, E)( in E b ) 541 if( mixin( `is( typeof( selftype.init ` ~ op ~ ` E.init ) == selftype )` ) ) 542 { mixin( `return this = this ` ~ op ~ ` b;` ); } 543 544 /// 545 bool opCast(E)() const if( is( E == bool ) ) 546 { 547 foreach( v; asArray ) if( !isFinite(v) ) return false; 548 return true; 549 } 550 551 /// transponate 552 auto T() const @property 553 { 554 Matrix!(W,H,E) ret; 555 static if( isDynamic ) ret.resize(width,height); 556 foreach( i; 0 .. height) 557 foreach( j; 0 .. width ) 558 ret[j][i] = data[i][j]; 559 return ret; 560 } 561 562 /// 563 ref typeof(this) setCol(X...)( size_t no, in X vals ) 564 if( is(typeof(flatData!E(vals))) ) 565 { 566 enforce( no < width, "bad col index" ); 567 auto buf = flatData!E(vals); 568 enforce( buf.length == height, "bad data length" ); 569 foreach( i; 0 .. height ) 570 data[i][no] = buf[i]; 571 return this; 572 } 573 574 /// 575 ref typeof(this) setRow(X...)( size_t no, in X vals ) 576 if( is(typeof(flatData!E(vals))) ) 577 { 578 enforce( no < height, "bad row index" ); 579 auto buf = flatData!E(vals); 580 enforce( buf.length == width, "bad data length" ); 581 data[no][] = buf[]; 582 return this; 583 } 584 585 /// 586 ref typeof(this) setRect(size_t bH, size_t bW, X)( size_t pos_row, size_t pos_col, in Matrix!(bH,bW,X) mtr ) 587 if( is(typeof(E(X.init))) ) 588 { 589 enforce( pos_row < height, "bad row index" ); 590 enforce( pos_col < width, "bad col index" ); 591 enforce( pos_row + mtr.height <= height, "bad height size" ); 592 enforce( pos_col + mtr.width <= width, "bad width size" ); 593 594 foreach( i; 0 .. mtr.height ) 595 foreach( j; 0 .. mtr.width ) 596 data[i+pos_row][j+pos_col] = E(mtr[i][j]); 597 598 return this; 599 } 600 601 /// 602 auto col( size_t no ) const 603 { 604 enforce( no < width, "bad col index" ); 605 Matrix!(H,1,E) ret; 606 static if( ret.isDynamic ) 607 ret.resize(height,1); 608 foreach( i; 0 .. height ) 609 ret[i][0] = data[i][no]; 610 return ret; 611 } 612 613 /// 614 auto row( size_t no ) const 615 { 616 enforce( no < height, "bad row index" ); 617 Matrix!(1,W,E) ret; 618 static if( ret.isDynamic ) 619 ret.resize(1,width); 620 ret[0][] = data[no][]; 621 return ret; 622 } 623 624 /// 625 auto opBinary(string op,size_t K,X,alias string AS)( in Vector!(K,X,AS) v ) const 626 if( op=="*" && allowSomeOp(W,K) && isValidOp!("*",E,X) && isValidOp!("+",E,E) ) 627 { 628 static if( isDynamic || v.isDynamic ) 629 enforce( width == v.length, "wrong vector length" ); 630 631 static if( isStatic && W == H ) 632 Vector!(H,E,AS) ret; 633 else 634 Vector!(H,E) ret; 635 636 static if( ret.isDynamic ) 637 ret.length = height; 638 639 foreach( i; 0 .. height ) 640 { 641 ret[i] = data[i][0] * v[0]; 642 643 foreach( j; 1 .. width ) 644 ret[i] = ret[i] + E(data[i][j] * v[j]); 645 } 646 647 return ret; 648 } 649 650 /// 651 auto opBinaryRight(string op,size_t K,X,alias string AS)( in Vector!(K,X,AS) v ) const 652 if( op=="*" && isVector!(typeof(selftype.init.T * typeof(v).init)) ) 653 { return this.T * v; } 654 655 static private size_t[] getIndexesWithout(size_t max, in size_t[] arr) 656 { 657 size_t[] ret; 658 foreach( i; 0 .. max ) if( !canFind(arr,i) ) ret ~= i; 659 return ret; 660 } 661 662 /// 663 auto subWithout( size_t[] without_rows=[], size_t[] without_cols=[] ) const 664 { 665 auto with_rows = getIndexesWithout(height,without_rows); 666 auto with_cols = getIndexesWithout(width,without_cols); 667 668 return sub( with_rows,with_cols ); 669 } 670 671 /// get sub matrix 672 auto sub( size_t[] with_rows, size_t[] with_cols ) const 673 { 674 auto wrows = array( uniq(with_rows) ); 675 auto wcols = array( uniq(with_cols) ); 676 677 enforce( all!(a=>a<height)( wrows ), "bad row index" ); 678 enforce( all!(a=>a<width)( wcols ), "bad col index" ); 679 680 Matrix!(0,0,E) ret; 681 ret.resize( wrows.length, wcols.length ); 682 683 foreach( i, orig_i; wrows ) 684 foreach( j, orig_j; wcols ) 685 ret[i][j] = data[orig_i][orig_j]; 686 687 return ret; 688 } 689 690 static if( isFloatingPoint!E && ( isDynamicAll || 691 ( isStaticHeightOnly && H > 1 ) || 692 ( isStaticWidthOnly && W > 1 ) || 693 ( isStatic && W == H ) ) ) 694 { 695 /// 696 E cofactor( size_t i, size_t j ) const 697 { return subWithout([i],[j]).det * coef(i,j); } 698 699 private static nothrow @trusted auto coef( size_t i, size_t j ) 700 { return ((i+j)%2?-1:1); } 701 702 /// 703 auto det() const @property 704 { 705 static if( isDynamic ) 706 enforce( width == height, "not square matrix" ); 707 708 static if( isDynamic ) 709 { 710 if( width == 1 ) 711 return data[0][0]; 712 else if( width == 2 ) 713 return data[0][0] * data[1][1] - 714 data[0][1] * data[1][0]; 715 else return classicDet; 716 } 717 else 718 { 719 static if( W == 1 ) 720 return data[0][0]; 721 else static if( W == 2 ) 722 return data[0][0] * data[1][1] - 723 data[0][1] * data[1][0]; 724 else return classicDet; 725 } 726 } 727 728 private @property classicDet() const 729 { 730 auto i = 0UL; // TODO: find max zeros line 731 auto ret = data[i][0] * cofactor(i,0); 732 733 foreach( j; 1 .. width ) 734 ret = ret + data[i][j] * cofactor(i,j); 735 736 return ret; 737 } 738 739 /// 740 auto inv() const @property 741 { 742 static if( isDynamic ) 743 enforce( width == height, "not square matrix" ); 744 else static assert( W==H, "not square matrix" ); 745 746 selftype buf; 747 748 static if( isDynamic ) 749 buf.resize(height,width); 750 751 foreach( i; 0 .. height ) 752 foreach( j; 0 .. width ) 753 buf[i][j] = cofactor(i,j); 754 755 auto i = 0UL; // TODO: find max zeros line 756 auto d = data[i][0] * buf[i][0]; 757 758 foreach( j; 1 .. width ) 759 d = d + data[i][j] * buf[i][j]; 760 761 return buf.T / d; 762 } 763 764 static if( (isStaticHeightOnly && H==4) || 765 (isStaticWidthOnly && W==4) || 766 (isStatic && H==W && H==4) || 767 isDynamicAll ) 768 { 769 /++ only for transform matrix +/ 770 @property auto speedTransformInv() const 771 { 772 static if( isDynamic ) 773 { 774 enforce( width == height, "not square matrix" ); 775 enforce( width == 4, "matrix must be 4x4" ); 776 } 777 778 selftype ret; 779 static if( isDynamic ) 780 ret.resize( height, width ); 781 782 foreach( i; 0 .. 3 ) 783 foreach( j; 0 .. 3 ) 784 ret[i][j] = this[j][i]; 785 786 auto a22k = 1.0 / this[3][3]; 787 788 ret[0][3] = -( ret[0][0] * this[0][3] + ret[0][1] * this[1][3] + ret[0][2] * this[2][3] ) * a22k; 789 ret[1][3] = -( ret[1][0] * this[0][3] + ret[1][1] * this[1][3] + ret[1][2] * this[2][3] ) * a22k; 790 ret[2][3] = -( ret[2][0] * this[0][3] + ret[2][1] * this[1][3] + ret[2][2] * this[2][3] ) * a22k; 791 792 ret[3][0] = -( this[3][0] * ret[0][0] + this[3][1] * ret[1][0] + this[3][2] * ret[2][0] ) * a22k; 793 ret[3][1] = -( this[3][0] * ret[0][1] + this[3][1] * ret[1][1] + this[3][2] * ret[2][1] ) * a22k; 794 ret[3][2] = -( this[3][0] * ret[0][2] + this[3][1] * ret[1][2] + this[3][2] * ret[2][2] ) * a22k; 795 796 ret[3][3] = a22k * ( 1.0 - ( this[3][0] * ret[0][3] + this[3][1] * ret[1][3] + this[3][2] * ret[2][3] ) ); 797 798 return ret; 799 } 800 } 801 802 /// 803 auto rowReduceInv() const @property 804 { 805 static if( isDynamic ) 806 enforce( width == height, "not square matrix" ); 807 808 auto ln = height; 809 810 auto orig = selftype(this); 811 selftype invt; 812 static if( isDynamic ) 813 { 814 invt.resize(ln,ln); 815 foreach( i; 0 .. ln ) 816 foreach( j; 0 .. ln ) 817 invt[i][j] = E(i==j); 818 } 819 820 foreach( r; 0 .. ln-1 ) 821 { 822 auto k = E(1) / orig[r][r]; 823 foreach( c; 0 .. ln ) 824 { 825 orig[r][c] *= k; 826 invt[r][c] *= k; 827 } 828 foreach( rr; r+1 .. ln ) 829 { 830 auto v = orig[rr][r]; 831 foreach( c; 0 .. ln ) 832 { 833 orig[rr][c] -= orig[r][c] * v; 834 invt[rr][c] -= invt[r][c] * v; 835 } 836 } 837 } 838 839 foreach_reverse( r; 1 .. ln ) 840 { 841 auto k = E(1) / orig[r][r]; 842 foreach( c; 0 .. ln ) 843 { 844 orig[r][c] *= k; 845 invt[r][c] *= k; 846 } 847 foreach_reverse( rr; 0 .. r ) 848 { 849 auto v = orig[rr][r]; 850 foreach( c; 0 .. ln ) 851 { 852 orig[rr][c] -= orig[r][c] * v; 853 invt[rr][c] -= invt[r][c] * v; 854 } 855 } 856 } 857 858 return invt; 859 } 860 } 861 } 862 863 alias Matrix2(T) = Matrix!(2,2,T); /// 864 alias Matrix2x3(T) = Matrix!(2,3,T); /// 865 alias Matrix2x4(T) = Matrix!(2,4,T); /// 866 alias Matrix2xD(T) = Matrix!(2,0,T); /// 867 alias Matrix3x2(T) = Matrix!(3,2,T); /// 868 alias Matrix3(T) = Matrix!(3,3,T); /// 869 alias Matrix3x4(T) = Matrix!(3,4,T); /// 870 alias Matrix3xD(T) = Matrix!(3,0,T); /// 871 alias Matrix4x2(T) = Matrix!(4,2,T); /// 872 alias Matrix4x3(T) = Matrix!(4,3,T); /// 873 alias Matrix4(T) = Matrix!(4,4,T); /// 874 alias Matrix4xD(T) = Matrix!(4,0,T); /// 875 alias MatrixDx2(T) = Matrix!(0,2,T); /// 876 alias MatrixDx3(T) = Matrix!(0,3,T); /// 877 alias MatrixDx4(T) = Matrix!(0,4,T); /// 878 alias MatrixDxD(T) = Matrix!(0,0,T); /// 879 880 alias Matrix!(2,2,float) mat2; /// 881 alias Matrix!(2,3,float) mat2x3; /// 882 alias Matrix!(2,4,float) mat2x4; /// 883 alias Matrix!(2,0,float) mat2xD; /// 884 alias Matrix!(3,2,float) mat3x2; /// 885 alias Matrix!(3,3,float) mat3; /// 886 alias Matrix!(3,4,float) mat3x4; /// 887 alias Matrix!(3,0,float) mat3xD; /// 888 alias Matrix!(4,2,float) mat4x2; /// 889 alias Matrix!(4,3,float) mat4x3; /// 890 alias Matrix!(4,4,float) mat4; /// 891 alias Matrix!(4,0,float) mat4xD; /// 892 alias Matrix!(0,2,float) matDx2; /// 893 alias Matrix!(0,3,float) matDx3; /// 894 alias Matrix!(0,4,float) matDx4; /// 895 alias Matrix!(0,0,float) matD; /// 896 897 alias Matrix!(2,2,double) dmat2; /// 898 alias Matrix!(2,3,double) dmat2x3; /// 899 alias Matrix!(2,4,double) dmat2x4; /// 900 alias Matrix!(2,0,double) dmat2xD; /// 901 alias Matrix!(3,2,double) dmat3x2; /// 902 alias Matrix!(3,3,double) dmat3; /// 903 alias Matrix!(3,4,double) dmat3x4; /// 904 alias Matrix!(3,0,double) dmat3xD; /// 905 alias Matrix!(4,2,double) dmat4x2; /// 906 alias Matrix!(4,3,double) dmat4x3; /// 907 alias Matrix!(4,4,double) dmat4; /// 908 alias Matrix!(4,0,double) dmat4xD; /// 909 alias Matrix!(0,2,double) dmatDx2; /// 910 alias Matrix!(0,3,double) dmatDx3; /// 911 alias Matrix!(0,4,double) dmatDx4; /// 912 alias Matrix!(0,0,double) dmatD; /// 913 914 alias Matrix!(2,2,real) rmat2; /// 915 alias Matrix!(2,3,real) rmat2x3; /// 916 alias Matrix!(2,4,real) rmat2x4; /// 917 alias Matrix!(2,0,real) rmat2xD; /// 918 alias Matrix!(3,2,real) rmat3x2; /// 919 alias Matrix!(3,3,real) rmat3; /// 920 alias Matrix!(3,4,real) rmat3x4; /// 921 alias Matrix!(3,0,real) rmat3xD; /// 922 alias Matrix!(4,2,real) rmat4x2; /// 923 alias Matrix!(4,3,real) rmat4x3; /// 924 alias Matrix!(4,4,real) rmat4; /// 925 alias Matrix!(4,0,real) rmat4xD; /// 926 alias Matrix!(0,2,real) rmatDx2; /// 927 alias Matrix!(0,3,real) rmatDx3; /// 928 alias Matrix!(0,4,real) rmatDx4; /// 929 alias Matrix!(0,0,real) rmatD; /// 930 931 unittest 932 { 933 static assert( Matrix!(3,3,float).sizeof == float.sizeof * 9 ); 934 static assert( Matrix!(3,3,float).isStatic ); 935 static assert( Matrix!(0,3,float).isDynamic ); 936 static assert( Matrix!(0,3,float).isDynamicHeight ); 937 static assert( Matrix!(0,3,float).isStaticWidth ); 938 static assert( Matrix!(3,0,float).isDynamic ); 939 static assert( Matrix!(3,0,float).isDynamicWidth ); 940 static assert( Matrix!(3,0,float).isStaticHeight ); 941 static assert( Matrix!(0,0,float).isDynamic ); 942 static assert( Matrix!(0,0,float).isDynamicHeight ); 943 static assert( Matrix!(0,0,float).isDynamicWidth ); 944 } 945 946 unittest 947 { 948 static assert( isStaticMatrix!(mat3) ); 949 static assert( !isStaticMatrix!(matD) ); 950 static assert( !isStaticMatrix!(int[]) ); 951 } 952 953 unittest 954 { 955 assert( eq( mat3.init, [[1,0,0],[0,1,0],[0,0,1]] ) ); 956 assert( eq( mat2.init, [[1,0],[0,1]] ) ); 957 assert( eq( mat2x3.init, [[0,0,0],[0,0,0]] ) ); 958 } 959 960 unittest 961 { 962 auto a = Matrix!(3,2,double)( 36, 0, 3, 3, 0, 0 ); 963 auto b = Matrix!(3,2,double)( [ 36, 0, 3, 3, 0, 0 ] ); 964 assert( eq( a.asArray, b.asArray ) ); 965 assert( eq( a.asArray, [ 36, 0, 3, 3, 0, 0 ] ) ); 966 } 967 968 unittest 969 { 970 auto a = mat3( 1,2,3,4,5,6,7,8,9 ); 971 assert( a.height == 3 ); 972 assert( a.width == 3 ); 973 assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) ); 974 static assert( !__traits(compiles,a.resize(1,1)) ); 975 a.fill(1); 976 assert( eq( a, [[1,1,1], [1,1,1], [1,1,1]] ) ); 977 a[0][0] *= 3; 978 assert( eq( a, [[3,1,1], [1,1,1], [1,1,1]] ) ); 979 a[1][0] += 3; 980 assert( eq( a, [[3,1,1], [4,1,1], [1,1,1]] ) ); 981 } 982 983 /// 984 unittest 985 { 986 static struct Test 987 { 988 union 989 { 990 mat3 um; 991 float[9] uf; 992 } 993 } 994 995 Test tt; 996 997 foreach( i, ref v; tt.uf ) v = i+1; 998 assert( eq( tt.um, [[1,2,3],[4,5,6],[7,8,9]] ) ); 999 } 1000 1001 /// 1002 unittest 1003 { 1004 auto a = matDx3( 1,2,3,4,5,6,7,8,9 ); 1005 assert( mustExcept( {matDx3(1,2,3,4);} ) ); 1006 assert( a.height == 3 ); 1007 assert( a.width == 3 ); 1008 assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) ); 1009 assert( mustExcept({ a.resize(2,2); }) ); 1010 a.resize(2,3); 1011 assert( eq( a, [[1,2,3],[4,5,6]] ) ); 1012 assert( a.height == 2 ); 1013 a.fill(1); 1014 assert( eq( a, [[1,1,1],[1,1,1]] ) ); 1015 a.resize(0,3); 1016 assert( a.width == 3 ); 1017 a.resize(2,3); 1018 a.fill(1); 1019 1020 auto b = a; 1021 assert( eq( b, [[1,1,1],[1,1,1]] ) ); 1022 } 1023 1024 unittest 1025 { 1026 auto a = mat3xD( 1,2,3,4,5,6,7,8,9 ); 1027 assert( mustExcept( {mat3xD(1,2,3,4);} ) ); 1028 assert( a.height == 3 ); 1029 assert( a.width == 3 ); 1030 assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) ); 1031 assert( mustExcept({ a.resize(2,2); }) ); 1032 a.resize(3,2); 1033 assert( eq( a, [[1,2],[4,5],[7,8]] ) ); 1034 assert( a.width == 2 ); 1035 a.fill(1); 1036 assert( eq( a, [[1,1],[1,1],[1,1]] ) ); 1037 1038 auto b = a; 1039 assert( eq( b, [[1,1],[1,1],[1,1]] ) ); 1040 } 1041 1042 unittest 1043 { 1044 auto a = matD( 3,3, 1,2,3,4,5,6,7,8,9 ); 1045 assert( mustExcept( {matD(1,2,3,4,5);} ) ); 1046 assert( a.height == 3 ); 1047 assert( a.width == 3 ); 1048 assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) ); 1049 a.resize(2,2); 1050 assert( eq( a, [[1,2],[4,5]] ) ); 1051 auto b = matD(2,2); 1052 b.fill(1); 1053 assert( eq( b, [[1,1],[1,1]] ) ); 1054 b = a; 1055 assert( eq( a, b ) ); 1056 auto c = matD( Matrix!(2,4,float)(1,2,3,4,5,6,7,8) ); 1057 assert( eq( c, [[1,2,3,4],[5,6,7,8]] ) ); 1058 assert( c.height == 2 ); 1059 assert( c.width == 4 ); 1060 assert( b.height == 2 ); 1061 assert( b.width == 2 ); 1062 b = c; 1063 assert( b.height == 2 ); 1064 assert( b.width == 4 ); 1065 assert( eq( b, c ) ); 1066 b[0][0] = 666; 1067 assert( !eq( b, c ) ); 1068 } 1069 1070 unittest 1071 { 1072 auto a = mat3xD( 1,2,3,4,5,6,7,8,9 ); 1073 matD b; 1074 assert( b.height == 0 ); 1075 assert( b.width == 0 ); 1076 b = a; 1077 assert( b.height == 3 ); 1078 assert( b.width == 3 ); 1079 assert( eq( a, b ) ); 1080 a[0][0] = 22; 1081 assert( !eq( a, b ) ); 1082 a = b; 1083 assert( eq( a, b ) ); 1084 b.height = 4; 1085 assert( mustExcept({ a = b; }) ); 1086 assert( !mustExcept({ b = a; }) ); 1087 } 1088 1089 /// 1090 unittest 1091 { 1092 auto a = mat3( 1,2,3,4,5,6,7,8,9 ); 1093 auto b = matD( 3,3, 1,2,3,4,5,6,7,8,9 ); 1094 auto c = mat3xD( 1,2,3,4,5,6,7,8,9 ); 1095 auto d = matDx3( 1,2,3,4,5,6,7,8,9 ); 1096 1097 auto eha = a.expandHeight( 8,8,8 ); 1098 auto ehb = b.expandHeight( 8,8,8 ); 1099 auto ehc = c.expandHeight( 8,8,8 ); 1100 auto ehd = d.expandHeight( 8,8,8 ); 1101 1102 assert( eq( eha, [[1,2,3],[4,5,6],[7,8,9],[8,8,8]] )); 1103 assert( eha.height == 4 ); 1104 assert( ehb.height == 4 ); 1105 assert( ehc.height == 4 ); 1106 assert( ehd.height == 4 ); 1107 assert( eq( eha, ehb ) ); 1108 assert( eq( eha, ehc ) ); 1109 assert( eq( eha, ehd ) ); 1110 1111 static assert( is(typeof(eha) == Matrix!(0,3,float)) ); 1112 static assert( is(typeof(ehd) == Matrix!(0,3,float)) ); 1113 1114 static assert( is(typeof(ehb) == Matrix!(0,0,float)) ); 1115 static assert( is(typeof(ehc) == Matrix!(0,0,float)) ); 1116 1117 auto ewa = a.expandWidth( 8,8,8 ); 1118 auto ewb = b.expandWidth( 8,8,8 ); 1119 auto ewc = c.expandWidth( 8,8,8 ); 1120 auto ewd = d.expandWidth( 8,8,8 ); 1121 1122 assert( eq( ewa, [[1,2,3,8],[4,5,6,8],[7,8,9,8]] )); 1123 assert( ewa.width == 4 ); 1124 assert( ewb.width == 4 ); 1125 assert( ewc.width == 4 ); 1126 assert( ewd.width == 4 ); 1127 assert( eq( ewa, ewb ) ); 1128 assert( eq( ewa, ewc ) ); 1129 assert( eq( ewa, ewd ) ); 1130 1131 static assert( is(typeof(ewa) == Matrix!(3,0,float)) ); 1132 static assert( is(typeof(ewc) == Matrix!(3,0,float)) ); 1133 1134 static assert( is(typeof(ewb) == Matrix!(0,0,float)) ); 1135 static assert( is(typeof(ewd) == Matrix!(0,0,float)) ); 1136 1137 auto aa = a.expandHeight(a); 1138 assert( eq( aa, [[1,2,3],[4,5,6],[7,8,9],[1,2,3],[4,5,6],[7,8,9]] )); 1139 assert( aa.height == 6 ); 1140 static assert( is(typeof(aa) == Matrix!(0,3,float)) ); 1141 } 1142 1143 unittest 1144 { 1145 auto a = mat3( 1,2,3,4,5,6,7,8,9 ); 1146 assert( a.asArray == [1.0f,2,3,4,5,6,7,8,9] ); 1147 } 1148 1149 /// 1150 unittest 1151 { 1152 auto a = matD(4,4,0).fillDiag(1); 1153 assert( eq( a, mat4() ) ); 1154 assert( eq( a.inv, a ) ); 1155 } 1156 1157 /// 1158 unittest 1159 { 1160 auto a = mat4x2( 1,2,3,4,5,6,7,8 ); 1161 auto b = mat4( 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 ); 1162 auto c = a.T * b * a; 1163 static assert( c.height == 2 && c.width == 2 ); 1164 } 1165 1166 unittest 1167 { 1168 matD a; 1169 matD b; 1170 a.resize( 10, 4 ); 1171 b.resize( 10, 10 ); 1172 auto c = a.T * b * a; 1173 assert( c.height == 4 && c.width == 4 ); 1174 } 1175 1176 /// 1177 unittest 1178 { 1179 auto a = mat3.diag(1); 1180 assert( eq(a,[[1,0,0],[0,1,0],[0,0,1]]) ); 1181 auto b = mat3xD.diag(1,2); 1182 assert( eq(b,[[1,0,0],[0,2,0],[0,0,1]]) ); 1183 auto c = mat3xD.diag(1,2,3); 1184 assert( eq(c,[[1,0,0],[0,2,0],[0,0,3]]) ); 1185 static assert( !__traits(compiles,matD.diag(1)) ); 1186 auto d = matD(3,3).fill(0).fillDiag(1); 1187 assert( eq(d,[[1,0,0],[0,1,0],[0,0,1]]) ); 1188 } 1189 1190 /// 1191 unittest 1192 { 1193 auto a = mat3( 1,2,3,4,5,6,7,8,9 ); 1194 auto sha = a.sliceHeight(1); 1195 assert( eq(sha,[[4,5,6],[7,8,9]]) ); 1196 auto swsha = sha.sliceWidth(0,1); 1197 assert( eq(swsha,[[4],[7]]) ); 1198 } 1199 1200 /// 1201 unittest 1202 { 1203 auto a = mat3.diag(1); 1204 assert( eq( -a,[[-1,0,0],[0,-1,0],[0,0,-1]]) ); 1205 } 1206 1207 unittest 1208 { 1209 auto a = mat3.diag(1); 1210 auto b = a*3-a; 1211 assert( eq( b,a*2 ) ); 1212 b /= 2; 1213 assert( eq( b,a ) ); 1214 } 1215 1216 /// 1217 unittest 1218 { 1219 auto a = rmat3( 3,2,2, 1,3,1, 5,3,4 ); 1220 auto ainv = rmat3xD( 9,-2,-4, 1,2,-1, -12,1,7 ) / 5; 1221 1222 auto b = a * ainv; 1223 static assert( is( typeof(b) == Matrix!(3,0,real) ) ); 1224 assert( eq( b, mat3.diag(1) ) ); 1225 1226 static assert( !__traits(compiles,a *= ainv ) ); 1227 a *= rmat3(ainv); 1228 assert( eq( a, mat3.diag(1) ) ); 1229 } 1230 1231 /// 1232 unittest 1233 { 1234 alias Matrix!(4,5,float) mat4x5; 1235 1236 auto a = mat2x4.init * mat4xD.init; 1237 assert( mustExcept({ mat4xD.init * mat2x4.init; }) ); 1238 auto b = mat2x4.init * mat4x5.init; 1239 1240 static assert( is( typeof(a) == Matrix!(2,0,float) ) ); 1241 static assert( is( typeof(b) == Matrix!(2,5,float) ) ); 1242 static assert( is( typeof(mat4xD.init * mat2x4.init) == Matrix!(4,4,float) ) ); 1243 } 1244 1245 unittest 1246 { 1247 1248 auto a = mat2x3( 1,2,3, 4,5,6 ); 1249 auto b = mat4xD( 1,2,3,4 ); 1250 1251 auto aT = a.T; 1252 auto bT = b.T; 1253 1254 static assert( is( typeof(aT) == Matrix!(3,2,float) ) ); 1255 static assert( is( typeof(bT) == Matrix!(0,4,float) ) ); 1256 1257 assert( bT.height == b.width ); 1258 } 1259 1260 unittest 1261 { 1262 auto a = mat3.diag(1); 1263 1264 a.setRow(2,4,5,6); 1265 assert( eq( a, [[1,0,0],[0,1,0],[4,5,6]] ) ); 1266 a.setCol(1,8,4,2); 1267 assert( eq( a, [[1,8,0],[0,4,0],[4,2,6]] ) ); 1268 1269 assert( eq( a.row(0), [[1,8,0]] ) ); 1270 assert( eq( a.col(0), [[1],[0],[4]] ) ); 1271 } 1272 1273 /// 1274 unittest 1275 { 1276 auto b = mat3.diag(2) * vec3(2,3,4); 1277 assert( is( typeof(b) == vec3 ) ); 1278 assert( eq( b, [4,6,8] ) ); 1279 1280 auto c = vec3(1,1,1) * mat3.diag(1,2,3); 1281 assert( is( typeof(c) == vec3 ) ); 1282 assert( eq( c, [1,2,3] ) ); 1283 } 1284 1285 /// 1286 unittest 1287 { 1288 auto mtr = matD(2,3).fill( 1,2,3, 1289 3,4,7 ); 1290 1291 auto a = mtr * vec3(1,1,1); 1292 1293 assert( is( typeof(a) == Vector!(0,float) ) ); 1294 assert( a.length == 2 ); 1295 assert( eq( a, [ 6, 14 ] ) ); 1296 1297 auto b = vec3(1,1,1) * mtr.T; 1298 assert( eq( a, b ) ); 1299 } 1300 1301 /// 1302 unittest 1303 { 1304 void check(E)( E mtr ) if( isMatrix!E ) 1305 { 1306 mtr.fill( 1,2,3,4, 1307 5,6,7,8, 1308 9,10,11,12, 1309 13,14,15,16 ); 1310 auto sm = mtr.subWithout( [0,3,3,3], [1,2] ); 1311 assert( is( typeof(sm) == matD ) ); 1312 assert( sm.width == 2 ); 1313 assert( sm.height == 2 ); 1314 assert( eq( sm, [ [5,8], [9,12] ] ) ); 1315 assert( mustExcept({ mtr.sub( [0,4], [1,2] ); }) ); 1316 auto sm2 = mtr.subWithout( [], [1,2] ); 1317 assert( sm2.width == 2 ); 1318 assert( sm2.height == 4 ); 1319 assert( eq( sm2, [ [1,4],[5,8],[9,12],[13,16] ] ) ); 1320 } 1321 1322 check( matD(4,4) ); 1323 check( mat4() ); 1324 } 1325 1326 unittest 1327 { 1328 assert( eq( matD(4,4,0).fillDiag(1,2,3,4).det, 24 ) ); 1329 } 1330 1331 unittest 1332 { 1333 auto mtr = rmatD(4,4).fill( 1,2,5,2, 1334 5,6,1,4, 1335 9,1,3,0, 1336 9,2,4,2 ); 1337 auto xx = mtr * mtr.inv; 1338 assert( eq( xx, matD(4,4,0).fillDiag(1) ) ); 1339 } 1340 1341 unittest 1342 { 1343 auto mtr = matD(4,4).fill( 0,1,0,2, 1344 1,0,0,4, 1345 0,0,1,1, 1346 0,0,0,1 ); 1347 auto vv = vec4(4,2,1,1); 1348 auto rv = mtr.speedTransformInv * (mtr*vv); 1349 assert( eq( rv, vv ) ); 1350 } 1351 1352 unittest 1353 { 1354 auto mtr = matD(4,4).fillDiag(1); 1355 auto vv = vec4(4,2,1,1); 1356 auto rv = vv * mtr; 1357 auto vr = mtr.T * vv * mtr; 1358 } 1359 1360 unittest 1361 { 1362 auto mtr = rmat4.diag(1); 1363 auto vv = vec4(4,2,1,1); 1364 auto rv = vv * mtr; 1365 auto vr = mtr.T * vv * mtr; 1366 1367 auto xx = mtr * mtr.inv; 1368 assert( eq( xx, mat4.diag(1) ) ); 1369 } 1370 1371 unittest 1372 { 1373 auto mtr = rmat4( 1,2,5,2, 1374 5,6,1,4, 1375 9,1,3,0, 1376 9,2,4,2 ); 1377 1378 auto xx = mtr * mtr.rowReduceInv; 1379 assert( eq( xx, mat4() ) ); 1380 } 1381 1382 /// 1383 unittest 1384 { 1385 auto mtr = mat4().setRect(0,0,mat3.diag(1)).setCol(3,1,2,3,4); 1386 assert( eq( mtr, [[1,0,0,1], 1387 [0,1,0,2], 1388 [0,0,1,3], 1389 [0,0,0,4]] ) ); 1390 } 1391 1392 unittest 1393 { 1394 auto stm = mat4(); 1395 assert( stm ); 1396 auto dnm = matD(); 1397 assert( dnm ); 1398 } 1399 1400 /// 1401 auto quatToMatrix(E)( in Quaterni!E iq ) 1402 { 1403 auto q = iq / iq.len2; 1404 1405 E wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 1406 1407 x2 = q.i + q.i; 1408 y2 = q.j + q.j; 1409 z2 = q.k + q.k; 1410 xx = q.i * x2; xy = q.i * y2; xz = q.i * z2; 1411 yy = q.j * y2; yz = q.j * z2; zz = q.k * z2; 1412 wx = q.a * x2; wy = q.a * y2; wz = q.a * z2; 1413 1414 return Matrix!(3,3,E)( 1.0-(yy+zz), xy-wz, xz+wy, 1415 xy+wz, 1.0-(xx+zz), yz-wx, 1416 xz-wy, yz+wx, 1.0-(xx+yy) ); 1417 } 1418 1419 /// 1420 unittest 1421 { 1422 auto q = rquat.fromAngle( PI_2, vec3(0,0,1) ); 1423 1424 auto m = quatToMatrix(q); 1425 auto r = mat3(0,-1, 0, 1, 0, 0, 0, 0, 1); 1426 assert( eq_approx( m, r, 1e-7 ) ); 1427 } 1428 1429 /// 1430 auto quatAndPosToMatrix(E,V)( in Quaterni!E iq, in V pos ) 1431 if( isCompatibleVector!(3,E,V) ) 1432 { 1433 auto q = iq / iq.len2; 1434 1435 E wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 1436 1437 x2 = q.i + q.i; 1438 y2 = q.j + q.j; 1439 z2 = q.k + q.k; 1440 xx = q.i * x2; xy = q.i * y2; xz = q.i * z2; 1441 yy = q.j * y2; yz = q.j * z2; zz = q.k * z2; 1442 wx = q.a * x2; wy = q.a * y2; wz = q.a * z2; 1443 1444 return Matrix!(4,4,E)( 1.0-(yy+zz), xy-wz, xz+wy, pos.x, 1445 xy+wz, 1.0-(xx+zz), yz-wx, pos.y, 1446 xz-wy, yz+wx, 1.0-(xx+yy), pos.z, 1447 0, 0, 0, 1 ); 1448 } 1449 1450 /// 1451 unittest 1452 { 1453 auto q = rquat.fromAngle( PI_2, vec3(0,0,1) ); 1454 auto m = quatAndPosToMatrix(q, vec3(1,2,3) ); 1455 assert( eq_approx( m, [[0,-1,0,1], 1456 [1, 0,0,2], 1457 [0, 0,1,3], 1458 [0, 0,0,1]], 1e-7 ) ); 1459 1460 }