1 module math.linear.vector; 2 3 import core.internal.traits : Unconst; 4 ////import std.traits : Unconst; 5 import std.traits; 6 import std.math; 7 import std.range; 8 import std.algorithm; 9 import std.functional; 10 11 public import math.linear._qv; 12 13 alias Vec2(T) = Vec!(T, 2); 14 alias Vec3(T) = Vec!(T, 3); 15 alias Vec4(T) = Vec!(T, 4); 16 // TODO: Add tests to ensure T is a compotable type (number, etc...). 17 struct Vec(T, size_t size) { 18 alias Type = T; 19 union { 20 T[size] data; 21 struct { 22 static if (size>=1) 23 T x; 24 static if (size>=2) 25 T y; 26 static if (size>=3) 27 T z; 28 static if (size>=4) 29 T w; 30 } 31 } 32 alias data this; 33 34 this(T[size] data ...) { 35 this.data = data; 36 } 37 this(T x) { 38 this.data[] = x; 39 } 40 this(Ts)(Ts data) if (isInputRange!T) { 41 this.data = data.staticArray!size; 42 } 43 44 const 45 Vec!(NT, size) castType(NT)() { 46 return Vec!(NT,size)(data.arrayCast!NT); 47 } 48 49 inout 50 T opIndex(const size_t i) { 51 return data[i]; 52 } 53 void opIndex(const size_t i, T n) { 54 data[i] = n; 55 } 56 57 const 58 auto opBinary(string op, T)(T b) {////if (__traits(compiles, opBinaryImpl!op(this, b))){ 59 static if (__traits(compiles, opBinaryImpl!op(this, b))) 60 return opBinaryImpl!op(this, b); 61 else 62 return opBinaryImpl1!op(this, b); 63 } 64 const 65 auto opBinaryRight(string op, T)(T a) if (__traits(compiles, opBinaryImpl!op(a, this)) || __traits(compiles, opBinaryImpl1!op(a, this))){ 66 static if (__traits(compiles, opBinaryImpl!op(a,this))) 67 return opBinaryImpl!op(a,this); 68 else 69 return opBinaryImpl1!op(a,this); 70 } 71 auto opOpAssign(string op, T)(T b) {////if (__traits(compiles, opOpAssignImpl!op(this, b))){ 72 return opOpAssignImpl!op(this, b); 73 } 74 75 auto map(funs...)() { 76 return vec(data[].map!funs.array[0..size]); 77 } 78 79 const nothrow pure @nogc @safe 80 size_t toHash() { 81 return data.hashOf; 82 } 83 } 84 auto vec(T, size_t size)(T[size] data ...) { 85 return Vec!(T, size)(data); 86 } 87 auto vec(size_t size, T)(T data) { 88 return Vec!(T, size)(data); 89 } 90 91 template vecMap(funs...) { 92 auto vecMap(T, size_t size)(Vec!(T, size) v) { 93 Vec!(typeof(rvalueOf!T.pipe!funs), size) n; 94 static foreach(i; 0..size) { 95 n.data[i] = v[i].pipe!funs; 96 } 97 return n; 98 } 99 } 100 101 T magnitudeSquared(T, size_t size)(const Vec!(T,size) v) { 102 return v.data[].map!"a^^2".sum; 103 } 104 T magnitude(T, size_t size)(const Vec!(T,size) v) { 105 return cast(T) sqrt(cast(real) v.magnitudeSquared); 106 } 107 void normalize(bool zero=true, T, size_t size)(Vec!(T,size) v) { 108 auto ms = v.magnitudeSquared; 109 if (zero && ms == 0) 110 this.data[] = 0; 111 else 112 this.data[] /= cast(T) sqrt(cast(real) ms); 113 } 114 Vec!(T,size) normalized(bool zero=true, T, size_t size)(const Vec!(T,size) v) { 115 Vec!(T,size) n; 116 auto ms = v.magnitudeSquared; 117 if (zero && ms == 0) 118 n.data[] = 0; 119 else 120 n.data[] = v.data[] / cast(T) sqrt(cast(real) ms); 121 return n; 122 } 123 124 auto rotate(T, U)(Vec!(T,2) v, U a) { 125 return vec(v.x * a.cos - v.y * a.sin, v.x * a.sin + v.y * a.cos); 126 } 127 128 129 void invert(T, size_t size)(Vec!(T,size) v) { 130 v.data[] = -v.data[]; 131 } 132 Vec!(T,size) inverse(T, size_t size)(constVec!(T,size) v) { 133 Vec!(T,size) n; 134 n.data[] = -v.data[]; 135 return n; 136 } 137 138 auto cross(T, U)(const Vec!(T,3) a, const Vec!(U,3) b) { 139 return Vec!(typeof(a[0]*b[0]),3) ( a.y * b.z - b.y * a.z 140 , a.z * b.x - b.z * a.x 141 , a.x * b.y - b.x * a.y 142 ); 143 } 144 auto cross(T, U)(const Vec!(T,2) a, const Vec!(U,2) b) { 145 return a.x * b.y - b.x * a.y; 146 } 147 auto dot(T, U, size_t size)(const Vec!(T,size) a, const Vec!(U,size) b) if (size==2||size==3) { 148 return cast(typeof(a[0]*b[0])) zip(a.data[],b.data[]).map!"a[0]*a[1]".sum();// `cast` because sum will increase precision of type. 149 } 150 151 152 ////auto cross(T, U)(const Vec!(T,3) a, const U[3] b) { 153 //// return cross(a, Vec!(U,3)(b)); 154 ////} 155 ////auto cross(T, U)(const T[3] a, const Vec!(U,3) b) { 156 //// return cross(Vec!(T,3)(a), b); 157 ////} 158 ////auto cross(T, U)(const T[3] a, const U[3] b) { 159 //// return cross(Vec!(T,3)(a), Vec!(U,3)(b)); 160 ////} 161 ////auto dot(T, U, size_t size)(const Vec!(T,size) a, const U[size] b) if (size==2||size==3) { 162 //// return dot(a,Vec!(U,size)(b)); 163 ////} 164 ////auto dot(T, U, size_t size)(const T[size] a, const Vec!(U,size) b) if (size==2||size==3) { 165 //// return dot(Vec!(T,size)(a),b); 166 ////} 167 ////auto dot(T, U, size_t size)(const T[size] a, const U[size] b) if (size==2||size==3) { 168 //// return dot(Vec!(T,size)(a),Vec!(U,size)(b)); 169 ////} 170 171 auto abs(T, size_t size)(Vec!(T,size) v) { 172 alias NT = Unconst!(typeof(std.math.abs(rvalueOf!T))); 173 Vec!(NT, size) n; 174 n.data[] = v.data[].map!"abs(a)".array[]; 175 return n; 176 } 177 auto distance(T, U, size_t size)(Vec!(T,size) v, Vec!(U,size) w) { 178 return magnitude(v-w); 179 } 180 181 auto opBinaryImpl(string op, size_t size,T,U)(const Vec!(T, size) a, const Vec!(U, size) b) 182 if (__traits(compiles, mixin("rvalueOf!T"~op~"rvalueOf!U"))) 183 { 184 alias NT = Unconst!(typeof(mixin("rvalueOf!T"~op~"rvalueOf!U"))); 185 Vec!(NT, size) n; 186 static if (__traits(compiles, mixin("a.data[]"~op~"b.data[]"))) 187 n.data[] = mixin("a.data[]"~op~"b.data[]"); 188 else static foreach(i; 0..size) 189 n.data[i] = mixin("a.data[i]"~op~"b.data[i]"); 190 return n; 191 } 192 auto opBinaryImpl(string op, size_t size,T,U)(const Vec!(T, size) a, const U[size] b) 193 if (__traits(compiles, mixin("rvalueOf!T"~op~"rvalueOf!U"))) 194 { 195 return mixin("a"~op~"Vec!(U,size)(b)"); 196 } 197 auto opBinaryImpl(string op, size_t size,T,U)(const T[size] a, const Vec!(U, size) b) 198 if (__traits(compiles, mixin("rvalueOf!T"~op~"rvalueOf!U"))) 199 { 200 return mixin("Vec!(T,size)(a)"~op~"b"); 201 } 202 203 auto opBinaryImpl1(string op, size_t size,T,U)(const Vec!(T, size) a, const U b) 204 if (__traits(compiles, mixin("rvalueOf!T"~op~"rvalueOf!U"))) 205 { 206 alias NT = Unconst!(typeof(mixin("rvalueOf!T"~op~"rvalueOf!U"))); 207 Vec!(NT, size) n; 208 static if (__traits(compiles, mixin("a.data[]"~op~"b"))) 209 n.data[] = mixin("a.data[]"~op~"b"); 210 else static foreach(i; 0..size) 211 n.data[i] = mixin("a.data[i]"~op~"b"); 212 return n; 213 } 214 215 auto opBinaryImpl1(string op, size_t size,T,U)(const T a, const Vec!(U, size) b) 216 if (__traits(compiles, mixin("rvalueOf!T"~op~"rvalueOf!U")) && !__traits(compiles, mixin("opBinaryImpl!\""~op~"\"(rvalueOf!T, rvalueOf!U)"))) 217 { 218 alias NT = Unconst!(typeof(rvalueOf!T*rvalueOf!U)); 219 Vec!(NT, size) n; 220 n.data[] = mixin("a"~op~"b.data[]"); 221 return n; 222 } 223 224 225 226 227 228 229 auto opOpAssignImpl(string op, size_t size,T,U)(ref Vec!(T, size) a, const Vec!(U, size) b) 230 if (__traits(compiles, mixin("lvalueOf!T"~op~"=rvalueOf!U")) && !(isIntegral!T && isFloatingPoint!U)) 231 { 232 static if(__traits(compiles,mixin("a.data[]"~op~"=b.data[];"))) 233 mixin("a.data[]"~op~"=b.data[];"); 234 else static foreach(i; 0..size) 235 mixin("a.data[i]"~op~"=b.data[i];"); 236 return a; 237 } 238 auto opOpAssignImpl(string op, size_t size,T,U)(ref Vec!(T, size) a, const U[size] b) 239 if (__traits(compiles, mixin("lvalueOf!T"~op~"=rvalueOf!U")) && !(isIntegral!T && isFloatingPoint!U)) 240 { 241 static if(__traits(compiles,mixin("a.data[]"~op~"=b[];"))) 242 mixin("a.data[]"~op~"=b[];"); 243 else static foreach(i; 0..size) 244 mixin("a.data[i]"~op~"=b[i];"); 245 return a; 246 } 247 auto opOpAssignImpl(string op, size_t size,T,U)(ref Vec!(T, size) a, const U b) 248 if (__traits(compiles, mixin("lvalueOf!T"~op~"=rvalueOf!U")) && !(isIntegral!T && isFloatingPoint!U)) 249 { 250 static if(__traits(compiles,mixin("a.data[]"~op~"=b;"))) 251 mixin("a.data[]"~op~"=b;"); 252 else static foreach(i; 0..size) 253 mixin("a.data[i]"~op~"=b;"); 254 return a; 255 } 256 257 258 259 260 261 private { 262 inout (NT[]) arrayCast(NT,OT)(inout OT[] xs) { 263 NT[] nxs = new NT[xs.length]; 264 foreach (i,e; xs) { 265 nxs[i] = cast(inout NT) e; 266 } 267 return nxs; 268 } 269 inout(NT[L]) arrayCast(NT,OT,size_t L)(inout OT[L] xs) { 270 NT[L] nxs; 271 foreach (i,e; xs) { 272 nxs[i] = cast(inout NT) e; 273 } 274 return nxs; 275 } 276 } 277 278 279 280 281 unittest { 282 import std.stdio; 283 void testOp(string op)() { 284 void testValues(A,B)(A a1, A a2, A a3, B b1, B b2, B b3) { 285 void testConst(bool aConst, bool bConst)() { 286 void testTypes(AT, BT)() { 287 static if (aConst) 288 const AT a = [a1,a2,a3]; 289 else 290 AT a = [a1,a2,a3]; 291 static if (bConst) 292 const BT b = [b1,b2,b3]; 293 else 294 BT b = [b1,b2,b3]; 295 static assert(is(typeof(mixin("a"~op~"b")) == Vec!(typeof(a1+b1),3))); 296 assert(mixin("a"~op~"b")== vec([mixin("a1"~op~"b1"),mixin("a2"~op~"b2"),mixin("a3"~op~"b3")])); 297 static if(op=="*") { 298 static assert(is(typeof(cross(a,b)) == Vec!(typeof(a1*b1),3))); 299 static assert(is(typeof(dot(a,b)) == typeof(a1*b1))); 300 } 301 } 302 testTypes!(Vec!(A,3), Vec!(B,3)); 303 testTypes!(Vec!(A,3), B[3]); 304 testTypes!(A[3], Vec!(B,3)); 305 } 306 testConst!(false,false); 307 testConst!(true,true); 308 testConst!(true,false); 309 testConst!(false,true); 310 } 311 testValues!(int,int)(1,2,3,2,3,4); 312 testValues!(float,float)(1.5,2.5,3,2.5,3,4.5); 313 testValues!(int,float)(1,2,3,2.5,3,4.5); 314 testValues!(float,double)(1.5,2.5,3,2.5,3,4.5); 315 } 316 testOp!"+"; 317 testOp!"-"; 318 testOp!"*"; 319 testOp!"/"; 320 testOp!"%"; 321 } 322 323 324 325 326