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