1 
2 module ws.math.matrix;
3 
4 import std.math, ws.string, ws.math.vector, ws.math.quaternion;
5 
6 private alias float Type;
7 
8 
9 class Matrix(size_t Width, size_t Height) {
10 
11 
12 	Type[Width*Height] data;
13 
14 
15 	this(){
16 		for(size_t x=0; x<Width; ++x)
17 			for(size_t y=0; y<Height; y++)
18 				data[x + y*Width] = x==y ? 1 : 0;
19 	}
20 	
21 
22 	this(Matrix o){
23 		data = o.data;
24 	}
25 
26 
27 	this(const Type[Width*Height] f){
28 		data = f;
29 	}
30 
31 
32 	Matrix dup(){
33 		return new Matrix(this);
34 	}
35 
36 
37 	Matrix opAssign(Type[Width*Height] f){
38 		data = f;
39 		return this;
40 	}
41 
42 
43 	ref Type opIndex(size_t i){
44 		return data[i];
45 	}
46 
47 	ref Type opIndex(size_t x, size_t y){
48 		return data[y*Width+x];
49 	}
50 
51 
52 	@property static Matrix identity(){
53 		return new Matrix;
54 	}
55 
56 	static if(Width == 4 && Height == 4){
57 		
58 		void scale(float x, float y, float z){
59 			auto t = new Matrix!(4,4);
60 			t[0] = x; t[5] = y; t[10] = z;
61 			this *= t;
62 		}
63 	
64 	
65 		void translate(float[3] v){
66 			auto t = new Matrix!(4,4);
67 			t[12] = v.x; t[13] = v.y; t[14] = v.z;
68 			this *= t;
69 		}
70 	
71 	
72 		void translate(Type x, Type y, Type z){
73 			auto t = new Matrix!(4,4);
74 			t[12] = x; t[13] = y; t[14] = z;
75 			this *= t;
76 		}
77 	
78 	
79 		void rotate(Quaternion q){
80 			data = (this*q).data;
81 		}
82 		
83 		
84 		void rotate(Type angle, Type x, Type y, Type z){
85 			double mag = sqrt(x*x + y*y + z*z);
86 			if (mag == 0.0)
87 				return;
88 			auto r = new Matrix!(4,4);
89 			double s = sin(angle*PI/180);
90 			double c = cos(angle*PI/180);
91 			double one_c = 1.0 - c;
92 			x /= mag;
93 			y /= mag;
94 			z /= mag;
95 			r.data[0] = (one_c*x*x) + c;
96 			r.data[4] = (one_c*x*y) - z*s;
97 			r.data[8] = (one_c*x*z) + y*s;
98 			r.data[1] = (one_c*y*x) + z*s;
99 			r.data[1+4] = (one_c*y*y) + c;
100 			r.data[1+8] = (one_c*y*z) - x*s;
101 			r.data[2] = (one_c*z*x) - y*s;
102 			r.data[2+4] = (one_c*z*y) + x*s;
103 			r.data[2+8] = (one_c*z*z) + c;
104 			this *= r;
105 		}
106 
107 		Matrix inverse(){
108 			// http://rodolphe-vaillant.fr/?e=7
109 			auto res = new Matrix;
110 			Type MINOR(int r0, int r1, int r2, int c0, int c1, int c2)
111 			{
112 				return this[4*r0+c0] * (this[4*r1+c1] * this[4*r2+c2] - this[4*r2+c1] * this[4*r1+c2]) -
113 					   this[4*r0+c1] * (this[4*r1+c0] * this[4*r2+c2] - this[4*r2+c0] * this[4*r1+c2]) +
114 					   this[4*r0+c2] * (this[4*r1+c0] * this[4*r2+c1] - this[4*r2+c0] * this[4*r1+c1]);
115 			}
116 			Type det()
117 			{
118 				return this[0] * MINOR(1, 2, 3, 1, 2, 3) -
119 					   this[1] * MINOR(1, 2, 3, 0, 2, 3) +
120 					   this[2] * MINOR(1, 2, 3, 0, 1, 3) -
121 					   this[3] * MINOR(1, 2, 3, 0, 1, 2);
122 			}
123 			res[ 0] =  MINOR(1,2,3,1,2,3); res[ 1] = -MINOR(0,2,3,1,2,3); res[ 2] =  MINOR(0,1,3,1,2,3); res[ 3] = -MINOR(0,1,2,1,2,3);
124 			res[ 4] = -MINOR(1,2,3,0,2,3); res[ 5] =  MINOR(0,2,3,0,2,3); res[ 6] = -MINOR(0,1,3,0,2,3); res[ 7] =  MINOR(0,1,2,0,2,3);
125 			res[ 8] =  MINOR(1,2,3,0,1,3); res[ 9] = -MINOR(0,2,3,0,1,3); res[10] =  MINOR(0,1,3,0,1,3); res[11] = -MINOR(0,1,2,0,1,3);
126 			res[12] = -MINOR(1,2,3,0,1,2); res[13] =  MINOR(0,2,3,0,1,2); res[14] = -MINOR(0,1,3,0,1,2); res[15] =  MINOR(0,1,2,0,1,2);
127 			Type inv_det = 1.0 / det();
128 			for(int i = 0; i < 16; ++i)
129 				res[i] = res[i] * inv_det;
130 			return res;
131 		}
132 
133 	}
134 
135 
136 	Matrix!(Height,Width) transpose(){
137 		auto res = new Matrix!(Height,Width);
138 		for(int x=0; x<Width; x++)
139 			for(int y=0; y<Height; y++)
140 				res[y, x] = this[x, y];
141 		return res;
142 	}
143 	
144 
145 	Type[3] opBinary(string op)(Type[3] v) if(op=="*" && Width == 4) {
146 		Type[4] ve = v ~ 1;
147 		ve = opBinary!op(ve);
148 		return [ve[0], ve[1], ve[2]];
149 	}
150 
151 	
152 	Type[Width] opBinary(string op)(Type[Width] v) if(op=="*") {
153 		Type[Width] res = 0;
154 		for(int x=0; x<Width; x++)
155 			for(int y=0; y<Height; y++)
156 				res[x] += v[y] * this[x, y];
157 		return res;
158 	}
159 
160 
161 	auto opBinary(string op, T)(T v) if(op=="*"){
162 		static if(is(T==Type)){
163 			
164 			auto o = new Matrix;
165 			for (long i=0; i<Width*Height; ++i)
166 				mixin("o[i] " ~ op ~ "= v;");
167 			return o;
168 							
169 		}else static if(is(T==Matrix)){
170 			
171 			auto o = new Matrix;
172 			for (int i = 0; i < Width; i++)
173 				for(int y = 0; y < Width; y++){
174 					o.data[i+Width*y] = 0;
175 					for(int x = 0; x < Width; x++)
176 						o.data[i+Width*y] += data[i+Width*x] * v.data[x+Width*y];
177 				}
178 			return o;
179 				
180 		}else static if(is(T==Quaternion)){
181 			
182 			auto o = new Matrix;
183 			o = this * v.matrix();
184 			return o;
185 
186 		}else{
187 			import std.traits;
188 			static assert(false, "Cannot multiply Matrix!(%s,%s) with %s".format(Width,Height, fullyQualifiedName!T));
189 		}
190 	}
191 	
192 	
193 	Matrix opOpAssign(string op)(Matrix n){
194 		mixin("data = (this " ~ op ~ " n).data;");
195 		return this;
196 	}
197 	
198 
199 	override string toString(){
200 		string s = "Matrix!(%,%){\n".tostring(Width, Height);
201 		for(size_t y=0; y<Height; y++){
202 			s ~= '\t';
203 			for(size_t x=0; x<Width; x++)
204 				s ~= ' '.tostring(data[y*Width+x], x==Width-1 ? (y==Height-1 ? "\n" : ",\n") : ",");
205 				//s ~= tostring(' ', data[y*Width+x], x==Width-1 ? (y==Height-1 ? "\n" : ",\n") : ",");
206 		}
207 		s ~= "}";
208 		return s;
209 	}
210 
211 
212 }
213 
214 
215 unittest {
216 
217 	auto mat1 = new Matrix!(3,3)([
218 		1, 2, 3,
219 		4, 5, 6,
220 		7, 8, 9
221 	]);
222 	float[3] vec = [10, 11, 12];
223 	float[3] resultExpected = [
224 		mat1[0,0] * vec[0] + mat1[0,1] * vec[1] + mat1[0,2] * vec[2],
225 		mat1[1,0] * vec[0] + mat1[1,1] * vec[1] + mat1[1,2] * vec[2],
226 		mat1[2,0] * vec[0] + mat1[2,1] * vec[1] + mat1[2,2] * vec[2]
227 	];
228 	float[3] result = mat1*vec;
229 	assert(result == resultExpected);
230 
231 }
232