1 /// Flexible templated matrix with some math utils
2 module sily.matrix;
3 
4 import std.math;
5 import std.numeric;
6 import std.conv;
7 import std.traits;
8 import std.typetuple;
9 import std.algorithm;
10 import std.stdio;
11 import std.string;
12 import std.format;
13 
14 import sily.math;
15 import sily.array;
16 import sily.vector;
17 import sily.quat;
18 
19 /// GLSL style aliases
20 alias mat(T, size_t H, size_t W) = Matrix!(T, H, W);
21 /// Ditto
22 alias mat(size_t H, size_t W) = Matrix!(float, H, W);
23 /// Ditto
24 alias mat4 = mat!(4, 4);
25 /// Ditto
26 alias mat3 = mat!(3, 3);
27 /// Ditto
28 alias mat2 = mat!(2, 2);
29 
30 /// Ditto
31 alias imat(size_t H, size_t W) = Matrix!(int, H, W);
32 /// Ditto
33 alias imat4 = imat!(4, 4);
34 /// Ditto
35 alias imat3 = imat!(3, 3);
36 /// Ditto
37 alias imat2 = imat!(2, 2);
38 
39 // mat2x3 // mat[row][col] // mat[1][2] = b
40 // data def = data[row][col] aka T[col][row]
41 // [ [a], [b], [c] ] 
42 // [ [d], [e], [f] ] 
43 /++
44 Matrix implementation, row-major right handed
45 Basic operations:
46 ---
47 // Scalar operations, any of *, /, +, -, % with number
48 m1 + 1;
49 m1 * 2;
50 // etc...
51 // Matrix/Matrix scalar op (on each element)
52 m1 + m2;
53 m1 - m2;
54 m1.scalarMultiply(m2);
55 m1.scalarDivide(m2);
56 // Unary operations
57 -m1; // invert all numbers
58 ~m1; // invert matrix, aka m1^-1, where ~m1*m1=m1.identity
59 // Binary operations
60 m1 * m2; // matrix multiplication
61 vec1x2 * m2x1; // vector by matrix
62 m3x4 * vec4x1; // matrix by vector
63 // Casting
64 cast(vec1x3) mat1x3; // cast to column vector
65 cast(vec3x1) mat1x3; // or row vector
66 cast(mat1x3) vec3x1; // or event vector to mat
67 cast(imat2) fmat2; // and type changing casts
68 cast(imat2) imat4; // changing size (keeps top left)
69 cast(imat4) imat2; // fills new cells with zeros
70 // Data access:
71 m1[1][2]; // access to row 1 column 2
72 m1.data[1][2]; // or explicit access to data
73 ---
74 +/
75 struct Matrix(T, size_t H, size_t W) if (isNumeric!T && W > 0 && H > 0) {
76     /// Matrix data
77     T[W][H] data = 0;
78 
79     /// Alias to allow data access
80     alias data this;
81     /// Alias to data type
82     alias dataType = T;
83 
84     /// Is matrix square (W == H)
85     enum bool isSquare = W == H;
86     
87     /++
88     Alias to matrix type. Can be used to construct matrices
89     of same type
90     ---
91     auto rmat7x2 = Matrix!(real, 7, 2);
92     auto rmat7x2_2 = rmat7x2.MatType;
93     +/
94     alias MatType = Matrix!(T, H, W);
95     /// Matrix size (h*w)
96     enum size_t size = H * W;
97     /// Matrix size (h)
98     enum size_t rows = H;
99     /// Matrix size (w)
100     enum size_t columns = W;
101 
102     /++
103     Constructs Matrix from components. If no components present
104     matrix will be filled with 0
105     Example:
106     ---
107     // Matrix can be constructed manually or with aliases
108     // Fills int matrix 2x2 with 1
109     auto m0 = Matrix!(int, 2, 2)(1);
110     // Fills int matrix 2x2 with [[1, 2], [3, 4]]
111     auto m1 = Matrix!(int, 2, 2)(1, 2, 3, 4);
112     // Same
113     auto m2 = Matrix!(int, 2, 2)([[1, 2], [3, 4]]);
114     // Same
115     mat2 m3 = [1, 2, 3, 4];
116     // Same
117     mat2 m4 = [[1, 2], [3, 4]];
118     // Also you can construct matrix by 
119     // assigning values directly
120     mat!(2, 3) m5 = [[1, 2, 3], [4, 5, 6]];
121     +/
122     this(in T val) {
123         foreach (i; 0..H) {
124             foreach (j; 0..W) {
125                 data[i][j] = val; 
126             }
127         }
128     }
129     /// Ditto
130     this(in T[size] vals...) {
131         foreach (i; 0..H) {
132             foreach (j; 0..W) {
133                 data[i][j] = vals[i * W + j];
134             }    
135         }
136     }
137     /// Ditto
138     this(in T[W][H] vals...) {
139         data = vals;
140     }
141 
142     static if (isSquare && W == 4 && isFloatingPoint!T) {
143         /// Construct matrix from quaternion
144         this(in quat q) {
145             T x2 = 2.0 * q.x * q.x;
146             T y2 = 2.0 * q.y * q.y;
147             T z2 = 2.0 * q.z * q.z;
148             T xy = 2.0 * q.x * q.y;
149             T xz = 2.0 * q.x * q.z;
150             T xw = 2.0 * q.x * q.w;
151             T yz = 2.0 * q.y * q.z;
152             T yw = 2.0 * q.y * q.w;
153             T zw = 2.0 * q.z * q.w;
154 
155             data = [
156                 [1.0 - y2 - z2, xy - zw, xz + yw, 0],
157                 [xy + zw, 1.0 - x2 - z2, yz - xw, 0],
158                 [xz - yw, yz + xw, 1.0 - x2 - y2, 0],
159                 [0.0, 0.0, 0.0, 1.0]
160             ];
161         }
162     }
163     
164 
165     /* -------------------------------------------------------------------------- */
166     /*                         UNARY OPERATIONS OVERRIDES                         */
167     /* -------------------------------------------------------------------------- */
168 
169     /// Scalar matrix addition and subtraction
170     MatType opBinary(string op, R)(in Matrix!(R, H, W) b) const if ( op == "+" || op == "-" ) {
171         MatType ret = MatType();
172         foreach (i; 0 .. H) {
173             foreach (j; 0..W) {
174                 mixin( "ret[i][j] = data[i][j] " ~ op ~ " b.data[i][j];" );
175             }
176         }
177         return ret;
178     }
179 
180     /// Ditto
181     MatType opBinaryRight(string op, R)(in Matrix!(R, H, W) b) const if ( op == "+" || op == "-" ) {
182         MatType ret = MatType();
183         foreach (i; 0 .. H) {
184             foreach (j; 0..W) {
185                 mixin( "ret[i][j] = b.data[i][j] " ~ op ~ " data[i][j];" );
186             }
187         }
188         return ret;
189     }
190 
191     /// Matrix multiplication
192     Matrix!(T, H, U) opBinary(string op, R, size_t U)(in Matrix!(R, W, U) b) const if ( op == "*" ) {
193         Matrix!(T, H, U) ret = Matrix!(T, H, U)(0);
194         foreach (i; 0 .. H) {
195             foreach (j; 0..U) {
196                 foreach (k; 0.. W) {
197                     ret[i][j] += data[i][k] * b.data[k][j];
198                     if (abs(ret[i][j]) <= float.epsilon * 2.0f) ret[i][j] = 0;
199                 }
200             }
201         }
202         return ret;
203     }
204 
205     /// Ditto
206     Matrix!(T, U, W) opBinaryRight(string op, R, size_t U)(in Matrix!(R, U, H) b) const if ( op == "*" ) {
207         Matrix!(T, U, W) ret = Matrix!(T, U, W)(0);
208         foreach (i; 0..U) {
209             foreach (j; 0..W) {
210                 foreach (k; 0..H) {
211                     ret[i][j] += b.data[i][k] * data[k][j];
212                     if (abs(ret[i][j]) <= float.epsilon * 2.0f) ret[i][j] = 0;
213                 }
214             }
215         }
216         return ret;
217     }
218 
219     /// Vector transformation
220     Vector!(R, H) opBinary(string op, R)(in Vector!(R, W) b) const if ( op == "*" ) {
221         Vector!(R, H) ret = 0;
222         foreach (i; 0 .. H) {
223             foreach (k; 0.. W) {
224                 ret[i] += data[i][k] * b.data[k];
225                 if (abs(ret[i]) <= float.epsilon * 2.0f) ret[i] = 0;
226             }
227         }
228         return ret;
229     }
230 
231     /// Ditto
232     Vector!(R, W) opBinaryRight(string op, R)(in Vector!(R, H) b) const if ( op == "*" ) {
233         Vector!(R, W) ret = 0;
234         foreach (j; 0..W) {
235             foreach (k; 0..H) {
236                 ret[j] += b.data[k] * data[k][j];
237                 if (abs(ret[j]) <= float.epsilon * 2.0f) ret[j] = 0;
238             }
239         }
240         return ret;
241     }
242 
243     /// Vector3 transformation
244     Vector!(R, 3) opBinary(string op, R)(in Vector!(R, 3) b) const if ( op == "*" && W == 4 ) {
245         Vector!(R, 3) ret = 0;
246         foreach (i; 0 .. 3) {
247             foreach (k; 0.. W) {
248                 if (k == 3) {
249                     ret[i] += data[i][k];
250                 } else {
251                     ret[i] += data[i][k] * b.data[k];
252                 }
253                 if (abs(ret[i]) <= float.epsilon * 2.0f) ret[i] = 0;
254             }
255         }
256         return ret;
257     }
258 
259     /// Ditto
260     Vector!(R, 3) opBinaryRight(string op, R)(in Vector!(R, 3) b) const if ( op == "*" && H == 4 ) {
261         Vector!(R, 3) ret = 0;
262         foreach (j; 0..3) {
263             foreach (k; 0..H) {
264                 if (k == 3) {
265                     ret[j] += data[k][j];
266                 } else {
267                     ret[j] += b.data[k] * data[k][j];
268                 }
269                 if (abs(ret[j]) <= float.epsilon * 2.0f) ret[j] = 0;
270             }
271         }
272         return ret;
273     }
274 
275     /// Quaternion transformation
276     quat opBinary(string op)(in quat b) const if ( op == "*" && W == 4) {
277         quat ret = 0;
278         foreach (i; 0 .. H) {
279             foreach (k; 0.. W) {
280                 ret[i] += data[i][k] * b.data[k];
281                 if (abs(ret[i]) <= float.epsilon * 2.0f) ret[i] = 0;
282             }
283         }
284         return ret;
285     }
286 
287     /// Ditto 
288     quat opBinaryRight(string op)(in quat b) const if ( op == "*" && H == 4) {
289         quat ret = 0;
290         foreach (j; 0..W) {
291             foreach (k; 0..H) {
292                 ret[j] += b.data[k] * data[k][j];
293                 if (abs(ret[j]) <= float.epsilon * 2.0f) ret[j] = 0;
294             }
295         }
296         return ret;
297     }
298 
299     /// Scalar number operations
300     MatType opBinary(string op, R)(in R b) const if ( isNumeric!R ) {
301         MatType ret = MatType();
302         foreach (i; 0 .. H) {
303             foreach (j; 0..W) {
304                 mixin( "ret[i][j] = data[i][j] " ~ op ~ " b;" );
305             }
306         }
307         return ret;
308     }
309 
310     /// Ditto
311     MatType opBinaryRight(string op, R)(in R b) const if ( isNumeric!R ) {
312         MatType ret = MatType();
313         foreach (i; 0 .. H) {
314             foreach (j; 0..W) {
315                 mixin( "ret[i][j] = b " ~ op ~ " data[i][j];" );
316             }
317         }
318         return ret;
319     }
320 
321     /// opEquals x == y
322     bool opEquals(R)(in Matrix!(R, H, W) b) const {
323         bool eq = true;
324         foreach (i; 0 .. H) {
325             foreach (j; 0..W) {
326                 eq = eq && data[i][j] == b.data[i][j];
327                 if (!eq) break;
328             }
329         }
330         return eq;
331     }
332 
333     /// Ditto
334     bool opEquals(R, size_t V, size_t U)(in Matrix!(R, V, U) b) const if (V != H || U != W) {
335         return false;
336     }
337   
338     /// Ditto
339     bool opEquals(R)(in R[][] b) const if ( isNumeric!R ){
340         bool eq = true;
341         foreach (i; 0 .. H) {
342             foreach (j; 0..W) {
343                 eq = eq && data[i][j] == b[i][j];
344                 if (!eq) break;
345             }
346         }
347         return eq;
348     }
349 
350     /// Ditto 
351     bool opEquals(R)(in R[] b) const if ( isNumeric!R ){
352         bool eq = true;
353         foreach (i; 0 .. H) {
354             foreach (j; 0..W) {
355                 eq = eq && data[i][j] == b[i * W + j];
356                 if (!eq) break;
357             }
358         }
359         return eq;
360     }
361 
362     // Cannot compare matrices
363     // opCmp x [< > <= >=] y
364 
365     /// opUnary [-, +, --, ++, *, ~] x 
366     MatType opUnary(string op)() if(op == "-" || op == "+"){
367         MatType ret = MatType();
368         foreach (i; 0 .. H) {
369             foreach (j; 0..W) {
370                 mixin( "ret[i][j] = " ~ op ~ " data[i][j];" );
371             }
372         }
373         return ret;
374     }
375 
376     /// Invert matrix
377     Matrix!(float, W, W) opUnary(string op)() if(op == "~" && isSquare) {
378         T det = determinant;
379         if (det == 0) throw new Error("Cannot invert single matrix.");
380         Matrix!(float, W, W) inv = 0;
381         T[W][W] adj = adjoint();
382         foreach (i; 0..W) {
383             foreach (j; 0..H) {
384                 inv[i][j] = adj[i][j] / cast(float) det;
385                 if (abs(inv[i][j]) <= float.epsilon * 2.0f) inv[i][j] = 0;
386             }
387         }
388         return inv;
389     }
390     
391     /// Scalar addition and subtraction in place 
392     MatType opOpAssign(string op, R)( in Matrix!(R, H, W) b ) if ( op == "+" || op == "-" ) { 
393         foreach (i; 0 .. H) {
394             foreach (j; 0..W) {
395                 mixin( "data[i] " ~ op ~ "= b.data[i][j];" );
396             }
397         }
398         return this;
399     }
400     
401     // no OpAssign for mat * mat coz this size is constant
402 
403     /// Ditto
404     MatType opOpAssign(string op, R)( in R b ) if ( isNumeric!R ) { 
405         foreach (i; 0 .. H) {
406             foreach (j; 0..W) {
407                 mixin( "data[i] " ~ op ~ "= b" );
408             }
409         }
410         return this;
411     }
412 
413     /// Matrix type conversion
414     R opCast(R)() const if (isMatrix!(R, H, W)){
415         R ret;
416         foreach (i; 0 .. H) {
417             foreach (j; 0 ..W) {
418                 ret[i][j] = cast(R.dataType) data[i][j];
419             }
420         }
421         return ret;
422     }
423     
424     /// Matrix resizing
425     R opCast(R)() const if (isMatrix!(R) && (R.rows != H || R.columns != W)) {
426         size_t V = R.rows;
427         size_t U = R.columns;
428         R ret = 0;
429         size_t _h = V < H ? V : H;
430         size_t _w = U < W ? U : W;
431         foreach (i; 0.._h) {
432             foreach (j; 0.._w) {
433                 ret[i][j] = data[i][j];
434             }
435         }
436         return ret;
437     }
438     
439     /// Matrix to vector cast (column)
440     R opCast(R)() const if (isVector!(R, H) && W == 1) {
441         R ret;
442         foreach (i; 0..H) {
443             ret[i] = cast(ret.dataType) data[i][0];
444         }
445         return ret;
446     }
447     
448     /// Matrix to vector cast (row)
449     R opCast(R)() const if (isVector!(R, W) && H == 1) {
450         R ret;
451         foreach (i; 0..W) {
452             ret[i] = cast(ret.dataType) data[0][i];
453         }
454         return ret;
455     }
456 
457     /// Boolean cast
458     bool opCast(T)() const if (is(T == bool)) {
459         foreach (i; 0 .. H) {
460             foreach (j; 0..W) {
461                 if (data[i][j] != 0) return true;
462             }
463         }
464         return false;
465     }
466 
467     /// Returns hash 
468     size_t toHash() const @safe nothrow {
469         return typeid(data).getHash(&data);
470     }
471     
472     /// Returns copy of matrix
473     public MatType copyof() {
474         return MatType(data);
475     }
476 
477     /// Returns string representation of matrix: `[1.00, 1.00,... , 1.00]`
478     public string toString() const {
479         import std.conv : to;
480         string s;
481         s ~= "[";
482         foreach (i; 0..H) {
483             foreach (j; 0..W) {
484                 s ~= isFloatingPoint!T ? format("%.2f", data[i][j]) : format("%d", data[i][j]);
485                 if (!(i == H - 1 && j == W - 1)) s ~= ", ";
486             }
487         }
488         s ~= "]";
489         return s;
490     }
491     
492     /// Returns string representation of matrix: `1.00, 1.00,... |\n|1.00, ... , 1.00|`
493     public string toStringPretty() const {
494         import std.conv : to;
495         size_t biggest = 1;
496         foreach (i; 0..H) {
497             foreach (j; 0..W) {
498                 string s = isFloatingPoint!T ? format("%.2f", data[i][j]) : format("%d", data[i][j]);
499                 if (s.length > biggest) biggest = s.length;
500             }
501         }
502 
503         string s;
504         foreach (i; 0..H) {
505             s ~= "|";
506             foreach (j; 0..W) {
507                 string _s = isFloatingPoint!T ? format("%.2f", data[i][j]) : format("%d", data[i][j]);
508                 s ~= _s ~ format("%-*s", (biggest + 1) - _s.length, " ");
509             }
510             if (i != H - 1) s ~= "|\n";
511         }
512         s ~= "|";
513         return s;
514     }
515 
516 
517     /// Returns pointer to data
518     public T[W]* ptr() return {
519         return data.ptr;
520     }
521 
522     static if (isSquare) {
523         /// Returns determinant of matrix
524         public T determinant() const {
525             return getDeterminant!(W)(data.to!(T[][]));
526         }
527 
528         private T getDeterminant(size_t S)(T[][] _mat) const if (S > 0) {
529             static if (S == 1) { 
530                 return _mat[0][0];
531             } else {
532                 T d = 0;
533                 T[][] temp = new T[][](S, S);
534                 T sign = 1;
535                 foreach (f; 0..S) {
536                     getCofactor!S(_mat, temp, 0, f);
537                     d += sign * _mat[0][f] * getDeterminant!(S - 1)(temp);
538                     sign = -sign;
539                 }
540                 return d;
541             }
542         }
543 
544         private void getCofactor(size_t S)(T[][] _mat, ref T[][] _temp, size_t y, size_t x) const {
545             int i = 0;
546             int j = 0;
547             foreach (r; 0..S) {
548                 foreach (c; 0..S) {
549                     if (r != y && c != x) {
550                         _temp[i][j++] = _mat[r][c];
551                         if (j == S - 1) {
552                             j = 0;
553                             ++i;
554                         }
555                     }
556                 }
557             }
558         }
559         
560         /// Returns ajdoint matrix
561         public MatType adjoint() const {
562             static if (W == 1) {
563                 return data[0][0];
564             } else {
565                 MatType ret = 0;
566                 int sign = 1;
567                 T[][] temp = new T[][](W, W);
568                 T[][] tdata = data.to!(T[][]);
569                 foreach (i; 0..W) {
570                     foreach (j; 0..W) {
571                         getCofactor!W(tdata, temp, i, j);
572                         sign = ((i + j) % 2 == 0) ? 1 : -1;
573                         ret[j][i] = sign * getDeterminant!(W - 1)(temp);
574                     }
575                 }
576                 return ret;
577             }
578         }
579         
580         /// Returns identity matrix for size
581         public static MatType identity() {
582             MatType ret = 0;
583             foreach (i; 0..W) {
584                 ret[i][i] = 1;
585             }
586             return ret;
587         }
588     
589         static if (isFloatingPoint!T) {
590             /// Inverts current matrix, alias to `data = ~matrix`
591             public MatType invert() {
592                 MatType m = copyof;
593                 data = (~m).data;
594                 return this;
595             }
596             /// Ditto
597             alias inverse = invert;
598         }
599         
600         /// Returns inverted, alias to `return ~matrix`
601         public mat!(H, W) inverted() {
602             MatType m = copyof;
603             return (~m);
604         }
605 
606         /// Ditto
607         alias inversed = inverted;
608 
609     }
610     
611     /// Multiplies each element by each element of matrices
612     public MatType scalarMultiply(R)(in Matrix!(R, W, H) b) {
613         MatType ret = 0;
614         foreach (i; 0 .. H) {
615             foreach (j; 0..W) {
616                 ret[i][j] = data[i][j] * b.data[i][j];
617             }
618         }
619         return ret;
620     }
621 
622     /// Divides each element by each element of matrices
623     public MatType scalarDivide(R)(in Matrix!(R, W, H) b) {
624         MatType ret = 0;
625         foreach (i; 0 .. H) {
626             foreach (j; 0..W) {
627                 ret[i][j] = data[i][j] / b.data[i][j];
628             }
629         }
630         return ret;
631     }
632     
633     /// Transpose of matrix (swaps rows and columns)
634     public Matrix!(T, W, H) transpose() {
635         Matrix!(T, W, H) ret = 0;
636         foreach (i; 0 .. H) {
637             foreach (j; 0..W) {
638                 ret[j][i] = data[i][j];
639             }
640         }
641         return ret;
642     }
643     
644     /// Resize matrix (keep left top). Alias to cast(Matrix!(T, V, U)) Matrix!(T, H, W) 
645     public Matrix!(T, V, U) resize(size_t V, size_t U)() if (V > 0 && U > 0) {
646         return cast(Matrix!(T, V, U)) this;
647     }
648 
649     static if (isSquare && W == 2 && isFloatingPoint!T) {
650     } else
651     static if (isSquare && W == 3 && isFloatingPoint!T) {
652         /// Constructs 2d rotation matrix
653         static MatType rotation(T angle) {
654             return MatType(cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0, 0, 0, 1).fixNegativeZero;
655         }
656 
657         /// Constructs 2d scale matrix
658         static MatType scale(T[2] s, ...) {
659             return MatType(s[0], 0, 0, 0, s[1], 0, 0, 0, 1);
660         }
661         
662         /// Constructs 2d shear matrix
663         static MatType shear(T[2] s, ...) {
664             return MatType(1, s[0], 0, s[1], 1, 0, 0, 0, 1);
665         }
666 
667         /// Constructs 2d translation matrix
668         static MatType translation(T[2] s, ...) {
669             return MatType(1, 0, s[0], 0, 1, s[1], 0, 0, 1);
670         }
671     } else
672     static if (isSquare && W == 4 && isFloatingPoint!T) {
673         /// Constructs 3d rotation matrix from axis and angle
674         static MatType rotation(T[3] axis, T angle) {
675             if (!vec3(axis).isNormalized) axis = vec3(axis).normalize();
676             T x = axis[0];
677             T y = axis[1];
678             T z = axis[2];
679             
680             float sina = sin(angle);
681             float cosa = cos(angle);
682             float t = 1.0f - cosa;
683 
684             MatType ret = MatType(
685                 x * x * t + cosa,     x * y * t - z * sina, x * z * t + y * sina, 0,
686                 y * x * t + z * sina, y * y * t + cosa,     y * z * t - x * sina, 0,
687                 z * x * t - y * sina, z * y * t + x * sina, z * z * t + cosa, 0,
688                 0, 0, 0, 1
689             ).fixNegativeZero;
690 
691             return ret;
692         }
693         
694         /// Constructs 3d rotation matrix from angle on X axis
695         static MatType rotationX(T angle) {
696             return MatType(
697                 1, 0, 0, 0,
698                 0, cos(angle), -sin(angle), 0,
699                 0, sin(angle), cos(angle), 0,
700                 0, 0, 0, 1
701             ).fixNegativeZero;
702         }
703 
704         /// Constructs 3d rotation matrix from angle on Y axis
705         static MatType rotationY(T angle) {
706             return MatType(
707                 cos(angle), 0, sin(angle), 0,
708                 0, 1, 0, 0,
709                 -sin(angle), 0, cos(angle), 0,
710                 0, 0, 0, 1
711             ).fixNegativeZero;
712         }
713 
714         /// Constructs 3d rotation matrix from angle on Z axis
715         static MatType rotationZ(T angle) {
716             return MatType(
717                 cos(angle), -sin(angle), 0, 0,
718                 sin(angle), cos(angle), 0, 0,
719                 0, 0, 1, 0,
720                 0, 0, 0, 1
721             ).fixNegativeZero;
722         }
723         
724         /// Constructs 3d scale matrix
725         static MatType scale(T[3] v, ...) {
726             return MatType(
727                 v[0], 0, 0, 0,
728                 0, v[1], 0, 0,
729                 0, 0, v[2], 0,
730                 0, 0, 0, 1
731             );
732         }
733         
734         /// Constructs 3d translation matrix
735         static MatType translation(T[3] v, ...) {
736             return MatType(
737                 1, 0, 0, v[0],
738                 0, 1, 0, v[1],
739                 0, 0, 1, v[2],
740                 0, 0, 0, 1
741             );
742         }
743         
744         /// Constructs frustum matrix
745         static MatType frustum(T left, T right, T bottom, T top, T near, T far) {
746             T rl = right - left;
747             T tb = top - bottom;
748             T fn = far - near;
749 
750             return MatType(
751                 near * 2.0 / rl, 0, (right + left) / rl, 0,
752                 0, near * 2.0 / tb, (top + bottom) / tb, 0,
753                 0, 0, -(far + near) / fn, -(far * near * 2.0f) / fn,
754                 0, 0, -1.0f, 0
755             );
756         }
757         
758         /++
759         Construct perspective matrix
760         Params:
761             fovy = vertical fov in degrees (int) or radians (double)
762             aspect = screen.width / screen.height
763             near = near cutoff plane
764             fat = far cutoff plane
765         +/
766         static MatType perspective(int fovy, T aspect, T near, T far) {
767             return perspective( fovy * deg2rad, aspect, near, far );
768         }
769 
770         /++ Ditto +/ 
771         static MatType perspective(T fovy, T aspect, T near, T far) {
772             T top = near * tan(fovy * 0.5);
773             T bottom = -top;
774             T right = top * aspect;
775             T left = -right;
776 
777             T rl = right - left;
778             T tb = top - bottom;
779             T fn = far - near;
780             
781             return MatType(
782                 near * 2.0 / rl, 0, (right + left) / rl, 0,
783                 0, near * 2.0 / tb, (top + bottom) / tb, 0,
784                 0, 0, -(far + near) / fn, -(far * near * 2.0) / fn,
785                 0, 0, -1.0f, 0
786             );
787         }
788         
789         /// Constructs orthographic matrix
790         static MatType ortho(T left, T right, T bottom, T top, T near, T far) {
791             T rl = right - left;
792             T tb = top - bottom;
793             T fn = far - near;
794             
795             return MatType(
796                 2.0 / rl, 0, 0, -(left + right) / rl,
797                 0, 2.0 / tb, 0, -(top + bottom) / tb,
798                 0, 0, -2.0 / fn, -(far + near) / fn,
799                 0, 0,  0, 1.0f
800             );
801         }
802         
803         /// Constructs lookAt matrix
804         static MatType lookAt(vec!(T, 3) eye, vec!(T, 3) target, vec!(T, 3) up) {
805             vec3 vz = eye - target;
806             vz.normalize;
807 
808             vec3 vx = up.cross(vz);
809             vx.normalize;
810 
811             vec3 vy = vz.cross(vx);
812 
813             return MatType(
814                 vx.x, vx.y, vx.z, -vx.dot(eye), 
815                 vy.x, vy.y, vy.z, -vy.dot(eye),
816                 vz.x, vz.y, vz.z, -vz.dot(eye),
817                 0, 0, 0, 1
818             );
819         }
820     }
821     
822     /// Removes negative sign from numbers less then float.epsilon*2
823     public MatType fixNegativeZero() {
824         foreach (i; 0 .. H) {
825             foreach (j; 0..W) {
826                 if (abs(data[i][j]) <= float.epsilon * 2.0f) data[i][j] = 0;
827             }
828         }
829         return this;
830     }
831 
832     /++
833     Construct row-major one dimentional array from matrix. 
834 
835     **Do not pass to OpenGL, use** `glbuffer()` **instead**
836     +/
837     public T[W*H] buffer() {
838         T[W*H] ret;
839         foreach (i; 0 .. H) {
840             foreach (j; 0..W) {
841                 ret[i * W + j] = data[i][j];
842             }
843         }
844         return ret;
845     }
846     /// Ditto
847     alias toArray = buffer;
848 
849     /++
850     Construct column-major one dimentional array from matrix.
851 
852     **OpenGL is column-major, please use this method for it.**
853     +/
854     public T[W*H] glbuffer() {
855         T[W*H] ret;
856         foreach (j; 0..W) {
857             foreach (i; 0 .. H) {
858                 ret[j * H + i] = data[i][j];
859             }
860         }
861         return ret;
862     }
863     /// Ditto
864     alias toGlArray = glbuffer;
865 }
866 
867 /// Is V a matrix with any size and any type
868 template isMatrix(M) {
869     enum isMatrix = is(Unqual!M == Matrix!(T, H, W), T, size_t H, size_t W);
870 }
871 
872 /// Is V a vector with size H2xW2 and any type
873 template isMatrix(M, size_t H2, size_t W2) {
874     static if(is(Unqual!M == Matrix!(T, H, W), T, size_t H, size_t W)) {
875         enum isMatrix = H2 == H && W2 == W;
876     } else {
877         enum isMatrix = false;
878     }
879 }
880 
881 /// Is V a vector with any size and type T
882 template isMatrix(M, T) {
883     static if(is(Unqual!M == Matrix!(U, H, W), U, size_t H, size_t W)) {
884         enum isMatrix = is(T == U);
885     } else {
886         enum isMatrix = false;
887     }
888 }
889 
890 /// Is V a vector with size H2xW2 and type T
891 template isMatrix(M, T, size_t H, size_t W) {
892     enum isMatrix = is(Unqual!M == Matrix!(T, H, W));
893 }
894