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