1 module raymathext; 2 3 import raylib; 4 import std.math; 5 6 pragma(inline, true): 7 8 version (unittest) 9 { 10 import fluent.asserts; 11 } 12 13 mixin template Linear() 14 { 15 import std.algorithm : canFind, map; 16 import std.range : join; 17 import std.traits : FieldNameTuple; 18 19 private static alias T = typeof(this); 20 21 static T zero() 22 { 23 enum fragment = [FieldNameTuple!T].map!(field => "0.").join(","); 24 return mixin("T(" ~ fragment ~ ")"); 25 } 26 27 static T one() 28 { 29 enum fragment = [FieldNameTuple!T].map!(field => "1.").join(","); 30 return mixin("T(" ~ fragment ~ ")"); 31 } 32 33 inout T opUnary(string op)() if (["+", "-"].canFind(op)) 34 { 35 enum fragment = [FieldNameTuple!T].map!(field => op ~ field).join(","); 36 return mixin("T(" ~ fragment ~ ")"); 37 } 38 39 inout T opBinary(string op)(inout T rhs) if (["+", "-"].canFind(op)) 40 { 41 enum fragment = [FieldNameTuple!T].map!(field => field ~ op ~ "rhs." ~ field).join(","); 42 return mixin("T(" ~ fragment ~ ")"); 43 } 44 45 inout T opBinary(string op)(inout float rhs) if (["+", "-", "*", "/"].canFind(op)) 46 { 47 enum fragment = [FieldNameTuple!T].map!(field => field ~ op ~ "rhs").join(","); 48 return mixin("T(" ~ fragment ~ ")"); 49 } 50 51 inout T opBinaryRight(string op)(inout float lhs) if (["+", "-", "*", "/"].canFind(op)) 52 { 53 enum fragment = [FieldNameTuple!T].map!(field => "lhs" ~ op ~ field).join(","); 54 return mixin("T(" ~ fragment ~ ")"); 55 } 56 } 57 58 unittest 59 { 60 Assert.equal(Vector2.init, Vector2.zero); 61 Assert.equal(Vector2(), Vector2.zero); 62 Assert.equal(-Vector2(1, 2), Vector2(-1, -2)); 63 auto a = Vector3(1, 2, 9); 64 immutable b = Vector3(3, 4, 9); 65 Vector3 c = a + b; 66 Assert.equal(c, Vector3(4, 6, 18)); 67 Assert.equal(4.0f - Vector2.zero, Vector2(4, 4)); 68 Assert.equal(Vector2.one - 3.0f, Vector2(-2, -2)); 69 } 70 71 import std.traits : FieldNameTuple; 72 import std.algorithm : map; 73 import std.range : join; 74 75 float length(T)(T v) 76 { 77 enum fragment = [FieldNameTuple!T].map!(field => "v." ~ field ~ "*" ~ "v." ~ field).join("+"); 78 return mixin("sqrt(" ~ fragment ~ ")"); 79 } 80 81 T normal(T)(T v) 82 { 83 return v / v.length; 84 } 85 86 float distance(T)(T lhs, T rhs) 87 { 88 return (lhs - rhs).length; 89 } 90 91 float dot(T)(T lhs, T rhs) 92 { 93 enum fragment = [FieldNameTuple!T].map!(field => "lhs." ~ field ~ "*" ~ "rhs." ~ field).join( 94 "+"); 95 return mixin(fragment); 96 } 97 98 unittest 99 { 100 Assert.equal(Vector2(3, 4).length, 5); 101 const a = Vector2(-3, 4); 102 Assert.equal(a.normal, Vector2(-3. / 5., 4. / 5.)); 103 immutable b = Vector2(9, 8); 104 Assert.equal(b.distance(Vector2(-3, 3)), 13); 105 Assert.equal(Vector3(2, 3, 4).dot(Vector3(4, 5, 6)), 47); 106 Assert.equal(Vector2.one.length, sqrt(2.0f)); 107 } 108 109 /// Mix `amount` of `lhs` with `1-amount` of `rhs` 110 /// `amount` should be between 0 and 1, but can be anything 111 /// lerp(lhs, rhs, 0) == lhs 112 /// lerp(lhs, rhs, 1) == rhs 113 T lerp(T)(T lhs, T rhs, float amount) 114 { 115 return lhs + amount * (rhs - lhs); 116 } 117 118 /// angle betwenn vector and x-axis (+y +x -> positive) 119 float angle(Vector2 v) 120 { 121 return atan2(v.y, v.x); 122 } 123 124 Vector2 rotate(Vector2 v, float angle) 125 { 126 return Vector2(v.x * cos(angle) - v.y * sin(angle), v.x * sin(angle) + v.y * cos(angle)); 127 } 128 129 Vector2 slide(Vector2 v, Vector2 along) 130 { 131 return along.normal * dot(v, along); 132 } 133 134 Vector3 transform(Vector3 v, Matrix mat) 135 { 136 with (v) with (mat) 137 return Vector3( 138 m0 * x + m4 * y + m8 * z + m12, 139 m1 * x + m5 * y + m9 * z + m13, 140 m2 * x + m6 * y + m10 * z + m14 141 ); 142 } 143 // dfmt on 144 145 unittest { 146 // TODO 147 }