1 module des.math.basic.mathstruct; 2 3 import std.string; 4 import std.array; 5 import std.traits; 6 7 import des.math.util; 8 import des.math.basic.traits; 9 10 private enum SEP = " "; 11 12 @property string BasicMathOp( string fields_str )() 13 if( isArrayAccessString( fields_str, SEP, true ) ) 14 { 15 auto fields = fields_str.split(SEP); 16 string bmctorname = "__basic_math_ctor"; 17 return format(` 18 import std.traits; 19 alias Unqual!(typeof(this)) self; 20 21 %1$s 22 %2$s 23 `, 24 basicMathCtor( fields, bmctorname ), 25 staticAsserts( fields ) 26 ) ~ format(` 27 28 auto opAdd( in self b ) const { return %1$s( %2$s ); } 29 auto opSub( in self b ) const { return %1$s( %3$s ); } 30 auto opMul( double b ) const { return %1$s( %4$s ); } 31 auto opDiv( double b ) const { return %1$s( %5$s ); } 32 33 auto opOpAssign(string op)( in self b ) 34 { mixin( "return this = this " ~ op ~ " b;" ); } 35 36 auto opOpAssign(string op)( double b ) 37 { mixin( "return this = this " ~ op ~ " b;" ); } 38 `, 39 bmctorname, 40 opSelf( fields, "+" ), 41 opSelf( fields, "-" ), 42 opEach( fields, "*" ), 43 opEach( fields, "/" ) 44 ); 45 } 46 47 private 48 { 49 string opSelf( string[] fields, string op, string b="b" ) 50 { 51 string[] rb; 52 foreach( f; fields ) 53 rb ~= format( "%1$s %2$s %3$s.%1$s", f, op, b ); 54 return rb.join(", "); 55 } 56 57 unittest 58 { 59 assert( opSelf( "pnt rot".split, "+", "vv" ), "pnt + vv.pnt, rot + vv.rot" ); 60 assert( opSelf( "a b c".split, "*", "x" ), "a * x.a, b * x.b, c * x.c" ); 61 } 62 63 string opEach( string[] fields, string op, string b="b" ) 64 { 65 string[] rb; 66 foreach( f; fields ) 67 rb ~= format( "%1$s %2$s %3$s", f, op, b ); 68 return rb.join(","); 69 } 70 71 unittest 72 { 73 assert( opEach( "pnt rot".split, "+", "vv" ), "pnt + vv, rot + vv" ); 74 assert( opEach( "a b c".split, "*", "x" ), "a * x, b * x, c * x" ); 75 } 76 77 string argName( string f ) 78 { 79 return format( "arg_%s", f.split(".").join("_") ); 80 } 81 82 unittest 83 { 84 assert( argName( "pos" ) == "arg_pos" ); 85 assert( argName( "Pos" ) == "arg_Pos" ); 86 assert( argName( "Pos.x" ) == "arg_Pos_x" ); 87 assert( argName( "P.s.x" ) == "arg_P_s_x" ); 88 } 89 90 string basicMathCtor( string[] fields, string name ) 91 { 92 string args[]; 93 string cbody = "auto ret = cast(self)(this); "; 94 foreach( field; fields ) 95 { 96 auto arg = argName( field ); 97 args ~= format( "in typeof(%s) %s", field, arg ); 98 cbody ~= format( "ret.%1$s = %2$s; ", field, arg ); 99 } 100 cbody ~= "return ret;"; 101 return format( "self %s( %s ) const { %s }", name, args.join(", "), cbody ); 102 } 103 104 unittest 105 { 106 assert( basicMathCtor( ["p", "v"], "b" ) == 107 "self b( in typeof(p) arg_p, in typeof(v) arg_v ) const { auto ret = cast(self)(this); ret.p = arg_p; ret.v = arg_v; return ret; }" ); 108 } 109 110 string staticAsserts( string[] fields ) 111 { 112 string ret; 113 foreach( f; fields ) 114 ret ~= format(`static assert( hasBasicMathOp!(typeof(%1$s)), "member '%1$s' hasn't basic math ops" );`, f ); 115 return ret; 116 } 117 } 118 119 unittest 120 { 121 static struct Val 122 { 123 float v1 = 0; 124 double v2 = 0; 125 mixin( BasicMathOp!"v1 v2" ); 126 } 127 128 static assert( isAssignable!(Unqual!Val,Unqual!Val) ); 129 static assert( is( typeof(Val.init + Val.init) == Val ) ); 130 static assert( is( typeof(Val.init - Val.init) == Val ) ); 131 static assert( is( typeof( cast(Val)(Val.init * 0.5) ) ) ); 132 static assert( is( typeof( cast(Val)(Val.init / 0.5) ) ) ); 133 134 static assert( hasBasicMathOp!Val ); 135 136 auto p1 = Val( 1, 2 ); 137 auto p2 = Val( 2, 3 ); 138 139 assert( p1 + p2 == Val(3,5) ); 140 assert( p2 - p1 == Val(1,1) ); 141 assert( p1 * 3 == Val(3,6) ); 142 assert( p1 / 2 == Val(0.5,1) ); 143 144 static struct Comp 145 { 146 string str; 147 float val; 148 float time = 0; 149 mixin( BasicMathOp!"val" ); 150 } 151 152 static assert( hasBasicMathOp!Comp ); 153 154 auto c1 = Comp( "ololo", 10, 1.3 ); 155 auto c2 = Comp( "valav", 5, .8 ); 156 157 assert( c1 + c2 == Comp("ololo", 15, 1.3) ); 158 } 159 160 unittest 161 { 162 static struct Val 163 { 164 float v1 = 0; 165 double v2 = 0; 166 mixin( BasicMathOp!"v1 v2" ); 167 } 168 169 auto p1 = Val( 1, 2 ); 170 auto p2 = Val( 2, 3 ); 171 172 auto p3 = p1 + p2; 173 p1 += p2; 174 assert( p1 == p3 ); 175 } 176 177 unittest 178 { 179 static struct Vec 180 { 181 double x = 0, y = 0; 182 mixin( BasicMathOp!"x y" ); 183 } 184 185 static assert( hasBasicMathOp!Vec ); 186 187 static struct Point 188 { 189 Vec pos, vel; 190 this( in Vec p, in Vec v ) 191 { 192 pos = p; 193 vel = v; 194 } 195 mixin( BasicMathOp!"pos vel" ); 196 } 197 198 static assert( hasBasicMathOp!Vec ); 199 } 200 201 unittest 202 { 203 import des.math.linear.vector; 204 static assert( hasBasicMathOp!dvec3 ); 205 static assert( hasBasicMathOp!vec3 ); 206 207 static struct Point 208 { 209 vec3 pos, vel; 210 mixin( BasicMathOp!"pos vel" ); 211 } 212 213 static assert( hasBasicMathOp!Point ); 214 } 215 216 unittest 217 { 218 static struct Vec { double x=0, y=0; } 219 static assert( !hasBasicMathOp!Vec ); 220 static struct Point 221 { 222 Vec pos, vel; 223 string str; 224 float val; 225 mixin( BasicMathOp!"pos.x pos.y vel.x vel.y val" ); 226 } 227 static assert( hasBasicMathOp!Point ); 228 auto a = Point( Vec(1,2), Vec(2,3), "hello", 3 ); 229 assert( a + a == Point( Vec(2,4), Vec(4,6), "hello", 6 ) ); 230 assert( a * 2 == Point( Vec(2,4), Vec(4,6), "hello", 6 ) ); 231 }