1 module prova.math.matrix;
2 
3 import prova.math,
4        std.algorithm.mutation,
5        std.math;
6 
7 /// Struct representing a 4x4 matrix
8 struct Matrix
9 {
10   /// array[row][column]
11   float[4][4] array = [
12     [0, 0, 0, 0],
13     [0, 0, 0, 0],
14     [0, 0, 0, 0],
15     [0, 0, 0, 0]
16   ];
17 
18   ///
19   static @property Matrix identity()
20   {
21     Matrix identity;
22 
23     foreach(i; 0 .. 4)
24       identity[i][i] = 1;
25 
26     return identity;
27   }
28 
29   ///
30   static Matrix ortho(float left, float right, float top, float bottom, float near, float far)
31   {
32     Matrix ortho;
33     ortho[0][0] = 2 / (right - left);
34     ortho[1][1] = 2 / (top - bottom);
35     ortho[2][2] = 2 / (near - far);
36 
37     ortho[0][3] = (left + right) / (left - right);
38     ortho[1][3] = (bottom + top) / (bottom - top);
39     ortho[2][3] = (near + far) / (near - far);
40     ortho[3][3] = 1;
41 
42     return ortho;
43   }
44 
45   ///
46   static Matrix perspective(float width, float height, float near, float far, float fov)
47   {
48     fov = fov / 180 * PI;
49 
50     const float tanHalfFov = tan(fov / 2);
51     const float range = far - near;
52     const float aspectRatio = width / height;
53 
54     Matrix perspective;
55     perspective[0][0] = 1 / (tanHalfFov * aspectRatio);
56     perspective[1][1] = 1 / tanHalfFov;
57     perspective[2][2] = - (far + near) / range;
58     perspective[2][3] = - (2 * near * far) / range;
59     perspective[3][2] = -1;
60     return perspective;
61   }
62 
63   Matrix rotate(Quaternion rotation) const
64   {
65     Matrix rotationMatrix;
66     float x = rotation.x;
67     float y = rotation.y;
68     float z = rotation.z;
69     float w = rotation.w;
70 
71     rotationMatrix.array = [
72       [1 - 2 * y * y - 2 * z * z,     2 * x * y - 2 * w * z,     2 * x * z + 2 * w * y, 0 ],
73       [    2 * x * y + 2 * w * z, 1 - 2 * x * x - 2 * z * z,     2 * y * z - 2 * w * x, 0 ],
74       [    2 * x * z - 2 * w * y,     2 * y * z + 2 * w * x, 1 - 2 * x * x - 2 * y * y, 0 ],
75       [    0f,                        0f,                        0f,                    1f]
76     ];
77 
78     return rotationMatrix * this;
79   }
80 
81   ///
82   Matrix rotateX(float degrees) const
83   {
84     const float angle = degrees / 180 * PI;
85     const float angleSin = sin(-angle);
86     const float angleCos = cos(-angle);
87 
88     Matrix rotation = identity;
89     rotation[1][1] = angleCos;
90     rotation[1][2] = -angleSin;
91     rotation[2][1] = angleSin;
92     rotation[2][2] = angleCos;
93 
94     return rotation * this;
95   }
96 
97   ///
98   Matrix rotateY(float degrees) const
99   {
100     const float angle = degrees / 180 * PI;
101     const float angleSin = sin(-angle);
102     const float angleCos = cos(-angle);
103 
104     Matrix rotation = identity;
105     rotation[0][0] = angleCos;
106     rotation[0][2] = angleSin;
107     rotation[2][0] = -angleSin;
108     rotation[2][2] = angleCos;
109 
110     return rotation * this;
111   }
112 
113   ///
114   Matrix rotateZ(float degrees) const
115   {
116     const float angle = degrees / 180 * PI;
117     const float angleSin = sin(-angle);
118     const float angleCos = cos(-angle);
119 
120     Matrix rotation = identity;
121     rotation[0][0] = angleCos;
122     rotation[0][1] = -angleSin;
123     rotation[1][0] = angleSin;
124     rotation[1][1] = angleCos;
125 
126     return rotation * this;
127   }
128 
129   ///
130   Matrix scale(Vector2 vector) const
131   {
132     return scale(vector.x, vector.y, 1);
133   }
134 
135   ///
136   Matrix scale(Vector3 vector) const
137   {
138     return scale(vector.x, vector.y, vector.z);
139   }
140 
141   ///
142   Matrix scale(float x, float y, float z) const
143   {
144     Matrix result;
145 
146     foreach(col; 0 .. 4) {
147       result[0][col] = array[0][col] * x;
148       result[1][col] = array[1][col] * y;
149       result[2][col] = array[2][col] * z;
150       result[3][col] = array[3][col];
151     }
152 
153     return result;
154   }
155 
156   ///
157   Matrix translate(Vector2 vector) const
158   {
159     return translate(vector.x, vector.y, 0);
160   }
161 
162   ///
163   Matrix translate(Vector3 vector) const
164   {
165     return translate(vector.x, vector.y, vector.z);
166   }
167 
168   ///
169   Matrix translate(float x, float y) const
170   {
171     return translate(x, y, 0);
172   }
173 
174   ///
175   Matrix translate(float x, float y, float z) const
176   {
177     Matrix translation = identity;
178     translation[0][3] = x;
179     translation[1][3] = y;
180     translation[2][3] = z;
181 
182     return translation * this;
183   }
184 
185   ///
186   Matrix transpose() const
187   {
188     Matrix transposition;
189 
190     foreach(x; 0 .. 4)
191       foreach(y; 0 .. 4)
192         transposition[x][y] = array[y][x];
193 
194     return transposition;
195   }
196 
197   ///
198   Matrix invert() const
199   {
200     // calculating inverse using row operations
201     Matrix identity = Matrix.identity;
202     float[8][4] appendedMatrix;
203 
204     // setup
205     foreach(row; 0 .. 4)
206       appendedMatrix[row] = array[row] ~ identity[row];
207 
208     // down
209     foreach(i; 0 .. 4) {
210       float denominator = appendedMatrix[i][i];
211 
212       if(denominator == 0)
213         foreach(col; 0 .. 4)
214           foreach(row; i .. 4) {
215             denominator = appendedMatrix[row][col];
216 
217             if(denominator == 0)
218               continue;
219 
220             swap(appendedMatrix[i], appendedMatrix[row]);
221           }
222 
223       appendedMatrix[i][] *= 1 / denominator;
224 
225       foreach(j; i + 1 .. 4)
226         appendedMatrix[j][] += appendedMatrix[i][] * -appendedMatrix[j][i];
227     }
228 
229     // up
230     foreach_reverse(i; 1 .. 4)
231       foreach_reverse(j; 0 .. i)
232         appendedMatrix[j][] += appendedMatrix[i][] * -appendedMatrix[j][i];
233 
234     // storing results
235     Matrix result;
236 
237     foreach(i; 0 .. 4)
238       result.array[i] = appendedMatrix[i][4 .. 8];
239 
240     return result;
241   }
242 
243   ref float[4] opIndex(int i)
244   {
245     return array[i];
246   }
247 
248   Matrix opAdd(Matrix matrix) const
249   {
250     foreach(y; 0 .. 4)
251       foreach(x; 0 .. 4)
252         matrix[y][x] += array[x][y];
253 
254     return matrix;
255   }
256 
257   Matrix opSub(Matrix matrix) const
258   {
259     foreach(y; 0 .. 4)
260       foreach(x; 0 .. 4)
261         matrix[y][x] -= array[y][x];
262 
263     return matrix;
264   }
265 
266   Matrix opMul(Matrix matrix) const
267   {
268     Matrix result;
269 
270     foreach(y; 0 .. 4)
271       foreach(x; 0 .. 4)
272         foreach(i; 0 .. 4)
273           result[y][x] += array[y][i] * matrix[i][x];
274 
275     return result;
276   }
277 
278   Vector4 opMul(Vector4 vector) const
279   {
280     float[4] result;
281 
282     foreach(i; 0 .. 4) {
283       result[i] = array[0][i] * vector.x +
284                   array[1][i] * vector.y +
285                   array[2][i] * vector.z +
286                   array[3][i] * vector.w;
287     }
288 
289     return Vector4(result[0], result[1], result[2], result[3]);
290   }
291 
292   Vector3 opMul(Vector3 vector) const
293   {
294     float[3] result;
295 
296     foreach(i; 0 .. 3) {
297       result[i] = array[0][i] * vector.x +
298                   array[1][i] * vector.y +
299                   array[2][i] * vector.z +
300                   array[3][i];
301     }
302 
303     return Vector3(result[0], result[1], result[2]);
304   }
305 
306   Vector2 opMul(Vector2 vector) const
307   {
308     float[2] result;
309 
310     foreach(i; 0 .. 2) {
311       result[i] = array[0][i] * vector.x +
312                   array[1][i] * vector.y +
313                   array[3][i];
314     }
315 
316     return Vector2(result[0], result[1]);
317   }
318 
319   Matrix opMul(float a) const
320   {
321     Matrix matrix;
322 
323     foreach(y; 0 .. 4)
324       foreach(x; 0 .. 4)
325         matrix[y][x] = array[y][x] * a;
326 
327     return matrix;
328   }
329 
330   Matrix opDiv(float a) const
331   {
332     Matrix matrix;
333 
334     foreach(y; 0 .. 4)
335       foreach(x; 0 .. 4)
336         matrix[y][x] = array[y][x] / a;
337 
338     return matrix;
339   }
340 }