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