1 module polyplex.math.glmath.matrix;
2 import polyplex.math.glmath.vector;
3 import polyplex.math.glmath.quaternion;
4 import std.conv;
5 import std.stdio;
6 
7 struct Matrix(int Dimensions) {
8 	alias GMatrix = typeof(this);
9 	float[Dimensions][Dimensions] data;
10 
11 	this(float[Dimensions][Dimensions] d) {
12 		this.data = d;
13 	}
14 
15 	private static float[Dimensions][Dimensions] clear(float val) {
16 		float[Dimensions][Dimensions] dat;
17 		foreach ( x; 0 .. Dimensions ) {
18 			foreach ( y; 0 .. Dimensions ) {
19 				dat[x][y] = val;
20 			}
21 		}
22 		return dat;
23 	}
24 
25 	public static GMatrix Identity() {
26 		float[Dimensions][Dimensions] data = clear(0);
27 		foreach ( i; 0 .. Dimensions ) {
28 			data[i][i] = 1f;
29 		}
30 		return GMatrix(data);
31 	}
32 
33 	static if (Dimensions == 4)
34 	public static GMatrix Scaling(float3 scale_vec) {
35 		GMatrix i = GMatrix.Identity;
36 		i.data[0][0] = scale_vec.X;
37 		i.data[1][1] = scale_vec.Y;
38 		i.data[2][2] = scale_vec.Z;
39 		return i;
40 	}
41 
42 	static if(Dimensions == 3)
43 	public static GMatrix Scaling(Vector!(float, Dimensions-1) scale_vec) {
44 		float[Dimensions][Dimensions] dims = GMatrix.Identity.data;
45 		foreach( x; 0 .. Dimensions-1 ) {
46 			foreach( y; 0 .. Dimensions-1 ) {
47 				if (x == y) dims[x][y] = scale_vec.data[x];
48 				else dims[x][y] = 0;
49 			}
50 		}
51 		dims[Dimensions-1][Dimensions-1] = 1;
52 		return GMatrix(dims);
53 	}
54 
55 	static if(Dimensions >= 3)
56 	public static GMatrix RotationX(float x_rot) {
57 		float[Dimensions][Dimensions] dims = GMatrix.Identity.data;
58 		dims[1][1] = Mathf.Cos(x_rot);
59 		dims[1][2] = Mathf.Sin(x_rot);
60 		dims[2][1] = -Mathf.Sin(x_rot);
61 		dims[2][2] = Mathf.Cos(x_rot);
62 		return GMatrix(dims);
63 	}
64 
65 	static if(Dimensions >= 3)
66 	public static GMatrix RotationY(float y_rot) {
67 		float[Dimensions][Dimensions] dims = GMatrix.Identity.data;
68 		dims[0][0] = Mathf.Cos(y_rot);
69 		dims[0][2] = -Mathf.Sin(y_rot);
70 		dims[2][0] = Mathf.Sin(y_rot);
71 		dims[2][2] = Mathf.Cos(y_rot);
72 		return GMatrix(dims);
73 	}
74 
75 	static if(Dimensions >= 3)
76 	public static GMatrix RotationZ(float z_rot) {
77 		float[Dimensions][Dimensions] dims = GMatrix.Identity.data;
78 		dims[0][0] = Mathf.Cos(z_rot);
79 		dims[1][0] = Mathf.Sin(z_rot);
80 		dims[0][1] = -Mathf.Sin(z_rot);
81 		dims[1][1] = Mathf.Cos(z_rot);
82 		return GMatrix(dims);
83 	}
84 
85 	static if (Dimensions == 2)
86 	public static GMatrix Rotation(float rot) {
87 		return GMatrix([
88 			[Mathf.Cos(rot), -Mathf.Sin(rot)],
89 			[Mathf.Sin(rot), Mathf.Cos(rot)]
90 		]);
91 	}
92 
93 	static if (Dimensions == 4)
94 	public static GMatrix FromQuaternion(Quaternion quat) {
95 		float qx = quat.X;
96 		float qy = quat.Y;
97 		float qz = quat.Z;
98 		float qw = quat.w;
99 		float n = 2f/(qx*qx+qy*qy+qz*qz+qw*qw);
100 		qx *= n;
101 		qy *= n;
102 		qz *= n;
103 		qw *= n;
104 		return GMatrix([
105 			[1.0f - n*qy*qy - n*qz*qz, n*qx*qy - n*qz*qw, n*qx*qz + n*qy*qw, 0f],
106 			[n*qx*qy + n*qz*qw, 1f - n*qx*qx - n*qz*qz, n*qy*qz - n*qx*qw, 0f],
107 			[n*qx*qz - n*qy*qw, n*qy*qz + n*qx*qw, 1f - n*qx*qx - n*qy*qy, 0f],
108 			[0f, 0f, 0f, 1f]
109 		]);
110 	}
111 
112 	static if(Dimensions == 2)
113 	public static GMatrix Translation(Vector!(float, Dimensions) trans_vec) {
114 		return GMatrix([
115 			[1f, trans_vec.X],
116 			[0f, trans_vec.Y]
117 		]);
118 	}
119 
120 	static if(Dimensions == 3)
121 	public static GMatrix Translation(Vector!(float, Dimensions) trans_vec) {
122 		return GMatrix([
123 			[1f, 0f, trans_vec.X],
124 			[0f, 1f, trans_vec.Y],
125 			[0f, 0f, trans_vec.Z]
126 		]);
127 	}
128 
129 	static if(Dimensions == 4)
130 	public static GMatrix Translation(Vector!(float, Dimensions-1) trans_vec) {		
131 		return GMatrix([
132 			[1f, 0f, 0f, trans_vec.X],
133 			[0f, 1f, 0f, trans_vec.Y],
134 			[0f, 0f, 1f, trans_vec.Z],
135 			[0f, 0f, 0f, 1f]
136 		]);
137 	}
138 
139 	static if(Dimensions == 4)
140 	public static GMatrix Orthographic(float left, float right, float bottom, float top, float znear, float zfar) {
141 		return GMatrix([
142 			[2/(right-left), 0f, 0f, -(right+left)/(right-left)],
143 			[0f, 2/(top-bottom), 0f, -(top+bottom)/(top-bottom)],
144 			[0f, 0f, -2/(zfar-znear), -(zfar+znear)/(zfar-znear)],
145 			[0f, 0f, 0f, 1f]
146 		]);
147 	}
148 
149 	static if(Dimensions == 4)
150 	public static GMatrix OrthographicInverse(float left, float right, float bottom, float top, float znear, float zfar) {
151 		return GMatrix([
152 			[(right-left)/2f, 0, 0, 0],
153 			[0, (top-bottom)/2f, 0, 0],
154 			[0, 0, (zfar-znear)/-2f, 0],
155 			[(right+left)/2f, (top+bottom)/2f, (zfar+znear)/2f, 1f]
156 		]);
157 	}
158 
159 	private static float[6] prepPerspective(float width, float height, float fov, float znear, float zfar) {
160 		float aspect = width/height;
161 		float top = znear * Mathf.Tan(Mathf.ToRadians(fov));
162 		float bottom = -top;
163 		float right = top * aspect;
164 		float left = -right;
165 		return [left, right, bottom, top, znear, zfar];
166 	}
167 
168 	static if(Dimensions == 4)
169 	public static GMatrix Perspective(float width, float height, float fov, float znear, float zfar) {
170 		float[6] persp_data = prepPerspective(width, height, fov, znear, zfar);
171 		return perspective(persp_data[0], persp_data[1], persp_data[2], persp_data[3], persp_data[4], persp_data[5]);
172 	}
173 
174 	static if(Dimensions == 4)
175 	private static GMatrix perspective(float left, float right, float bottom, float top, float near, float far) {
176 		return GMatrix([
177 			[(2f*near)/(right-left), 0, (right+left)/(right-left), 0],
178 			[0, (2f*near)/(top-bottom), -(2f*far*near)/(far-near), 0],
179 			[0, (top+bottom)/(top-bottom), -(far+near)/(far-near), 0],
180 			[0, 0, -1f, 0]
181 		]);
182 	}
183 
184 	public GMatrix opBinary(string op: "*")(GMatrix other) {
185 		float[Dimensions][Dimensions] dim = clear(0);
186 		foreach ( row; 0 .. Dimensions ) {
187 			foreach ( column; 0 .. Dimensions ) {
188 				foreach ( i; 0 .. Dimensions ) {
189 					dim[row][column] += this.data[row][i] * other.data[i][column];
190 				}
191 			}
192 		}
193 		return GMatrix(dim);
194 	}
195 
196 	public GMatrix Transpose() pure const nothrow {
197 		GMatrix temp = this;
198 		static foreach( x; 0 .. Dimensions ) {
199 			static foreach ( y; 0 .. Dimensions ) {
200 				temp.data[x][y] = this.data[y][x];
201 			}
202 		}
203 		return temp;
204 	}
205 
206 	static if (Dimensions == 4)
207 	public GMatrix Translate (Vector!(float, Dimensions-1) trans_vec) {
208 		this = GMatrix.Translation(trans_vec) * this;
209 		return this;
210 	}
211 
212 	static if (Dimensions >= 3)
213 	public GMatrix Scale(Vector!(float, Dimensions-1) scale_vec) {
214 		this = GMatrix.Scaling(scale_vec) * this;
215 		return this;
216 	}
217 
218 	static if (Dimensions >= 3)
219 	public GMatrix RotateX(float rot) {
220 		this = GMatrix.RotationX(rot) * this;
221 		return this;
222 	}
223 
224 	static if (Dimensions >= 3)
225 	public GMatrix RotateY(float rot) {
226 		return GMatrix.RotationY(rot) * this;
227 	}
228 
229 	static if (Dimensions >= 3)
230 	public GMatrix RotateZ(float rot) {
231 		return GMatrix.RotationZ(rot) * this;
232 	}
233 
234 	static if (Dimensions == 2)
235 	public GMatrix Rotate(float rot) {
236 		return GMatrix.Rotation(rot) * this;
237 	}
238 
239 	static if(Dimensions == 4)
240 	public GMatrix Rotate(Quaternion rot) {
241 		return GMatrix.FromQuaternion(rot) * this;
242 	} 
243 
244 	public string ToString() {
245 		string dim = "[";
246 		foreach( x; 0 .. Dimensions ) {
247 			dim ~= "\n\t[";
248 			foreach ( y; 0 .. Dimensions ) {
249 				dim ~= this.data[x][y].text;
250 				if (y != Dimensions-1) dim ~= ", ";
251 			}
252 			dim ~= "]";
253 			if (x != Dimensions-1) dim ~= ",";
254 		}
255 		return dim ~ "\n]";
256 	}
257 	
258 	/// Returns a pointer to the data container of this vector
259 	public inout(float)* ptr() pure inout nothrow { return data[0].ptr; }
260 }
261 
262 alias Matrix4x4 = Matrix!4;
263 alias Matrix3x3 = Matrix!3;
264 alias Matrix2x2 = Matrix!2;
265 
266 alias mat2x2 = Matrix2x2;
267 alias mat3x3 = Matrix3x3;
268 alias mat4x4 = Matrix4x4;
269 
270 unittest {
271 	Matrix4x4 mt4 = Matrix4x4.Identity;
272 	Matrix4x4 mt4_o = Matrix4x4([
273 		[1, 0, 0, 2],
274 		[0, 1, 0, 5],
275 		[0, 0, 1, 3],
276 		[0, 0, 0, 1]
277 	]);
278 
279 	assert((mt4*mt4_o) == mt4_o, "Matrix multiplication error! should be " ~ mt4_o.ToString);
280 	
281 	Matrix3x3 mt3 = Matrix3x3.Identity;
282 	Matrix3x3 mt3_o = Matrix3x3([
283 		[1, 0, 3],
284 		[0, 1, 2],
285 		[0, 0, 1]
286 	]);
287 	assert((mt3*mt3_o) == mt3_o, "Matrix multiplication error! should be " ~ mt3_o.ToString);
288 
289 	Matrix2x2 mt2 = Matrix2x2.Identity;
290 	Matrix2x2 mt2_o = Matrix2x2([
291 		[1, 2],
292 		[0, 1],
293 	]);
294 	assert((mt2*mt2_o) == mt2_o, "Matrix multiplication error! should be " ~ mt3_o.ToString);
295 	
296 	Matrix4x4 orth_mat = Matrix4x4.Orthographic(-1f, 1f, -1f, 1f, -1f, 1f);
297 	Matrix4x4 comp_orth = Matrix4x4([
298 		[1f, 0f, 0f, -0f],
299 		[0f, 1f, 0f, -0f],
300 		[0f, 0f, -1f, -0f],
301 		[0f, 0f, 0f, 1f]
302 	]);
303 	assert(orth_mat == comp_orth, "orth_mat != comp_orth \n" ~ orth_mat.ToString ~ ",\n" ~ comp_orth.ToString);
304 }