module maths.Vec; private { import maths.Fixed; import maths.Misc; import std.string; } public { import std.math; } const int X = 0; const int Y = 1; const int Z = 2; const int W = 3; struct vec(flt_, int dim_, int xpad, int ypad, int zpad, int wpad) { static assert (dim_ >= 2 && dim_ <= 4); alias flt_ flt; const static bool swizzled = xpad != 0 || ypad != flt.sizeof || zpad != flt.sizeof*2 || wpad != flt.sizeof*3; const static int dim = dim_; static if (!swizzled) { static assert (vec.sizeof == flt.sizeof * dim_); } union { static if (!swizzled) { flt[dim] cell; } static if (dim >= 1) { struct { byte[xpad] xpad_; union { flt x, r; } } } static if (dim >= 2) { struct { byte[ypad] ypad_; union { flt y, g; } } } static if (dim >= 3) { struct { byte[zpad] zpad_; union { flt z, b; } } } static if (dim >= 4) { struct { byte[wpad] wpad_; union { flt w, a; } } } } static if (!swizzled) { static if (2 == dim) const static vec zero = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0) }; static if (3 == dim) const static vec zero = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0), z : .cscalar!(flt, 0) }; static if (4 == dim) const static vec zero = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0), z : .cscalar!(flt, 0), w : .cscalar!(flt, 0) }; static if (2 == dim) const static vec one = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 1) }; static if (3 == dim) const static vec one = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 1), z : .cscalar!(flt, 1) }; static if (4 == dim) const static vec one = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 1), z : .cscalar!(flt, 1), w : .cscalar!(flt, 1) }; static if (2 == dim) const static vec unitX = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 0) }; static if (3 == dim) const static vec unitX = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 0), z : .cscalar!(flt, 0) }; static if (4 == dim) const static vec unitX = { x : .cscalar!(flt, 1), y : .cscalar!(flt, 0), z : .cscalar!(flt, 0), w : .cscalar!(flt, 0) }; static if (2 == dim) const static vec unitY = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 1) }; static if (3 == dim) const static vec unitY = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 1), z : .cscalar!(flt, 0) }; static if (4 == dim) const static vec unitY = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 1), z : .cscalar!(flt, 0), w : .cscalar!(flt, 0) }; static if (3 == dim) const static vec unitZ = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0), z : .cscalar!(flt, 1) }; static if (4 == dim) const static vec unitZ = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0), z : .cscalar!(flt, 1), w : .cscalar!(flt, 0) }; static if (4 == dim) const static vec unitW = { x : .cscalar!(flt, 0), y : .cscalar!(flt, 0), z : .cscalar!(flt, 0), w : .cscalar!(flt, 1) }; } template swizzleDim(int xs, int ys, int zs, int ws) { static if (-1 == xs) const int swizzleDim = swizzleDim!(0, ys, zs, ws) - 1; else static if (-1 == ys) const int swizzleDim = swizzleDim!(xs, 0, zs, ws) - 1; else static if (-1 == zs) const int swizzleDim = swizzleDim!(xs, ys, 0, ws) - 1; else static if (-1 == ws) const int swizzleDim = swizzleDim!(xs, ys, zs, 0) - 1; else const int swizzleDim = 4; } template swizzlePad(int pos) { static if (0 == pos) const int swizzlePad = xpad; else static if (1 == pos) const int swizzlePad = ypad; else static if (2 == pos) const int swizzlePad = zpad; else static if (3 == pos) const int swizzlePad = wpad; else const int swizzlePad = 0; } template swizzleT(int xs, int ys, int zs, int ws) { alias .vec!(flt, swizzleDim!(xs, ys, zs, ws), swizzlePad!(xs), swizzlePad!(ys), swizzlePad!(zs), swizzlePad!(ws)) swizzleT; } swizzleT!(xs, ys, zs, ws)* swizzle(int xs, int ys, int zs=-1, int ws=-1)() { return cast(swizzleT!(xs, ys, zs, ws)*)this; } static if (swizzled) { .vec!(flt, dim, 0, flt.sizeof, flt.sizeof*2, flt.sizeof*3) unswizzle() { .vec!(flt, dim, 0, flt.sizeof, flt.sizeof*2, flt.sizeof*3) res; static if (dim >= 1) res.x = x; static if (dim >= 2) res.y = y; static if (dim >= 3) res.z = z; static if (dim >= 4) res.w = w; return res; } } static vec convert(T)(T v) { vec res; static if (dim >= 1) res.x = scalar!(flt)(v.x); static if (dim >= 2) res.y = scalar!(flt)(v.y); static if (dim >= 3) res.z = scalar!(flt)(v.z); static if (dim >= 4) res.w = scalar!(flt)(v.w); return res; } void set(T)(T v) { static if (dim >= 1) x = scalar!(flt)(v.x); static if (dim >= 2) y = scalar!(flt)(v.y); static if (dim >= 3) z = scalar!(flt)(v.z); static if (dim >= 4) w = scalar!(flt)(v.w); } static if (dim == 2) { static vec opCall(flt x, flt y) { vec res; res.x = x; res.y = y; return res; } } static if (dim == 3) { static vec opCall(flt x, flt y, flt z) { vec res; res.x = x; res.y = y; res.z = z; return res; } } static if (dim == 4) { static vec opCall(flt x, flt y, flt z, flt w) { vec res; res.x = x; res.y = y; res.z = z; res.w = w; return res; } } static if (dim == 2) { static vec opIndex(real x, real y) { vec res; res.x = scalar!(flt)(x); res.y = scalar!(flt)(y); return res; } } static if (dim == 3) { static vec opIndex(real x, real y, real z) { vec res; res.x = scalar!(flt)(x); res.y = scalar!(flt)(y); res.z = scalar!(flt)(z); return res; } } static if (dim == 4) { static vec opIndex(real x, real y, real z, real w) { vec res; res.x = scalar!(flt)(x); res.y = scalar!(flt)(y); res.z = scalar!(flt)(z); res.w = scalar!(flt)(w); return res; } } char[] toString() { char[] res = "["; static if (is(typeof(x.toString))) { res ~= x.toString(); static if (dim >= 2) res ~= ", " ~ y.toString(); static if (dim >= 3) res ~= ", " ~ z.toString(); static if (dim >= 4) res ~= ", " ~ w.toString(); } else { res ~= .toString(x); static if (dim >= 2) res ~= ", " ~ .toString(y); static if (dim >= 3) res ~= ", " ~ .toString(z); static if (dim >= 4) res ~= ", " ~ .toString(w); } return res ~ "]"; } flt dot(T)(inout T v) { static assert (v.dim == dim); flt res = x * v.x; static if (dim >= 2) res += y * v.y; static if (dim >= 3) res += z * v.z; static if (dim >= 4) res += w * v.w; return res; } static if(dim==3) { static vec cross(vec a, vec b) { static assert (a.dim == 3 && b.dim == 3); return opCall ( a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - b.x * a.y ); } } flt sqLen() { return dot(*this); } flt len() { flt sq = sqLen(); if (sq != cscalar!(flt, 0)) { return scalar!(flt)(sqrt(cast(real)sq)); } return sq; } void normalize() { static if (is(flt == float)) { flt sl = sqLen; if (sl != 0.f) { *this *= invSqrt(sl); } } else { flt l = len(); if (l != cscalar!(flt, 0)) { static if (is(flt == float) || is(flt == double) || is(flt == real)) { flt inv = cscalar!(flt, 1) / l; *this *= inv; } else { *this /= l; } } } } vec normalized() { vec res = *this; res.normalize(); return res; } void opSubAssign(inout vec rhs) { static if (dim >= 1) x -= rhs.x; static if (dim >= 2) y -= rhs.y; static if (dim >= 3) z -= rhs.z; static if (dim >= 4) w -= rhs.w; } void opAddAssign(inout vec rhs) { static if (dim >= 1) x += rhs.x; static if (dim >= 2) y += rhs.y; static if (dim >= 3) z += rhs.z; static if (dim >= 4) w += rhs.w; } void opMulAssign(flt_)(flt_ rhs) { static if (dim >= 1) x *= rhs; static if (dim >= 2) y *= rhs; static if (dim >= 3) z *= rhs; static if (dim >= 4) w *= rhs; } void opDivAssign(flt rhs) { static if (dim >= 1) x /= rhs; static if (dim >= 2) y /= rhs; static if (dim >= 3) z /= rhs; static if (dim >= 4) w /= rhs; } vec opSub(inout vec rhs) { static if (2 == dim) return opCall(x - rhs.x, y - rhs.y); static if (3 == dim) return opCall(x - rhs.x, y - rhs.y, z - rhs.z); static if (4 == dim) return opCall(x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w); } vec opAdd(inout vec rhs) { static if (2 == dim) return opCall(x + rhs.x, y + rhs.y); static if (3 == dim) return opCall(x + rhs.x, y + rhs.y, z + rhs.z); static if (4 == dim) return opCall(x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w); } vec opMul(flt_)(flt_ rhs) { vec res = *this; res *= rhs; return res; } vec opDiv(flt rhs) { vec res = *this; res /= rhs; return res; } vec opNeg() { static if (2 == dim) return opCall(-x, -y); static if (3 == dim) return opCall(-x, -y, -z); static if (4 == dim) return opCall(-x, -y, -z, -w); } } alias vec!(float, 2, 0, float.sizeof, float.sizeof*2, float.sizeof*3) vec2; alias vec!(float, 3, 0, float.sizeof, float.sizeof*2, float.sizeof*3) vec3; alias vec!(float, 4, 0, float.sizeof, float.sizeof*2, float.sizeof*3) vec4; alias vec!(fixed, 2, 0, fixed.sizeof, fixed.sizeof*2, fixed.sizeof*3) vec2fi; alias vec!(fixed, 3, 0, fixed.sizeof, fixed.sizeof*2, fixed.sizeof*3) vec3fi; alias vec!(fixed, 4, 0, fixed.sizeof, fixed.sizeof*2, fixed.sizeof*3) vec4fi; alias vec!(int, 2, 0, int.sizeof, int.sizeof*2, int.sizeof*3) vec2i; alias vec!(int, 3, 0, int.sizeof, int.sizeof*2, int.sizeof*3) vec3i; alias vec!(int, 4, 0, int.sizeof, int.sizeof*2, int.sizeof*3) vec4i; alias vec!(ubyte, 2, 0, 1, 2, 3) vec2ub; alias vec!(ubyte, 3, 0, 1, 2, 3) vec3ub; alias vec!(ubyte, 4, 0, 1, 2, 3) vec4ub; alias vec!(double, 2, 0, double.sizeof, double.sizeof*2, double.sizeof*3) vec2d; alias vec!(double, 3, 0, double.sizeof, double.sizeof*2, double.sizeof*3) vec3d; alias vec!(double, 4, 0, double.sizeof, double.sizeof*2, double.sizeof*3) vec4d; unittest { // floats { vec4 a = vec4(0f, 1f, 2f, 3f); auto b = a.swizzle!(Y, X).unswizzle(); assert(b.dim == 2); assert(b == vec2(1f, 0f)); assert(a.swizzle!(W, Z).unswizzle() == vec2(3f, 2f)); assert(a.swizzle!(Y, Z).x == 1.f); assert(a.swizzle!(Z, Y, X).unswizzle() == vec3(2f, 1f, 0f)); assert(a.swizzle!(Z, Y, X)().swizzle!(Y, X)().unswizzle.cell == [1f, 2f]); vec3 i = vec3(1f, 0f, 0f); vec3 j = vec3(0f, 1f, 0f); vec3 k = vec3.cross(i, j); assert (k == vec3(0f, 0f, 1f)); } // fixeds { vec4fi a = vec4fi[0f, 1f, 2f, 3f]; auto b = a.swizzle!(Y, X).unswizzle(); assert(b.dim == 2); assert(b == vec2fi[1f, 0f]); assert(a.swizzle!(W, Z).unswizzle() == vec2fi[3f, 2f]); assert(a.swizzle!(Y, Z).x == scalar!(fixed)(1.f)); assert(a.swizzle!(Z, Y, X).unswizzle() == vec3fi[2f, 1f, 0f]); assert(a.swizzle!(Z, Y, X)().swizzle!(Y, X)().unswizzle.cell == [cscalar!(fixed, 1), cscalar!(fixed, 2)]); vec3fi i = vec3fi[1f, 0f, 0f]; vec3fi j = vec3fi[0f, 1f, 0f]; vec3fi k = vec3fi.cross(i, j); assert (k == vec3fi[0f, 0f, 1f]); } { vec3fi a = vec3fi[1, 2, 3]; auto b = a.swizzle!(X, Y, Y, Z)(); assert (b.unswizzle() == vec4fi[1, 2, 2, 3]); b.y = fixed.fromInt(4); assert (a == vec3fi[1, 4, 3]); b.z = fixed.fromInt(5); assert (a == vec3fi[1, 5, 3]); auto c = a.swizzle!(Z, Y, X)(); assert (c.unswizzle() == vec3fi[3, 5, 1]); assert (c.swizzle!(Z, Y, X).dot(a) == a.sqLen); } { vec3 a = vec3(1, 2, 3); //vec3fi b = vec3fi.convert(a); // bug in dmd.172 vec3fi b = vec3fi.init.convert(a); } }