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