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