import raylib; import std.math; mixin template Linear() { import std.algorithm : canFind, map; import std.range : join; import std.traits : FieldNameTuple; private static alias T = typeof(this); static T zero() { enum fragment = [FieldNameTuple!T].map!(field => "0.").join(","); return mixin("T(" ~ fragment ~ ")"); } static T one() { enum fragment = [FieldNameTuple!T].map!(field => "1.").join(","); return mixin("T(" ~ fragment ~ ")"); } T opUnary(string op)() if (["+", "-"].canFind(op)) { enum fragment = [FieldNameTuple!T].map!(field => op ~ field).join(","); return mixin("T(" ~ fragment ~ ")"); } T opBinary(string op)(T rhs) if (["+", "-", "*", "/"].canFind(op)) { enum fragment = [FieldNameTuple!T].map!(field => field ~ op ~ "rhs." ~ field).join(","); return mixin("T(" ~ fragment ~ ")"); } T opBinary(string op)(float rhs) if (["+", "-", "*", "/"].canFind(op)) { enum fragment = [FieldNameTuple!T].map!(field => field ~ op ~ "rhs").join(","); return mixin("T(" ~ fragment ~ ")"); } T opBinaryRight(string op)(float lhs) if (["+", "-", "*", "/"].canFind(op)) { enum fragment = [FieldNameTuple!T].map!(field => "lhs" ~ op ~ field).join(","); return mixin("T(" ~ fragment ~ ")"); } } import std.traits : FieldNameTuple; import std.algorithm : map; import std.range : join; float length(T)(T v) { enum fragment = [FieldNameTuple!T].map!(field => "v." ~ field ~ "*" ~ "v." ~ field).join("+"); return mixin("sqrt(" ~ fragment ~ ")"); } T normalize(T)(T v) { return v / v.length; } float distance(T)(T lhs, T rhs) { return (lhs - rhs).length; } T dot(T)(T lhs, T rhs) { return lhs * rhs; } unittest { assert(-Vector2(1, 2) == Vector2(-1, -2)); assert(Vector3(1, 2, 9) + Vector3(3, 4, 9) == Vector3(4, 6, 18)); assert(4.0f - Vector2.zero == Vector2(4, 4)); assert(Vector2.one - 3.0f == Vector2(-2, -2)); assert(Vector2(3, 4).length == 5.0); } /// Mix `amount` of `lhs` with `1-amount` of `rhs` /// `amount` should be between 0 and 1, but can be anything /// lerp(lhs, rhs, 0) == lhs /// lerp(lhs, rhs, 1) == rhs T lerp(T)(T lhs, T rhs, float amount) { return lhs + amount * (rhs - lhs); } float angle(Vector2 v) { return atan2(v.y, v.x); } /// Rotate on imaginary plane Vector2 rotate(Vector2 lhs, Vector2 rhs) { return Vector2(lhs.x * rhs.x - lhs.y * rhs.y, lhs.x * rhs.y + lhs.y * rhs.x); } Vector2 rotate(Vector2 v, float angle) { return Vector2(v.x * cos(angle) - v.y * sin(angle), v.x * sin(angle) + v.y * cos(angle)); } Vector2 slide(Vector2 v, Vector2 along) { return along.normalize * dot(v, along); } Bivector2 wedge(Vector2 lhs, Vector2 rhs) { return Bivector2(lhs.x * rhs.y - lhs.y * rhs.x); } // dfmt off Bivector3 wedge(Vector3 lhs, Vector3 rhs) { return Bivector3( lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x, ); } Vector3 transform(Vector3 v, Matrix4 mat) { with (v) with (mat) return Vector3( m0 * x + m4 * y + m8 * z + m12, m1 * x + m5 * y + m9 * z + m13, m2 * x + m6 * y + m10 * z + m14 ); } // dfmt on Vector3 cross(Vector3 lhs, Vector3 rhs) { auto v = wedge(lhs, rhs); return Vector3(v.yz, v.zx, v.xy); } // TODO implement rotor3 // Vector3 rotate(Vector3 v, Rotor3 r) { // return ; // } unittest { import std.format; const v = Vector2.one; assert(v.length == sqrt(2.0f), "%s %s".format(v,v.length)); }