1 /** 2 math.vector 3 4 5 Authors: Peter Particle ( based on gl3n by David Herberth ) 6 License: MIT 7 8 Note: All methods marked with pure are weakly pure since, they all access an instance member. 9 All static methods are strongly pure. 10 */ 11 12 module dlsl.vector; 13 14 import std.math : sqrt; 15 import std.format : sformat; 16 import std.traits : isFloatingPoint, isArray; 17 18 19 /// If T is a vector, this evaluates to true, otherwise false 20 template isVector( T ) { enum isVector = is( typeof( isVectorImpl( T.init ))); } 21 private void isVectorImpl( T, int dim )( Vector!( T, dim ) vec ) {} 22 23 /// Base template for all vector-types. 24 /// Params: 25 /// type = the value type of each vector element 26 /// dim = specifies the dimension of the vector, can be 1, 2, 3 or 4 27 /// Examples: 28 /// --- 29 /// alias Vector!(int, 3) vec3i; 30 /// alias Vector!(float, 4) vec4; 31 /// alias Vector!(real, 2) vec2r; 32 /// --- 33 struct Vector( type, int dim ) if (( dim >= 2 ) && ( dim <= 4 )) { 34 alias type valueType; /// Holds the internal type of the vector 35 static const ubyte dimension = dim; /// Holds the dimension of the vector 36 37 valueType[ dimension ] data; /// Holds all coordinates, length conforms dimension 38 alias data this; /// The Vector can be treated as an array 39 40 41 // Unittest Construction via aliased this static array 42 unittest { 43 vec2 v2 = [ 0.0f, 1.0f ]; 44 assert( v2[ 0 ] == 0.0f ); 45 assert( v2[ 1 ] == 1.0f ); 46 47 v2[ 0 .. 1 ] = [ 2.0 ]; 48 assert( v2 == [ 2.0f, 1.0f ] ); 49 50 vec3 v3 = vec3( 0.0f, 0.1f, 0.2f ); 51 assert( v3[ 0 ] == 0.0f ); 52 assert( v3[ 1 ] == 0.1f ); 53 assert( v3[ 2 ] == 0.2f ); 54 55 v3[ 0 .. $ ] = 0.0f; 56 assert( v3 == [ 0.0f, 0.0f, 0.0f ] ); 57 58 vec4 v4 = vec4( 0.0f, 0.1f, 0.2f, 0.3f ); 59 assert( v4[ 0 ] == 0.0f ); 60 assert( v4[ 1 ] == 0.1f ); 61 assert( v4[ 2 ] == 0.2f ); 62 assert( v4[ 3 ] == 0.3f ); 63 64 v4[ 1 .. 3 ] = [ 1.0f, 2.0f ]; 65 assert( v4 == [ 0.0f, 1.0f, 2.0f, 0.3f ] ); 66 } 67 68 /// Returns a pointer to the coordinates. 69 @property auto ptr() { return data.ptr; } 70 71 /// Returns the current vector formatted as string, useful for printing the vector 72 @property string asString() { import std.string : format; return format( "%s", data ); } 73 alias asString toString; 74 75 @safe pure nothrow : 76 template isCompatibleVector( T ) { enum isCompatibleVector = is( typeof( isCompatibleVectorImpl( T.init ))); } 77 //static void isCompatibleVectorImpl( int dim )( Vector!( valueType, dim ) vec ) if( dim <= dimension ) {} // no valueType conversion 78 static void isCompatibleVectorImpl( vt, int dim )( Vector!( vt, dim ) vec ) if( dim <= dimension ) {} // implicit valueType conversion 79 80 /// Unittest : isCompatibleType 81 unittest { 82 // isCompatibleType without implicit valueType conversion 83 //vec2 v2; assert( v2.isCompatibleVector!vec2 ); assert( !v2.isCompatibleVector!vec3 ); assert( !v2.isCompatibleVector!vec4 ); 84 //vec3 v3; assert( v3.isCompatibleVector!vec2 ); assert( v3.isCompatibleVector!vec3 ); assert( !v3.isCompatibleVector!vec4 ); 85 //vec4 v4; assert( v4.isCompatibleVector!vec2 ); assert( v4.isCompatibleVector!vec3 ); assert( v4.isCompatibleVector!vec4 ); 86 87 // isCompatibleType with implicit valueType conversion 88 vec2 v2; assert( v2.isCompatibleVector!vec2i ); assert( !v2.isCompatibleVector!vec3d ); assert( !v2.isCompatibleVector!vec4 ); 89 vec3i v3; assert( v3.isCompatibleVector!vec2d ); assert( v3.isCompatibleVector!vec3 ); assert( !v3.isCompatibleVector!vec4i ); 90 vec4d v4; assert( v4.isCompatibleVector!vec2 ); assert( v4.isCompatibleVector!vec3i ); assert( v4.isCompatibleVector!vec4d ); 91 } 92 93 94 /// Helper method to recursivly construct a vector from an argument list with different types 95 /// TODO: Fix Construction with combination of numeric, vector, static and dynamic array 96 private void construct( int i, T, Tail... )( T head, Tail tail ) { 97 import std.traits : isDynamicArray, isStaticArray; 98 static if ( i >= dimension ) { 99 static assert( false, "Constructor has too many arguments!" ); 100 } else static if ( is( T : valueType )) { 101 data[i] = head; 102 construct!( i + 1 )( tail ); 103 } else static if ( isDynamicArray!T ) { 104 static assert(( Tail.length == 0 ) && ( i == 0 ), "dynamic array can not be passed together with other arguments" ); 105 data = head; 106 } else static if( isStaticArray!T ) { 107 data[ i .. i + T.length ] = head; 108 construct!( i + T.length )( tail ); 109 } else static if ( isCompatibleVector!T ) { 110 data[ i .. i + T.dimension ] = head.data; 111 construct!( i + T.dimension )( tail ); 112 } else { 113 char[128] formatBuffer; 114 auto formatString = sformat( formatBuffer, "Vector constructor argument must be of type %s or Vector, not %s", valueType.stringof, T.stringof ); 115 static assert( false, formatString ); 116 } 117 } 118 119 private void construct( int i )() {} // terminate 120 121 /// TODO: Unittest: Construction with combination of numeric, vector, static and dynamic array 122 unittest { 123 auto v2 = vec2( 2.0f, 1 ); 124 } 125 126 127 /// Constructs the vector 128 /// If a single value is passed the vector, the vector will be cleared with this value 129 /// If a vector with a higher dimension is passed the vector will hold the first values up to its dimension 130 /// If mixed types are passed they will be joined together ( allowed types: vector, static array, $( I vt )) 131 /// Examples: 132 /// --- 133 /// vec4 v4 = vec4( 1.0f, vec2( 2.0f, 3.0f ), 4.0f ); 134 /// vec3 v3 = vec3( v4 ); // v3 = vec3( 1.0f, 2.0f, 3.0f ); 135 /// vec2 v2 = v3.xy; // swizzling returns a static array. 136 /// vec3 v3_2 = vec3( 1.0f ); // vec3 v3_2 = vec3( 1.0f, 1.0f, 1.0f ); 137 /// --- 138 this( Args... )( Args args ) { construct!( 0 )( args ); } 139 140 141 /// Construct a Vector from another Vector 142 this( V )( V vec ) if ( isVector!V && ( V.dimension >= dimension )) { 143 auto minDimension = dimension < V.dimension ? dimension : V.dimension; 144 static if( __traits( compiles, data = vec.data[ 0 .. minDimension ] )) { 145 data = vec.data[ 0 .. dimension ]; 146 } else { 147 foreach( i; 0 .. minDimension ) { 148 data[i] = cast( valueType )vec.data[i]; 149 } 150 } 151 } 152 153 154 /// Unittest: Construct a Vector from another Vector 155 unittest { 156 auto v4 = vec4( 1.0f, 2, 3, 4 ); 157 auto v3 = vec3( v4 ); 158 assert( v3 == [ 1, 2, 3 ] ); 159 160 auto v2 = vec2( v4 ); 161 assert( v2 == [ 1, 2 ] ); 162 163 /// Different valueTypes 164 auto v4i = vec4i( 1, 2, 3, 4 ); 165 auto v3d = vec3d( v4i ); 166 assert( v3d == [ 1, 2, 3 ] ); 167 168 v3d.y = 3.9; 169 auto v2i = vec2i( v3d ); 170 assert( v2i == [ 1, 3 ] ); 171 } 172 173 174 /// Construct a Vector from a single value 175 this()( valueType value ) { 176 clear( value ); 177 } 178 179 180 /// Unittest Construct a Vector from a single value 181 unittest { 182 auto v2 = vec2( 2 ); 183 assert( v2 == [ 2, 2 ] ); 184 185 auto v3i = vec3i( 3 ); 186 assert( v3i == [ 3, 3, 3 ] ); 187 188 auto v4ub = vec4ub( 4 ); 189 assert( v4ub == [ 4, 4, 4, 4 ] ); 190 } 191 192 193 /// FloatingPoint valueType, Returns true if all values are not nan and finite, otherwise false 194 static if ( isFloatingPoint!valueType ) { 195 bool opCast( T : bool )() const { return ok; } 196 import std.math : isNaN, isInfinity; 197 @property bool ok() const { 198 foreach( ref val; data ) { 199 if ( isNaN( val ) || isInfinity( val )) return false; 200 } 201 return true; 202 } 203 } 204 205 206 /// Sets all values of the vector to value 207 void clear( valueType value ) { 208 foreach( ref v; data ) { 209 v = value; 210 } 211 } 212 213 214 /// Unittest Construction 215 /// TODO: Split up these Unittests 216 unittest { 217 vec3 vecClear; 218 assert( !vecClear.ok ); 219 vecClear.clear( 1.0f ); 220 assert( vecClear.ok ); 221 assert( vecClear.data == [ 1.0f, 1.0f, 1.0f ] ); 222 assert( vecClear.data == vec3( 1.0f ).data ); 223 vecClear.clear( float.infinity ); 224 assert( !vecClear.ok ); 225 vecClear.clear( float.nan ); 226 assert( !vecClear.ok ); 227 vecClear.clear( 1.0f ); 228 assert( vecClear.ok ); 229 230 vec4 b = vec4( 1.0f, vecClear ); 231 assert( b.ok ); 232 assert( b.data == [ 1.0f, 1.0f, 1.0f, 1.0f ] ); 233 assert( b.data == vec4( 1.0f ).data ); 234 235 vec2 v2_1 = vec2( vec2( 0.0f, 1.0f )); 236 assert( v2_1.data == [ 0.0f, 1.0f ] ); 237 238 vec2 v2_2 = vec2( 1.0f, 1.0f ); 239 assert( v2_2.data == [ 1.0f, 1.0f ] ); 240 241 vec3 v3 = vec3( v2_1, 2.0f ); 242 assert( v3.data == [ 0.0f, 1.0f, 2.0f ] ); 243 244 vec4 v4_1 = vec4( 1.0f, vec2( 2.0f, 3.0f ), 4.0f ); 245 assert( v4_1.data == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 246 assert( vec3( v4_1 ).data == [ 1.0f, 2.0f, 3.0f ] ); 247 assert( vec2( vec3( v4_1 )).data == [ 1.0f, 2.0f ] ); 248 assert( vec2( vec3( v4_1 )).data == vec2( v4_1 ).data ); 249 assert( v4_1.data == vec4( [ 1.0f, 2.0f, 3.0f, 4.0f ] ).data ); 250 251 vec4 v4_2 = vec4( vec2( 1.0f, 2.0f ), vec2( 3.0f, 4.0f )); 252 assert( v4_2.data == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 253 assert( vec3( v4_2 ).data == [ 1.0f, 2.0f, 3.0f ] ); 254 assert( vec2( vec3( v4_2 )).data == [ 1.0f, 2.0f ] ); 255 assert( vec2( vec3( v4_2 )).data == vec2( v4_2 ).data ); 256 assert( v4_2.data == vec4([ 1.0f, 2.0f, 3.0f, 4.0f ] ).data ); 257 258 float[2] f2 = [ 1.0f, 2.0f ]; 259 float[3] f3 = [ 1.0f, 2.0f, 3.0f ]; 260 float[4] f4 = [ 1.0f, 2.0f, 3.0f, 4.0f ]; 261 assert( vec2( 1.0f, 2.0f ).data == vec2(f2).data); 262 assert( vec3( 1.0f, 2.0f, 3.0f ).data == vec3( f3 ).data ); 263 assert( vec3( 1.0f, 2.0f, 3.0f ).data == vec3( f2, 3.0f ).data ); 264 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f4 ).data ); 265 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f3, 4.0f ).data ); 266 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f2, 3.0f, 4.0f ).data ); 267 // useful for: "vec4 v4 = […]" or "vec4 v4 = other_vector.rgba" 268 269 assert( vec3( vec3i( 1, 2, 3 )) == vec3( 1.0, 2.0, 3.0 )); 270 assert( vec3d( vec3( 1.0, 2.0, 3.0 )) == vec3d( 1.0, 2.0, 3.0 )); 271 } 272 273 /// Build swizzle setter and getter properties 274 import std.conv : to; 275 enum string[dimension] comp = ( [ "x", "y", "z", "w" ] )[ 0 .. dimension ]; 276 enum string[dimension] fill = ( [ "", " ", " ", " " ] )[ 0 .. dimension ]; 277 278 /// generates one swizzle setter property for a given permutation 279 static string genSetSwizz( int[] idx ) { 280 string property; 281 string funcBody; 282 foreach( i; 0 .. idx.length ) { 283 property ~= comp[ idx[i] ]; 284 funcBody ~= "data[" ~ to!string( idx[i] ) ~ "] = vec[" ~ to!string( i ) ~ "]; "; 285 } 286 string funcHead = "( Vector!( valueType, " ~ to!string( idx.length ) ~ " ) vec ) { "; 287 return "@property void " ~ fill[ dimension - idx.length ] ~ property ~ funcHead ~ funcBody ~ "}\n"; 288 } 289 290 /// generates all possible swizzle setter properties for the default permutation ( e.g. [0,1,2,3] ) 291 static string setSwizz( int[] idx ) { 292 string result; 293 if ( idx.length == 1 ) { 294 auto funcBody = "( valueType val ) { " ~ "data[" ~ to!string( idx[0] ) ~ "] = val; }\n"; 295 result = "@property void " ~ fill[ dimension - idx.length ] ~ comp[ idx[0] ] ~ funcBody; 296 } 297 else if ( idx.length == 2 ) { 298 foreach( i; 0 .. 2 ) { 299 result ~= "\n" ~ setSwizz( [ idx[ i ] ] ); 300 result ~= genSetSwizz( [ idx[ i ], idx[ (i+1) % 2 ] ] ); 301 } 302 } 303 else if ( idx.length == 3 ) { 304 foreach( i; 0 .. 3 ) { 305 result ~= "\n" ~ setSwizz( [ idx[ i ] ] ); 306 foreach( j; 0 .. 2 ) result ~= genSetSwizz( [ idx[ i ], idx[ ( i + j + 1 ) % 3 ] ] ); 307 foreach( j; 0 .. 2 ) result ~= genSetSwizz( [ idx[ i ], idx[ ( i + j + 1 ) % 3 ], idx[ ( i + ( j + 1 ) % 2 + 1 ) % 3 ] ] ); 308 } 309 } 310 else if ( idx.length == 4 ) { 311 foreach( i; 0 .. 4 ) { 312 result ~= "\n" ~ setSwizz( [ idx[ i ] ] ); 313 foreach( j; 0 .. 3 ) { 314 result ~= genSetSwizz( [ idx[ i ], idx[ ( i + j + 1 ) % 4 ] ] ); 315 foreach( k; 0 .. 2 ) result ~= genSetSwizz( [ idx[ i ], idx[ ( i + j + 1 ) % 4 ], idx[ ( i + ( j + k + 1 ) % 3 + 1 ) % 4 ] ] ); 316 foreach( k; 0 .. 2 ) result ~= genSetSwizz( [ idx[ i ], idx[ ( i + j + 1 ) % 4 ], idx[ ( i + ( j + k + 1 ) % 3 + 1 ) % 4 ], idx[ ( i + ( j + ( k + 1 ) % 2 + 1 ) % 3 + 1 ) % 4 ] ] ); 317 } 318 } 319 } 320 return result; 321 } 322 323 enum string[dimension] aCol = ( [ "r", "g", "b", "a" ] )[ 0 .. dimension ]; 324 enum string[dimension] aTex = ( [ "s", "t", "p", "q" ] )[ 0 .. dimension ]; 325 326 /// Generates all possible swizzle getter properties, creates also property aliases e.g. rgba 327 static string getSwizz( string component, string alias1, string alias2, string r, int term ) { 328 string returnDim = to!string( dimension - term + 1 ); 329 string returnType = "@property auto "; 330 string returnPrefix = "() " ~ fill[ term - 1 ] ~ "const { return "; 331 string result; 332 333 foreach( i; 0 .. dimension ) { 334 string property = component ~ comp[ i ]; 335 string aliColor = alias1 ~ aCol[ i ]; 336 string aliTexST = alias2 ~ aTex[ i ]; 337 338 string returnData = r ~ "data[" ~ to!string( i ) ~ "]"; 339 string returnString = returnPrefix ~ ( term == dimension 340 ? returnData ~ "; }" 341 : "Vector!( valueType, " ~ returnDim ~ " )( " ~ returnData ~ " ); }" ); 342 /// skip vec2.xy, vec3.xyz, vec4.xyzw which all return identity 343 result ~= "alias " ~ aliColor~ fill[ term - 1 ] ~ " = " ~ property ~ fill[ term - 1 ] ~ "; "; 344 result ~= "alias " ~ aliTexST~ fill[ term - 1 ] ~ " = " ~ property ~ fill[ term - 1 ] ~ "; "; 345 if ( dimension == 2 && property == "xy" ) result ~= returnType ~ property ~ "() const { return this; }\n"; 346 else if ( dimension == 3 && property == "xyz" ) result ~= returnType ~ property ~ "() const { return this; }\n"; 347 else if ( dimension == 4 && property == "xyzw" ) result ~= returnType ~ property ~ "() const { return this; }\n"; 348 else { 349 result ~= returnType ~ property ~ returnString ~ "\n"; 350 if ( term > 1 ) result ~= getSwizz( property, aliColor, aliTexST, returnData ~ ", ", term - 1 ); 351 } 352 } 353 return result; 354 } 355 356 enum int[ dimension ] idcs = ( [ 0, 1, 2, 3 ] )[ 0 .. dimension ]; 357 mixin( setSwizz( idcs )); 358 mixin( getSwizz( "", "", "", "", dimension )); 359 //pragma( msg, setSwizz( idcs )); 360 //pragma( msg, getSwizz( "", "", "", "", dimension )); 361 362 // another swizzle variant based on opDispatch, source: http://www.mmartins.me/view/2015/9/27/vector-swizzle-in-d 363 // drawback is that lib is always recompiled if any swizzle operator changes, implementation at bottom of file 364 365 366 // Unittest swizzle setter 367 unittest { 368 vec2 v2 = vec2( 1 ); 369 v2.x = 0; 370 assert( v2 == [ 0, 1 ] ); 371 372 vec3 v3 = vec3( 0 ); 373 //v3.yx = [ 1, 2 ]; 374 v3.yx = vec2( 1, 2 ); 375 assert( v3 == [ 2, 1, 0 ] ); 376 v3.zx = vec2( 2, 0 ); 377 assert( v3 == [ 0, 1, 2 ] ); 378 v3.zyx = vec3( 3, 2, 1 ); 379 assert( v3 == [ 1, 2, 3 ] ); 380 381 vec4 v4 = vec4( 0 ); 382 //v3.yx = [ 1, 2 ]; 383 v4.wx = vec2( 1, 2 ); 384 assert( v4 == [ 2, 0, 0, 1 ] ); 385 v4.zx = vec2( 2, 0 ); 386 assert( v4 == [ 0, 0, 2, 1 ] ); 387 v4.zyx = vec3( 3, 2, 1 ); 388 assert( v4 == [ 1, 2, 3, 1 ] ); 389 } 390 391 // Unittest swizzle getter 392 unittest { 393 vec2 v2 = vec2( 1 ); 394 assert( v2 == v2.xy ); 395 v2.x = 0; 396 assert( v2 == [ 0, 1 ] ); 397 assert( v2.x == 0 ); 398 399 vec3 v3 = vec3( 0 ); 400 v3.yx = vec2( 1, 2 ); 401 //v3.yx = [ 1, 2 ]; 402 assert( v3 == [ 2, 1, 0 ] ); 403 } 404 //*/ 405 /// Updates the vector with the values from other. 406 //void update( Vector!( valueType, dimension ) vec ) { data = vec.data; } 407 408 /// Unittest for setting Values 409 unittest { 410 vec2 v2 = vec2( 1.0f, 2.0f ); 411 assert( v2.x == 1.0f ); 412 assert( v2.y == 2.0f ); 413 v2.x = 3.0f; 414 assert( v2.data == [ 3.0f, 2.0f ] ); 415 v2.y = 4.0f; 416 assert( v2.data == [ 3.0f, 4.0f ] ); 417 assert(( v2.x == 3.0f ) && ( v2.x == v2.r ) && ( v2.x == v2.s )); 418 assert( v2.y == 4.0f ); 419 assert(( v2.y == 4.0f ) && ( v2.y == v2.g ) && ( v2.y == v2.t )); 420 v2 = [ 0.0f, 1.0f ]; 421 assert( v2.data == [ 0.0f, 1.0f ] ); 422 //v2.update( vec2( 3.0f, 4.0f )); 423 //assert( v2.data == [ 3.0f, 4.0f ] ); 424 425 vec3 v3 = vec3( 1.0f, 2.0f, 3.0f ); 426 assert( v3.x == 1.0f ); 427 assert( v3.y == 2.0f ); 428 assert( v3.z == 3.0f ); 429 v3.x = 3.0f; 430 assert( v3.data == [ 3.0f, 2.0f, 3.0f ] ); 431 v3.y = 4.0f; 432 assert( v3.data == [ 3.0f, 4.0f, 3.0f ] ); 433 v3.z = 5.0f; 434 assert( v3.data == [ 3.0f, 4.0f, 5.0f ] ); 435 assert(( v3.x == 3.0f ) && ( v3.x == v3.r ) && ( v3.x == v3.s )); 436 assert(( v3.y == 4.0f ) && ( v3.y == v3.g ) && ( v3.y == v3.t )); 437 assert(( v3.z == 5.0f ) && ( v3.z == v3.b ) && ( v3.z == v3.p )); 438 v3 = [ 0.0f, 1.0f, 2.0f ]; 439 assert( v3.data == [ 0.0f, 1.0f, 2.0f ] ); 440 //v3.update( vec3( 3.0f, 4.0f, 5.0f )); 441 //assert( v3.data == [ 3.0f, 4.0f, 5.0f ] ); 442 443 vec4 v4 = vec4( 1.0f, 2.0f, vec2( 3.0f, 4.0f )); 444 assert( v4.x == 1.0f ); 445 assert( v4.y == 2.0f ); 446 assert( v4.z == 3.0f ); 447 assert( v4.w == 4.0f ); 448 v4.x = 3.0f; 449 assert( v4.data == [ 3.0f, 2.0f, 3.0f, 4.0f ] ); 450 v4.y = 4.0f; 451 assert( v4.data == [ 3.0f, 4.0f, 3.0f, 4.0f ] ); 452 v4.z = 5.0f; 453 assert( v4.data == [ 3.0f, 4.0f, 5.0f, 4.0f ] ); 454 v4.w = 6.0f; 455 assert( v4.data == [ 3.0f, 4.0f, 5.0f, 6.0f ] ); 456 assert(( v4.x == 3.0f ) && ( v4.x == v4.r ) && ( v4.x == v4.s )); 457 assert(( v4.y == 4.0f ) && ( v4.y == v4.g ) && ( v4.y == v4.t )); 458 assert(( v4.z == 5.0f ) && ( v4.z == v4.b ) && ( v4.z == v4.p )); 459 assert(( v4.w == 6.0f ) && ( v4.w == v4.a ) && ( v4.w == v4.q )); 460 v4 = [ 0.0f, 1.0f, 2.0f, 3.0f ]; 461 assert( v4.data == [ 0.0f, 1.0f, 2.0f, 3.0f ] ); 462 //v4.update( vec4( 3.0/, 4.0f, 5.0f, 6.0f )); 463 //assert( v4.data == [ 3.0f, 4.0f, 5.0f, 6.0f ] ); 464 } 465 466 /* 467 /// Implements dynamic swizzling. 468 /// Returns: a static valueType array 469 /// TODO : Limit to access sets, characters can be combined within one set only, set1: xyzw, set2: rgba, set3: stpq 470 @property valueType[ s.length ] opDispatch( string s )() const if ( s.length <= dimension ) { 471 valueType[ s.length ] result; 472 dispatchImpl!( 0, s )( result ); 473 return result; 474 } 475 476 void dispatchImpl( int i, string s, int size )( ref valueType[ size ] result ) const { 477 static if( s.length > 0 ) { 478 result[i] = data[ coord_to_index!( s[0] ) ]; 479 dispatchImpl!( i + 1, s[ 1 .. $ ] )( result ); 480 } 481 } 482 */ 483 /// TODO : patch unittest according to access sets !!! 484 unittest { 485 vec2 v2 = vec2( 1.0f, 2.0f ); 486 assert( v2.ts == [ 2.0f, 1.0f ] ); 487 488 assert( vec3( 1.0f, 2.0f, 3.0f ).xyz == [ 1.0f, 2.0f, 3.0f ] ); 489 assert( vec4( v2, 3.0f, 4.0f ).xyzw == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 490 assert( vec4( v2, 3.0f, 4.0f ).wxyz == [ 4.0f, 1.0f, 2.0f, 3.0f ] ); 491 assert( vec4( 1.0f, v2.yx, 2.0f ).data == [ 1.0f, 2.0f, 1.0f, 2.0f ] ); 492 } 493 494 495 /// Returns the euclidean length of the vector 496 @property auto length() const { // Required to overwrite array.length 497 return .length( this ); // .operator calls free module scope length function 498 } 499 500 501 /// TODO : This does not work 502 //XForm.xy += vec2( md.rx, md.ry ); 503 504 /// Negate the vector 505 Vector opUnary( string op : "-" )() const { 506 Vector result; 507 result.data[0] = - data[0]; 508 result.data[1] = - data[1]; 509 static if( dimension >= 3 ) { result.data[2] = - data[2]; } 510 static if( dimension == 4 ) { result.data[3] = - data[3]; } 511 return result; 512 } 513 514 515 /// Unittest OpUnary Negate 516 unittest { 517 assert( vec2( 1.0f, 1.0f ) == -vec2( -1.0f, -1.0f )); 518 assert( vec2( -1.0f, 1.0f ) == -vec2( 1.0f, -1.0f )); 519 520 assert( - vec3( 1.0f, 1.0f, 1.0f ) == vec3( -1.0f, -1.0f, -1.0f )); 521 assert( - vec3( -1.0f, 1.0f, -1.0f ) == vec3( 1.0f, -1.0f, 1.0f )); 522 523 assert( vec4( 1.0f, 1.0f, 1.0f, 1.0f ) == -vec4( -1.0f, -1.0f, -1.0f, -1.0f )); 524 assert( vec4( -1.0f, 1.0f, -1.0f, 1.0f ) == -vec4( 1.0f, -1.0f, 1.0f, -1.0f )); 525 } 526 527 528 /// Componentwise binary vector-skalar operation: addition, subtraction, multiplication, division 529 Vector opBinary( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 530 Vector result; 531 result.data[0] = mixin( "data[0]" ~ op ~ "s" ); 532 result.data[1] = mixin( "data[1]" ~ op ~ "s" ); 533 static if( dimension >= 3 ) { result.data[2] = mixin( "data[2]" ~ op ~ "s" ); } 534 static if( dimension == 4 ) { result.data[3] = mixin( "data[3]" ~ op ~ "s" ); } 535 return result; 536 } 537 538 539 /// Componentwise binary skalar-vector operation: addition, subtraction, multiplication, division 540 auto opBinaryRight( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 541 Vector result; 542 result.data[0] = mixin( "s" ~ op ~ "data[0]" ); 543 result.data[1] = mixin( "s" ~ op ~ "data[1]" ); 544 static if( dimension >= 3 ) { result.data[2] = mixin( "s" ~ op ~ "data[2]" ); } 545 static if( dimension == 4 ) { result.data[3] = mixin( "s" ~ op ~ "data[3]" ); } 546 return result; 547 } 548 549 550 /// Componentwise binary operation with aonther vector: addition, subtraction, multiplication, division 551 Vector opBinary( string op )( Vector v ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 552 Vector result; 553 result.data[0] = mixin( "data[0]" ~ op ~ "v.data[0]" ); 554 result.data[1] = mixin( "data[1]" ~ op ~ "v.data[1]" ); 555 static if( dimension >= 3 ) { result.data[2] = mixin( "data[2]" ~ op ~ "v.data[2]" ); } 556 static if( dimension == 4 ) { result.data[3] = mixin( "data[3]" ~ op ~ "v.data[3]" ); } 557 return result; 558 } 559 560 561 /// Unittest OpBinary 562 unittest { 563 vec2 v2 = vec2( 1.0f, 3.0f ); 564 cast( void )( 2 * v2 ); 565 assert(( v2 * 2.5f ).data == [ 2.5f, 7.5f ] ); 566 assert(( v2 + vec2( 3.0f, 1.0f )).data == [ 4.0f, 4.0f ] ); 567 assert(( v2 - vec2( 1.0f, 3.0f )).data == [ 0.0f, 0.0f ] ); 568 assert(( v2 * vec2( 2.0f, 2.0f )) == vec2( 2.0f, 6.0f )); 569 570 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 571 assert(( v3 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f ] ); 572 assert(( v3 + vec3( 3.0f, 1.0f, - 1.0f )).data == [ 4.0f, 4.0f, 4.0f ] ); 573 assert(( v3 - vec3( 1.0f, 3.0f, 5.0f )).data == [ 0.0f, 0.0f, 0.0f ] ); 574 assert(( v3 * vec3( 2.0f, 2.0f, 2.0f )) == vec3( 2.0f, 6.0f, 10.0f )); 575 576 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 577 assert(( v4 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f, 17.5 ] ); 578 assert(( v4 + vec4( 3.0f, 1.0f, - 1.0f, - 3.0f )).data == [ 4.0f, 4.0f, 4.0f, 4.0f ] ); 579 assert(( v4 - vec4( 1.0f, 3.0f, 5.0f, 7.0f )).data == [ 0.0f, 0.0f, 0.0f, 0.0f ] ); 580 assert(( v4 * vec4( 2.0f, 2.0f, 2.0f, 2.0f )) == vec4( 2.0f, 6.0f, 10.0f, 14.0f )); 581 582 } 583 584 /// Op= Operation with a scalar 585 void opOpAssign( string op )( valueType val ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 586 mixin( "data[0] " ~ op ~ "= val;" ); 587 mixin( "data[1] " ~ op ~ "= val;" ); 588 static if( dimension >= 3 ) mixin( "data[2] " ~ op ~ "= val;" ); 589 static if( dimension == 4 ) mixin( "data[3] " ~ op ~ "= val;" ); 590 } 591 592 /// Componentwise Op= Operation with another vector 593 void opOpAssign( string op )( Vector vec ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 594 mixin( "data[0] " ~ op ~ "= vec.data[0];" ); 595 mixin( "data[1] " ~ op ~ "= vec.data[1];" ); 596 static if( dimension >= 3 ) mixin( "data[2] " ~ op ~ "= vec.data[2];" ); 597 static if( dimension == 4 ) mixin( "data[3] " ~ op ~ "= vec.data[3];" ); 598 } 599 600 unittest { 601 vec2 v2 = vec2( 1.0f, 3.0f ); 602 v2 *= 2.5f; 603 assert( v2.data == [ 2.5f, 7.5f ] ); 604 v2 -= vec2( 2.5f, 7.5f ); 605 assert( v2.data == [ 0.0f, 0.0f ] ); 606 v2 += vec2( 1.0f, 3.0f ); 607 assert( v2.data == [ 1.0f, 3.0f ] ); 608 //assert( almost_equal( v2.normalized, vec2( 1.0f/sqrt( 10.0f ), 3.0f/sqrt( 10.0f )))); 609 610 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 611 v3 *= 2.5f; 612 assert( v3.data == [ 2.5f, 7.5f, 12.5f ] ); 613 v3 -= vec3( 2.5f, 7.5f, 12.5f ); 614 assert( v3.data == [ 0.0f, 0.0f, 0.0f ] ); 615 v3 += vec3( 1.0f, 3.0f, 5.0f ); 616 assert( v3.data == [ 1.0f, 3.0f, 5.0f ] ); 617 //assert( almost_equal( v3.normalized, vec3( 1.0f/sqrt( 35.0f ), 3.0f/sqrt( 35.0f ), 5.0f/sqrt( 35.0f )))); 618 619 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 620 v4 *= 2.5f; 621 assert( v4.data == [ 2.5f, 7.5f, 12.5f, 17.5 ] ); 622 v4 -= vec4( 2.5f, 7.5f, 12.5f, 17.5f ); 623 assert( v4.data == [ 0.0f, 0.0f, 0.0f, 0.0f ] ); 624 v4 += vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 625 assert( v4.data == [ 1.0f, 3.0f, 5.0f, 7.0f ] ); 626 //assert( almost_equal( v4.normalized, vec4( 1.0f/sqrt( 84.0f ), 3.0f/sqrt( 84.0f ), 5.0f/sqrt( 84.0f ), 7.0f/sqrt( 84.0f )))); 627 } 628 629 //void opAssign( A )( A a ) if ( isArray! A ) { 630 // data[] = cast( valueType[] )a[]; 631 //} 632 633 /// Comparisson Operator 634 // const bool opEquals( T )( T vec ) if ( T.dimension == dimension ) { return data == vec.data; } 635 636 /// Unittest Comparisson Operator 637 /* unittest { 638 assert( vec2( 1.0f, 2.0f ) == vec2( 1.0f, 2.0f )); 639 assert( vec2( 1.0f, 2.0f ) != vec2( 1.0f, 1.0f )); 640 assert( vec2( 1.0f, 2.0f ) == vec2d( 1.0, 2.0 )); 641 assert( vec2( 1.0f, 2.0f ) != vec2d( 1.0, 1.0 )); 642 643 assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3( 1.0f, 2.0f, 3.0f )); 644 assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3( 1.0f, 2.0f, 2.0f )); 645 assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3d( 1.0, 2.0, 3.0 )); 646 assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3d( 1.0, 2.0, 2.0 )); 647 648 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4( 1.0f, 2.0f, 3.0f, 4.0f )); 649 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4( 1.0f, 2.0f, 3.0f, 3.0f )); 650 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4d( 1.0, 2.0, 3.0, 4.0 )); 651 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4d( 1.0, 2.0, 3.0, 3.0 )); 652 653 assert( !( vec4( float.nan ))); 654 if ( vec4( 1.0f )) {} 655 else { assert( false ); } 656 }*/ 657 } 658 659 660 /// Vector dot product 661 @safe pure nothrow genType.valueType dot( genType )( in genType a, in genType b ) if ( isVector!genType ) { 662 static if ( !isFloatingPoint!( genType.valueType ) && genType.valueType.sizeof < 32 ) { 663 genType.valueType result = cast( genType.valueType )( a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ] ); 664 static if ( genType.dimension >= 3 ) { result += cast( genType.valueType )( a.data[ 2 ] * b.data[ 2 ] ); } 665 static if ( genType.dimension == 4 ) { result += cast( genType.valueType )( a.data[ 3 ] * b.data[ 3 ] ); } 666 return result; 667 } else { 668 genType.valueType result = a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ]; 669 static if ( genType.dimension >= 3 ) { result += a.data[ 2 ] * b.data[ 2 ]; } 670 static if ( genType.dimension == 4 ) { result += a.data[ 3 ] * b.data[ 3 ]; } 671 return result; 672 } 673 } 674 675 676 /// Vector cross product 677 @safe pure nothrow genType cross( genType )( in genType a, in genType b ) if ( isVector!genType && ( genType.dimension == 3 )) { 678 return genType( a.y * b.z - b.y * a.z, 679 a.z * b.x - b.z * a.x, 680 a.x * b.y - b.x * a.y ); 681 } 682 683 684 /// Vector length floating point valueType 685 @safe pure nothrow auto length( genType )( in genType v ) if ( isVector!genType ) { 686 static if ( isFloatingPoint!( genType.valueType )) return sqrt( dot( v, v )); 687 else return sqrt( cast( real )dot( v, v )); 688 } 689 690 691 /// Vector normalize 692 @safe pure nothrow genType normalize( genType )( in genType v ) if ( isVector!genType ) { 693 auto l = v.length; 694 if ( l == 0 ) return v; 695 auto invLength = 1.0 / l; 696 genType result = v; 697 result.data[0] *= invLength; 698 result.data[1] *= invLength; 699 static if( genType.dimension >= 3 ) { result.data[2] *= invLength; } 700 static if( genType.dimension == 4 ) { result.data[3] *= invLength; } 701 return result; 702 } 703 704 705 /// Distance between two vectors 706 @safe pure nothrow genType.valueType distance( genType )( const genType a, const genType b ) if ( isVector!genType ) { 707 return length( a - b ); 708 } 709 710 711 /// Flip the Vector N based on an incident vector I and a reference Vector Nref 712 @safe pure nothrow genType faceforward( genType )( in genType N, in genType I, in genType Nref ) if ( isVector!genType ) { 713 return dot( Nref, I ) < 0 ? N : -N; 714 } 715 716 717 /// Reflect the Vector I on a plane with normal N 718 /// The normal N must already to be normalized 719 @safe pure nothrow genType reflect( genType )( in genType I, in genType N ) if ( isVector!genType ) { 720 return I - 2 * dot( N, I ) * N; 721 } 722 723 724 /// For the incident vector I and surface normal N, and the ratio of indices of refraction eta, return the refraction vector 725 /// The input parameters for the incident vector I and the surface normal N must already be normalized 726 @safe pure nothrow genType.valueType refract( genType )( genType I, genType N, genType.valueType eta ) if ( isVector!genType ) { 727 auto dotNI = dot( N, I ); 728 auto k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI ); 729 if ( k < 0.0 ) return 0.0; 730 return eta * I - ( eta * dotNI + sqrt( k )) * N; 731 } 732 733 734 /// Unittest Geometric functions 735 /// TODO : add tests for faceforward, reflect and refract 736 unittest { 737 738 // dot 739 vec2 v2 = vec2( 1.0f, 3.0f ); 740 assert( dot( v2, vec2( 2.0f, 2.0f )) == 8.0f ); 741 742 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 743 assert( dot( v3, vec3( 2.0f, 2.0f, 2.0f )) == 18.0f ); 744 745 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 746 assert( dot( v4, vec4( 2.0f, 2.0f, 2.0f, 2.0f )) == 32.0f ); 747 748 vec3 v3_1 = vec3( 1.0f, 2.0f, -3.0f ); 749 vec3 v3_2 = vec3( 1.0f, 3.0f, 2.0f ); 750 751 assert( dot( v3_1, v3_2 ) == 1.0f ); 752 assert( dot( v3_1, v3_2 ) == dot( v3_2, v3_1 )); 753 assert( v3_1 * v3_2 == v3_1 * v3_2 ); 754 assert( v3_1 * v3_2 == vec3( 1.0f, 6.0f, -6.0f )); 755 756 // cross 757 assert( cross( v3_1, v3_2 ).data == [ 13.0f, -5.0f, 1.0f ] ); 758 assert( cross( v3_2, v3_1 ).data == [ -13.0f, 5.0f, -1.0f ] ); 759 760 // normalize 761 assert( normalize( vec2( 1 )) == [ 1.0f / sqrt( 2.0f ), 1.0f / sqrt( 2.0f ) ] ); 762 assert( vec3( 1 ).normalize == [ 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ) ] ); 763 assert( normalize( vec4( 1 )) == [ 0.5, 0.5, 0.5, 0.5 ] ); 764 765 // length 766 assert( length( v2 ) == sqrt( 10.0f )); 767 assert( v3.length == sqrt( 35.0f )); 768 assert( length( v4 ) == sqrt( 84.0f )); 769 770 // distance 771 assert( distance( vec2( 0.0f, 0.0f ), vec2( 0.0f, 10.0f )) == 10.0 ); 772 } 773 774 /// Pre-defined vector types 775 alias Vector!( float, 2 ) vec2; 776 alias Vector!( float, 3 ) vec3; 777 alias Vector!( float, 4 ) vec4; 778 779 alias Vector!( double, 2 ) dvec2; 780 alias Vector!( double, 3 ) dvec3; 781 alias Vector!( double, 4 ) dvec4; 782 783 alias Vector!( int, 2 ) ivec2; 784 alias Vector!( int, 3 ) ivec3; 785 alias Vector!( int, 4 ) ivec4; 786 787 alias Vector!( uint, 2 ) uvec2; 788 alias Vector!( uint, 3 ) uvec3; 789 alias Vector!( uint, 4 ) uvec4; 790 791 alias Vector!( byte, 2 ) ivec2b; 792 alias Vector!( byte, 3 ) ivec3b; 793 alias Vector!( byte, 4 ) ivec4b; 794 795 alias Vector!( ubyte, 2 ) uvec2b; 796 alias Vector!( ubyte, 3 ) uvec3b; 797 alias Vector!( ubyte, 4 ) uvec4b; 798 799 alias Vector!( short, 2 ) ivec2s; 800 alias Vector!( short, 3 ) ivec3s; 801 alias Vector!( short, 4 ) ivec4s; 802 803 alias Vector!( ushort, 2 ) uvec2s; 804 alias Vector!( ushort, 3 ) uvec3s; 805 alias Vector!( ushort, 4 ) uvec4s; 806 807 808 809 // this is another variant based on opDispatch, source: http://www.mmartins.me/view/2015/9/27/vector-swizzle-in-d 810 // drawback is that it is always recompiled if any swizzle operator changes, implementation at bottom of file 811 /* private enum vec_swizz_get_xyzw = "xyzw"; 812 private enum vec_swizz_get_rgba = "rgba"; 813 private enum vec_swizz_get_stpq = "stpq"; 814 @property auto opDispatch(string swizzle)() if (swizzle.length > 0 && swizzle.length < 5) { 815 import std.string : indexOf, join; 816 817 static if (swizzle.length == 1) { 818 pragma( msg, "length = 1" ); 819 static if (vec_swizz_get_xyzw.indexOf( swizzle[0] ) >= 0 ) 820 enum index = vec_swizz_get_xyzw.indexOf( swizzle[0] ); 821 else static if (vec_swizz_get_rgba.indexOf( swizzle[0] ) >= 0 ) 822 enum index = vec_swizz_get_rgba.indexOf( swizzle[0] ); 823 else static if (vec_swizz_get_stpq.indexOf( swizzle[0] ) >= 0 ) 824 enum index = vec_swizz_get_stpq.indexOf( swizzle[0] ); 825 else { 826 char[128] formatBuffer; 827 //auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ); 828 static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) ); 829 } 830 return data[index]; 831 } else { 832 import std.conv : to; 833 import std.array : array; 834 import std.algorithm : map; 835 pragma( msg, "length > 1, ", swizzle ); 836 static if (vec_swizz_get_xyzw.indexOf(swizzle[0]) >= 0) { 837 pragma( msg, vec_swizz_get_xyzw ); 838 enum indices = swizzle.map!(x => vec_swizz_get_xyzw.indexOf(x)).array; 839 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 840 pragma( msg, to!string( indices ) ); 841 return mixin(args); 842 } else static if(vec_swizz_get_rgba.indexOf(swizzle[0]) >= 0) { 843 pragma( msg, vec_swizz_get_rgba ); 844 enum indices = swizzle.map!(x => vec_swizz_get_rgba.indexOf(x)).array; 845 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 846 return mixin(args); 847 } else static if(vec_swizz_get_stpq.indexOf(swizzle[0]) >= 0) { 848 pragma( msg, vec_swizz_get_stpq ); 849 enum indices = swizzle.map!(x => vec_swizz_get_stpq.indexOf(x)).array; 850 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 851 return mixin(args); 852 } else { 853 char[128] formatBuffer; 854 //auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ); 855 static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) ); 856 } 857 858 //pragma( msg, args ); 859 //return mixin(args); 860 } 861 } 862 863 */