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   ///
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     foreach(col; 0 .. 4) {
148       result[0][col] = array[0][col] * x;
149       result[1][col] = array[1][col] * y;
150       result[2][col] = array[2][col] * z;
151       result[3][col] = array[3][col];
152     }
153 
154     return result;
155   }
156 
157   ///
158   Matrix translate(Vector2 vector) const
159   {
160     return translate(vector.x, vector.y, 0);
161   }
162 
163   ///
164   Matrix translate(Vector3 vector) const
165   {
166     return translate(vector.x, vector.y, vector.z);
167   }
168 
169   ///
170   Matrix translate(float x, float y) const
171   {
172     return translate(x, y, 0);
173   }
174 
175   ///
176   Matrix translate(float x, float y, float z) const
177   {
178     Matrix translation = identity;
179     translation[0][3] = x;
180     translation[1][3] = y;
181     translation[2][3] = z;
182 
183     return translation * this;
184   }
185 
186   ///
187   Matrix transpose() const
188   {
189     Matrix transposition;
190 
191     foreach(x; 0 .. 4)
192       foreach(y; 0 .. 4)
193         transposition[x][y] = array[y][x];
194 
195     return transposition;
196   }
197 
198   ///
199   Matrix invert() const
200   {
201     // calculating inverse using row operations
202     Matrix identity = Matrix.identity;
203     float[8][4] appendedMatrix;
204 
205     // setup
206     foreach(row; 0 .. 4)
207       appendedMatrix[row] = array[row] ~ identity[row];
208 
209     // down
210     foreach(i; 0 .. 4) {
211       float denominator = appendedMatrix[i][i];
212 
213       if(denominator == 0)
214         foreach(col; 0 .. 4)
215           foreach(row; i .. 4) {
216             denominator = appendedMatrix[row][col];
217 
218             if(denominator == 0)
219               continue;
220 
221             swap(appendedMatrix[i], appendedMatrix[row]);
222           }
223 
224       appendedMatrix[i][] *= 1 / denominator;
225 
226       foreach(j; i + 1 .. 4)
227         appendedMatrix[j][] += appendedMatrix[i][] * -appendedMatrix[j][i];
228     }
229 
230     // up
231     foreach_reverse(i; 1 .. 4)
232       foreach_reverse(j; 0 .. i)
233         appendedMatrix[j][] += appendedMatrix[i][] * -appendedMatrix[j][i];
234 
235     // storing results
236     Matrix result;
237 
238     foreach(i; 0 .. 4)
239       result.array[i] = appendedMatrix[i][4 .. 8];
240 
241     return result;
242   }
243 
244   ref float[4] opIndex(int i)
245   {
246     return array[i];
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   Matrix opSub(Matrix matrix) const
259   {
260     foreach(y; 0 .. 4)
261       foreach(x; 0 .. 4)
262         matrix[y][x] -= array[y][x];
263 
264     return matrix;
265   }
266 
267   Matrix opMul(Matrix matrix) const
268   {
269     Matrix result;
270 
271     foreach(y; 0 .. 4)
272       foreach(x; 0 .. 4)
273         foreach(i; 0 .. 4)
274           result[y][x] += array[y][i] * matrix[i][x];
275 
276     return result;
277   }
278 
279   Vector4 opMul(Vector4 vector) const
280   {
281     float[4] result;
282 
283     foreach(i; 0 .. 4) {
284       result[i] = array[0][i] * vector.x +
285                   array[1][i] * vector.y +
286                   array[2][i] * vector.z +
287                   array[3][i] * vector.w;
288     }
289 
290     return Vector4(result[0], result[1], result[2], result[3]);
291   }
292 
293   Vector3 opMul(Vector3 vector) const
294   {
295     float[3] result;
296 
297     foreach(i; 0 .. 3) {
298       result[i] = array[0][i] * vector.x +
299                   array[1][i] * vector.y +
300                   array[2][i] * vector.z +
301                   array[3][i];
302     }
303 
304     return Vector3(result[0], result[1], result[2]);
305   }
306 
307   Vector2 opMul(Vector2 vector) const
308   {
309     float[2] result;
310 
311     foreach(i; 0 .. 2) {
312       result[i] = array[0][i] * vector.x +
313                   array[1][i] * vector.y +
314                   array[3][i];
315     }
316 
317     return Vector2(result[0], result[1]);
318   }
319 
320   Matrix opMul(float a) const
321   {
322     Matrix matrix;
323 
324     foreach(y; 0 .. 4)
325       foreach(x; 0 .. 4)
326         matrix[y][x] = array[y][x] * a;
327 
328     return matrix;
329   }
330 
331   Matrix opDiv(float a) const
332   {
333     Matrix matrix;
334 
335     foreach(y; 0 .. 4)
336       foreach(x; 0 .. 4)
337         matrix[y][x] = array[y][x] / a;
338 
339     return matrix;
340   }
341 }