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.traits : isFloatingPoint, isNumeric; 16 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 69 nothrow @nogc: 70 71 /// Returns the current vector formatted as char[] slice of the passed in buffer excluding terminating \0. 72 /// The terminator is still part of the buffer to be able to use write(ln) as well as printf using buffer.ptr 73 char[] toString( char[] buffer ) { 74 assert( buffer.length >= dim * 16, "At least dimension * 16 chars buffer capacity required!" ); 75 import core.stdc.stdio : sprintf; 76 77 static if( __traits( isFloating, type )) { 78 static if( dim == 2 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %f, %f ]", x, y ) ]; 79 else static if( dim == 3 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %f, %f, %f ]", x, y, z ) ]; 80 else static if( dim == 4 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %f, %f, %f, %f ]", x, y, z, w ) ]; 81 } 82 else static if( __traits( isUnsigned, type )) { 83 static if( dim == 2 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %u, %u ]", x, y ) ]; 84 else static if( dim == 3 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %u, %u, %u ]", x, y, z ) ]; 85 else static if( dim == 4 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %u, %u, %u, %u ]", x, y, z, w ) ]; 86 } 87 else static if( __traits( isIntegral, type )) { 88 static if( dim == 2 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %i, %i ]", x, y ) ]; 89 else static if( dim == 3 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %i, %i, %i ]", x, y, z ) ]; 90 else static if( dim == 4 ) return buffer[ 0 .. sprintf( buffer.ptr, "[ %i, %i, %i, %i ]", x, y, z, w ) ]; 91 } 92 } 93 94 95 pure nothrow @nogc: 96 97 /// Returns a pointer to the coordinates 98 auto ptr() { 99 return data.ptr; 100 } 101 102 103 pure nothrow @nogc @safe: 104 105 union { /// access array data with vector properties 106 type[ dim ] data; /// Holds all coordinates, length conforms dimension 107 struct { /// access via x, y, z, w 108 type x, y; 109 static if( dim >= 3 ) type z; 110 static if( dim == 4 ) type w; 111 } 112 struct { /// access via r, g, b, a 113 type r, g; 114 static if( dim >= 3 ) type b; 115 static if( dim == 4 ) type a; 116 } 117 struct { /// access via s, t, p, q 118 type s, t; 119 static if( dim >= 3 ) type p; 120 static if( dim == 4 ) type q; 121 } 122 } 123 alias data this; /// The Vector can be treated as an array 124 alias dimension = dim; /// Holds the dimension of the vector 125 alias type valueType; /// Holds the internal type of the vector 126 127 128 // Unittest Construction via aliased this static array 129 unittest { 130 vec2 v2 = [ 0.0f, 1.0f ]; 131 assert( v2[ 0 ] == 0.0f ); 132 assert( v2[ 1 ] == 1.0f ); 133 134 v2[ 0 .. 1 ] = [ 2.0 ]; 135 assert( v2 == [ 2.0f, 1.0f ] ); 136 137 vec3 v3 = vec3( 0.0f, 0.1f, 0.2f ); 138 assert( v3[ 0 ] == 0.0f ); 139 assert( v3[ 1 ] == 0.1f ); 140 assert( v3[ 2 ] == 0.2f ); 141 142 v3[ 0 .. $ ] = 0.0f; 143 assert( v3 == [ 0.0f, 0.0f, 0.0f ] ); 144 145 vec4 v4 = vec4( 0.0f, 0.1f, 0.2f, 0.3f ); 146 assert( v4[ 0 ] == 0.0f ); 147 assert( v4[ 1 ] == 0.1f ); 148 assert( v4[ 2 ] == 0.2f ); 149 assert( v4[ 3 ] == 0.3f ); 150 151 v4[ 1 .. 3 ] = [ 1.0f, 2.0f ]; 152 assert( v4 == [ 0.0f, 1.0f, 2.0f, 0.3f ] ); 153 } 154 155 template isCompatibleVector( T ) { enum isCompatibleVector = is( typeof( isCompatibleVectorImpl( T.init ))); } 156 static void isCompatibleVectorImpl( vt, int dim )( Vector!( vt, dim ) vec ) if( dim <= dimension ) {} // implicit valueType conversion 157 158 /// Unittest : isCompatibleType 159 unittest { 160 // isCompatibleType without implicit valueType conversion 161 //vec2 v2; assert( v2.isCompatibleVector!vec2 ); assert( !v2.isCompatibleVector!vec3 ); assert( !v2.isCompatibleVector!vec4 ); 162 //vec3 v3; assert( v3.isCompatibleVector!vec2 ); assert( v3.isCompatibleVector!vec3 ); assert( !v3.isCompatibleVector!vec4 ); 163 //vec4 v4; assert( v4.isCompatibleVector!vec2 ); assert( v4.isCompatibleVector!vec3 ); assert( v4.isCompatibleVector!vec4 ); 164 165 // isCompatibleType with implicit valueType conversion 166 vec2 v2; assert( v2.isCompatibleVector!vec2i ); assert( !v2.isCompatibleVector!vec3d ); assert( !v2.isCompatibleVector!vec4 ); 167 vec3i v3; assert( v3.isCompatibleVector!vec2d ); assert( v3.isCompatibleVector!vec3 ); assert( !v3.isCompatibleVector!vec4i ); 168 vec4d v4; assert( v4.isCompatibleVector!vec2 ); assert( v4.isCompatibleVector!vec3i ); assert( v4.isCompatibleVector!vec4d ); 169 } 170 171 172 /// Helper method to recursivly construct a vector from an argument list with different types 173 /// TODO: Fix Construction with combination of numeric, vector, static and dynamic array 174 private void construct( int i, T, Tail... )( T head, Tail tail ) { 175 import std.traits : isDynamicArray, isStaticArray; 176 static if ( i >= dimension ) { 177 static assert( false, "Constructor has too many arguments!" ); 178 } else static if ( is( T : valueType )) { 179 data[i] = head; 180 construct!( i + 1 )( tail ); 181 } else static if ( isDynamicArray!T ) { 182 version( D_BetterC ) { 183 pragma( msg, "\nUsing -betterC, it's not possible to initialize from (statically defined) dynamic arrays!\n" ); 184 } else { 185 static assert(( Tail.length == 0 ) && ( i == 0 ), "dynamic array can not be passed together with other arguments" ); 186 static if( is( type == typeof( head[0] ))) data = head; 187 else foreach( j; 0 .. head.length ) data[ i + j ] = head[ j ]; 188 } 189 } else static if( isStaticArray!T ) { 190 static if( is( type == typeof( head[0] ))) { 191 data[ i .. i + T.length ] = head; 192 construct!( i + T.length )( tail ); 193 } else { 194 foreach( j; 0 .. T.length ) 195 data[ i + j ] = head[ j ]; 196 construct!( i + T.length )( tail ); 197 } 198 construct!( i + T.length )( tail ); 199 } else static if ( isCompatibleVector!T ) { 200 data[ i .. i + T.dimension ] = head.data; 201 construct!( i + T.dimension )( tail ); 202 } else { 203 //char[128] formatBuffer; 204 //import core.stdc.stdio : sprintf; 205 //auto formatString = formatBuffer.ptr.sprintf( "Vector constructor argument must be of type %s or Vector, not %s", valueType.stringof, T.stringof ); 206 pragma( msg, "Vector constructor argument must be of type ", valueType, " or Vector, but not ", T ); 207 static assert( false ); 208 } 209 } 210 211 private void construct( int i )() {} // terminate 212 213 /// TODO: Unittest: Construction with combination of numeric, vector, static and dynamic array 214 unittest { 215 auto v2 = vec2( 2.0f, 1 ); 216 } 217 218 219 /// Constructs the vector 220 /// If a single value is passed the vector, the vector will be cleared with this value 221 /// If a vector with a higher dimension is passed the vector will hold the first values up to its dimension 222 /// If mixed types are passed they will be joined together ( allowed types: vector, static array, $( I vt )) 223 /// Examples: 224 /// --- 225 /// vec4 v4 = vec4( 1.0f, vec2( 2.0f, 3.0f ), 4.0f ); 226 /// vec3 v3 = vec3( v4 ); // v3 = vec3( 1.0f, 2.0f, 3.0f ); 227 /// vec2 v2 = v3.xy; // swizzling returns a static array. 228 /// vec3 v3_2 = vec3( 1.0f ); // vec3 v3_2 = vec3( 1.0f, 1.0f, 1.0f ); 229 /// --- 230 this( Args... )( Args args ) { construct!( 0 )( args ); } 231 232 233 /// Construct a Vector from another Vector 234 this( V )( V vec ) if ( isVector!V && ( V.dimension >= dimension )) { 235 auto minDimension = dimension < V.dimension ? dimension : V.dimension; 236 static if( __traits( compiles, data = vec.data[ 0 .. minDimension ] )) { 237 data = vec.data[ 0 .. dimension ]; 238 } else { 239 foreach( i; 0 .. minDimension ) { 240 data[i] = cast( valueType )vec.data[i]; 241 } 242 } 243 } 244 245 246 /// Unittest: Construct a Vector from another Vector 247 unittest { 248 auto v4 = vec4( 1.0f, 2, 3, 4 ); 249 auto v3 = vec3( v4 ); 250 assert( v3 == [ 1, 2, 3 ] ); 251 252 auto v2 = vec2( v4 ); 253 assert( v2 == [ 1, 2 ] ); 254 255 /// Different valueTypes 256 auto v4i = vec4i( 1, 2, 3, 4 ); 257 auto v3d = vec3d( v4i ); 258 assert( v3d == [ 1, 2, 3 ] ); 259 260 v3d.y = 3.9; 261 auto v2i = vec2i( v3d ); 262 assert( v2i == [ 1, 3 ] ); 263 } 264 265 266 /// Construct a Vector from a single value 267 this()( valueType value ) { 268 data[] = value; 269 } 270 271 272 /// Unittest Construct a Vector from a single value 273 unittest { 274 auto v2 = vec2( 2 ); 275 assert( v2 == [ 2, 2 ] ); 276 277 auto v3i = vec3i( 3 ); 278 assert( v3i == [ 3, 3, 3 ] ); 279 280 auto v4ub = vec4ub( 4 ); 281 assert( v4ub == [ 4, 4, 4, 4 ] ); 282 } 283 284 285 /// FloatingPoint valueType, Returns true if all values are not nan and finite, otherwise false 286 static if ( isFloatingPoint!valueType ) { 287 bool opCast( T : bool )() const { return ok; } 288 import std.math : isNaN, isInfinity; 289 bool ok() const { 290 foreach( ref val; data ) { 291 if ( isNaN( val ) || isInfinity( val )) return false; 292 } 293 return true; 294 } 295 } 296 297 298 /// Sets all values of the vector to value 299 void clear( valueType value ) { 300 data[] = value; 301 } 302 303 304 /// Unittest Construction 305 /// TODO: Split up these Unittests 306 unittest { 307 vec3 vecClear; 308 assert( !vecClear.ok ); 309 vecClear.clear( 1.0f ); 310 assert( vecClear.ok ); 311 assert( vecClear.data == [ 1.0f, 1.0f, 1.0f ] ); 312 assert( vecClear.data == vec3( 1.0f ).data ); 313 vecClear.clear( float.infinity ); 314 assert( !vecClear.ok ); 315 vecClear.clear( float.nan ); 316 assert( !vecClear.ok ); 317 vecClear.clear( 1.0f ); 318 assert( vecClear.ok ); 319 320 vec4 b = vec4( 1.0f, vecClear ); 321 assert( b.ok ); 322 assert( b.data == [ 1.0f, 1.0f, 1.0f, 1.0f ] ); 323 assert( b.data == vec4( 1.0f ).data ); 324 325 vec2 v2_1 = vec2( vec2( 0.0f, 1.0f )); 326 assert( v2_1.data == [ 0.0f, 1.0f ] ); 327 328 vec2 v2_2 = vec2( 1.0f, 1.0f ); 329 assert( v2_2.data == [ 1.0f, 1.0f ] ); 330 331 vec3 v3 = vec3( v2_1, 2.0f ); 332 assert( v3.data == [ 0.0f, 1.0f, 2.0f ] ); 333 334 vec4 v4_1 = vec4( 1.0f, vec2( 2.0f, 3.0f ), 4.0f ); 335 assert( v4_1.data == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 336 assert( vec3( v4_1 ).data == [ 1.0f, 2.0f, 3.0f ] ); 337 assert( vec2( vec3( v4_1 )).data == [ 1.0f, 2.0f ] ); 338 assert( vec2( vec3( v4_1 )).data == vec2( v4_1 ).data ); 339 assert( v4_1.data == vec4( [ 1.0f, 2.0f, 3.0f, 4.0f ] ).data ); 340 341 vec4 v4_2 = vec4( vec2( 1.0f, 2.0f ), vec2( 3.0f, 4.0f )); 342 assert( v4_2.data == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 343 assert( vec3( v4_2 ).data == [ 1.0f, 2.0f, 3.0f ] ); 344 assert( vec2( vec3( v4_2 )).data == [ 1.0f, 2.0f ] ); 345 assert( vec2( vec3( v4_2 )).data == vec2( v4_2 ).data ); 346 assert( v4_2.data == vec4([ 1.0f, 2.0f, 3.0f, 4.0f ] ).data ); 347 348 float[2] f2 = [ 1.0f, 2.0f ]; 349 float[3] f3 = [ 1.0f, 2.0f, 3.0f ]; 350 float[4] f4 = [ 1.0f, 2.0f, 3.0f, 4.0f ]; 351 assert( vec2( 1.0f, 2.0f ).data == vec2(f2).data); 352 assert( vec3( 1.0f, 2.0f, 3.0f ).data == vec3( f3 ).data ); 353 assert( vec3( 1.0f, 2.0f, 3.0f ).data == vec3( f2, 3.0f ).data ); 354 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f4 ).data ); 355 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f3, 4.0f ).data ); 356 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ).data == vec4( f2, 3.0f, 4.0f ).data ); 357 // useful for: "vec4 v4 = […]" or "vec4 v4 = other_vector.rgba" 358 359 assert( vec3( vec3i( 1, 2, 3 )) == vec3( 1.0, 2.0, 3.0 )); 360 assert( vec3d( vec3( 1.0, 2.0, 3.0 )) == vec3d( 1.0, 2.0, 3.0 )); 361 } 362 363 364 // another swizzle variant based on opDispatch, source: https://github.com/d-gamedev-team/gfm/blob/master/math/gfm/math/vector.d#L238 365 // drawback is that lib is always recompiled if any swizzle operator changes, implementation at bottom of file 366 367 368 369 370 // Unittest swizzle setter 371 unittest { 372 vec2 v2 = vec2( 1 ); 373 v2.x = 0; 374 assert( v2 == [ 0, 1 ] ); 375 376 vec3 v3 = vec3( 0 ); 377 //v3.yx = [ 1, 2 ]; 378 v3.yx = vec2( 1, 2 ); 379 assert( v3 == [ 2, 1, 0 ] ); 380 v3.zx = vec2( 2, 0 ); 381 assert( v3 == [ 0, 1, 2 ] ); 382 v3.zyx = vec3( 3, 2, 1 ); 383 assert( v3 == [ 1, 2, 3 ] ); 384 385 vec4 v4 = vec4( 0 ); 386 //v3.yx = [ 1, 2 ]; 387 v4.wx = vec2( 1, 2 ); 388 assert( v4 == [ 2, 0, 0, 1 ] ); 389 v4.zx = vec2( 2, 0 ); 390 assert( v4 == [ 0, 0, 2, 1 ] ); 391 v4.zyx = vec3( 3, 2, 1 ); 392 assert( v4 == [ 1, 2, 3, 1 ] ); 393 } 394 395 // Unittest swizzle getter 396 unittest { 397 vec2 v2 = vec2( 1 ); 398 assert( v2 == v2.xy ); 399 v2.x = 0; 400 assert( v2 == [ 0, 1 ] ); 401 assert( v2.x == 0 ); 402 403 vec3 v3 = vec3( 0 ); 404 v3.yx = vec2( 1, 2 ); 405 //v3.yx = [ 1, 2 ]; 406 assert( v3 == [ 2, 1, 0 ] ); 407 } 408 409 /// Unittest for setting Values 410 unittest { 411 vec2 v2 = vec2( 1.0f, 2.0f ); 412 assert( v2.x == 1.0f ); 413 assert( v2.y == 2.0f ); 414 v2.x = 3.0f; 415 assert( v2.data == [ 3.0f, 2.0f ] ); 416 v2.y = 4.0f; 417 assert( v2.data == [ 3.0f, 4.0f ] ); 418 assert(( v2.x == 3.0f ) && ( v2.x == v2.r ) && ( v2.x == v2.s )); 419 assert( v2.y == 4.0f ); 420 assert(( v2.y == 4.0f ) && ( v2.y == v2.g ) && ( v2.y == v2.t )); 421 v2 = [ 0.0f, 1.0f ]; 422 assert( v2.data == [ 0.0f, 1.0f ] ); 423 //v2.update( vec2( 3.0f, 4.0f )); 424 //assert( v2.data == [ 3.0f, 4.0f ] ); 425 426 vec3 v3 = vec3( 1.0f, 2.0f, 3.0f ); 427 assert( v3.x == 1.0f ); 428 assert( v3.y == 2.0f ); 429 assert( v3.z == 3.0f ); 430 v3.x = 3.0f; 431 assert( v3.data == [ 3.0f, 2.0f, 3.0f ] ); 432 v3.y = 4.0f; 433 assert( v3.data == [ 3.0f, 4.0f, 3.0f ] ); 434 v3.z = 5.0f; 435 assert( v3.data == [ 3.0f, 4.0f, 5.0f ] ); 436 assert(( v3.x == 3.0f ) && ( v3.x == v3.r ) && ( v3.x == v3.s )); 437 assert(( v3.y == 4.0f ) && ( v3.y == v3.g ) && ( v3.y == v3.t )); 438 assert(( v3.z == 5.0f ) && ( v3.z == v3.b ) && ( v3.z == v3.p )); 439 v3 = [ 0.0f, 1.0f, 2.0f ]; 440 assert( v3.data == [ 0.0f, 1.0f, 2.0f ] ); 441 //v3.update( vec3( 3.0f, 4.0f, 5.0f )); 442 //assert( v3.data == [ 3.0f, 4.0f, 5.0f ] ); 443 444 vec4 v4 = vec4( 1.0f, 2.0f, vec2( 3.0f, 4.0f )); 445 assert( v4.x == 1.0f ); 446 assert( v4.y == 2.0f ); 447 assert( v4.z == 3.0f ); 448 assert( v4.w == 4.0f ); 449 v4.x = 3.0f; 450 assert( v4.data == [ 3.0f, 2.0f, 3.0f, 4.0f ] ); 451 v4.y = 4.0f; 452 assert( v4.data == [ 3.0f, 4.0f, 3.0f, 4.0f ] ); 453 v4.z = 5.0f; 454 assert( v4.data == [ 3.0f, 4.0f, 5.0f, 4.0f ] ); 455 v4.w = 6.0f; 456 assert( v4.data == [ 3.0f, 4.0f, 5.0f, 6.0f ] ); 457 assert(( v4.x == 3.0f ) && ( v4.x == v4.r ) && ( v4.x == v4.s )); 458 assert(( v4.y == 4.0f ) && ( v4.y == v4.g ) && ( v4.y == v4.t )); 459 assert(( v4.z == 5.0f ) && ( v4.z == v4.b ) && ( v4.z == v4.p )); 460 assert(( v4.w == 6.0f ) && ( v4.w == v4.a ) && ( v4.w == v4.q )); 461 v4 = [ 0.0f, 1.0f, 2.0f, 3.0f ]; 462 assert( v4.data == [ 0.0f, 1.0f, 2.0f, 3.0f ] ); 463 //v4.update( vec4( 3.0/, 4.0f, 5.0f, 6.0f )); 464 //assert( v4.data == [ 3.0f, 4.0f, 5.0f, 6.0f ] ); 465 } 466 467 468 /// TODO : patch unittest according to access sets !!! 469 unittest { 470 vec2 v2 = vec2( 1.0f, 2.0f ); 471 assert( v2.ts == [ 2.0f, 1.0f ] ); 472 473 assert( vec3( 1.0f, 2.0f, 3.0f ).xyz == [ 1.0f, 2.0f, 3.0f ] ); 474 assert( vec4( v2, 3.0f, 4.0f ).xyzw == [ 1.0f, 2.0f, 3.0f, 4.0f ] ); 475 assert( vec4( v2, 3.0f, 4.0f ).wxyz == [ 4.0f, 1.0f, 2.0f, 3.0f ] ); 476 assert( vec4( 1.0f, v2.yx, 2.0f ).data == [ 1.0f, 2.0f, 1.0f, 2.0f ] ); 477 } 478 479 480 /// Returns the euclidean length of the vector 481 auto length() const { // Required to overwrite array.length 482 return .length( this ); // .operator calls free module scope length function 483 } 484 485 486 /// TODO : This does not work 487 //XForm.xy += vec2( md.rx, md.ry ); 488 489 /// Negate the vector 490 Vector opUnary( string op : "-" )() const { 491 Vector result; 492 result.data[0] = - data[0]; 493 result.data[1] = - data[1]; 494 static if( dimension >= 3 ) { result.data[2] = - data[2]; } 495 static if( dimension == 4 ) { result.data[3] = - data[3]; } 496 return result; 497 } 498 499 500 /// Unittest OpUnary Negate 501 unittest { 502 assert( vec2( 1.0f, 1.0f ) == -vec2( -1.0f, -1.0f )); 503 assert( vec2( -1.0f, 1.0f ) == -vec2( 1.0f, -1.0f )); 504 505 assert( - vec3( 1.0f, 1.0f, 1.0f ) == vec3( -1.0f, -1.0f, -1.0f )); 506 assert( - vec3( -1.0f, 1.0f, -1.0f ) == vec3( 1.0f, -1.0f, 1.0f )); 507 508 assert( vec4( 1.0f, 1.0f, 1.0f, 1.0f ) == -vec4( -1.0f, -1.0f, -1.0f, -1.0f )); 509 assert( vec4( -1.0f, 1.0f, -1.0f, 1.0f ) == -vec4( 1.0f, -1.0f, 1.0f, -1.0f )); 510 } 511 512 513 /// Componentwise binary vector-skalar operation: addition, subtraction, multiplication, division 514 Vector opBinary( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 515 Vector result; 516 result.data[0] = mixin( "data[0]" ~ op ~ "s" ); 517 result.data[1] = mixin( "data[1]" ~ op ~ "s" ); 518 static if( dimension >= 3 ) { result.data[2] = mixin( "data[2]" ~ op ~ "s" ); } 519 static if( dimension == 4 ) { result.data[3] = mixin( "data[3]" ~ op ~ "s" ); } 520 return result; 521 } 522 523 524 /// Componentwise binary skalar-vector operation: addition, subtraction, multiplication, division 525 auto opBinaryRight( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 526 Vector result; 527 result.data[0] = mixin( "s" ~ op ~ "data[0]" ); 528 result.data[1] = mixin( "s" ~ op ~ "data[1]" ); 529 static if( dimension >= 3 ) { result.data[2] = mixin( "s" ~ op ~ "data[2]" ); } 530 static if( dimension == 4 ) { result.data[3] = mixin( "s" ~ op ~ "data[3]" ); } 531 return result; 532 } 533 534 535 /// Componentwise binary operation with aonther vector: addition, subtraction, multiplication, division 536 Vector opBinary( string op )( Vector v ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 537 Vector result; 538 result.data[0] = mixin( "data[0]" ~ op ~ "v.data[0]" ); 539 result.data[1] = mixin( "data[1]" ~ op ~ "v.data[1]" ); 540 static if( dimension >= 3 ) { result.data[2] = mixin( "data[2]" ~ op ~ "v.data[2]" ); } 541 static if( dimension == 4 ) { result.data[3] = mixin( "data[3]" ~ op ~ "v.data[3]" ); } 542 return result; 543 } 544 545 546 /// Unittest OpBinary 547 unittest { 548 vec2 v2 = vec2( 1.0f, 3.0f ); 549 cast( void )( 2 * v2 ); 550 assert(( v2 * 2.5f ).data == [ 2.5f, 7.5f ] ); 551 assert(( v2 + vec2( 3.0f, 1.0f )).data == [ 4.0f, 4.0f ] ); 552 assert(( v2 - vec2( 1.0f, 3.0f )).data == [ 0.0f, 0.0f ] ); 553 assert(( v2 * vec2( 2.0f, 2.0f )) == vec2( 2.0f, 6.0f )); 554 555 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 556 assert(( v3 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f ] ); 557 assert(( v3 + vec3( 3.0f, 1.0f, - 1.0f )).data == [ 4.0f, 4.0f, 4.0f ] ); 558 assert(( v3 - vec3( 1.0f, 3.0f, 5.0f )).data == [ 0.0f, 0.0f, 0.0f ] ); 559 assert(( v3 * vec3( 2.0f, 2.0f, 2.0f )) == vec3( 2.0f, 6.0f, 10.0f )); 560 561 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 562 assert(( v4 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f, 17.5 ] ); 563 assert(( v4 + vec4( 3.0f, 1.0f, - 1.0f, - 3.0f )).data == [ 4.0f, 4.0f, 4.0f, 4.0f ] ); 564 assert(( v4 - vec4( 1.0f, 3.0f, 5.0f, 7.0f )).data == [ 0.0f, 0.0f, 0.0f, 0.0f ] ); 565 assert(( v4 * vec4( 2.0f, 2.0f, 2.0f, 2.0f )) == vec4( 2.0f, 6.0f, 10.0f, 14.0f )); 566 } 567 568 569 /// Op= Operation with a scalar 570 void opOpAssign( string op )( valueType val ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 571 mixin( "data[0] " ~ op ~ "= val;" ); 572 mixin( "data[1] " ~ op ~ "= val;" ); 573 static if( dimension >= 3 ) mixin( "data[2] " ~ op ~ "= val;" ); 574 static if( dimension == 4 ) mixin( "data[3] " ~ op ~ "= val;" ); 575 } 576 577 /// Componentwise Op= Operation with another vector 578 void opOpAssign( string op )( Vector vec ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" )) { 579 mixin( "data[0] " ~ op ~ "= vec.data[0];" ); 580 mixin( "data[1] " ~ op ~ "= vec.data[1];" ); 581 static if( dimension >= 3 ) mixin( "data[2] " ~ op ~ "= vec.data[2];" ); 582 static if( dimension == 4 ) mixin( "data[3] " ~ op ~ "= vec.data[3];" ); 583 } 584 585 unittest { 586 vec2 v2 = vec2( 1.0f, 3.0f ); 587 v2 *= 2.5f; 588 assert( v2.data == [ 2.5f, 7.5f ] ); 589 v2 -= vec2( 2.5f, 7.5f ); 590 assert( v2.data == [ 0.0f, 0.0f ] ); 591 v2 += vec2( 1.0f, 3.0f ); 592 assert( v2.data == [ 1.0f, 3.0f ] ); 593 //assert( almost_equal( v2.normalized, vec2( 1.0f/sqrt( 10.0f ), 3.0f/sqrt( 10.0f )))); 594 595 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 596 v3 *= 2.5f; 597 assert( v3.data == [ 2.5f, 7.5f, 12.5f ] ); 598 v3 -= vec3( 2.5f, 7.5f, 12.5f ); 599 assert( v3.data == [ 0.0f, 0.0f, 0.0f ] ); 600 v3 += vec3( 1.0f, 3.0f, 5.0f ); 601 assert( v3.data == [ 1.0f, 3.0f, 5.0f ] ); 602 //assert( almost_equal( v3.normalized, vec3( 1.0f/sqrt( 35.0f ), 3.0f/sqrt( 35.0f ), 5.0f/sqrt( 35.0f )))); 603 604 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 605 v4 *= 2.5f; 606 assert( v4.data == [ 2.5f, 7.5f, 12.5f, 17.5 ] ); 607 v4 -= vec4( 2.5f, 7.5f, 12.5f, 17.5f ); 608 assert( v4.data == [ 0.0f, 0.0f, 0.0f, 0.0f ] ); 609 v4 += vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 610 assert( v4.data == [ 1.0f, 3.0f, 5.0f, 7.0f ] ); 611 //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 )))); 612 } 613 614 //void opAssign( A )( A a ) if ( isArray! A ) { 615 // data[] = cast( valueType[] )a[]; 616 //} 617 618 /// Comparisson Operator 619 const bool opEquals( T )( T vec ) if ( T.dimension == dimension ) { 620 if( data[0] != /*cast( type )*/vec.data[0] ) return false; 621 if( data[1] != /*cast( type )*/vec.data[1] ) return false; 622 static if( dimension >= 3 ) if( data[2] != /*cast( type )*/vec.data[2] ) return false; 623 static if( dimension == 4 ) if( data[3] != /*cast( type )*/vec.data[3] ) return false; 624 return true; 625 } 626 627 /// Unittest Comparisson Operator 628 unittest { 629 assert( vec2( 1.0f, 2.0f ) == vec2( 1.0f, 2.0f )); 630 assert( vec2( 1.0f, 2.0f ) != vec2( 1.0f, 1.0f )); 631 assert( vec2( 1.0f, 2.0f ) == vec2d( 1.0, 2.0 )); 632 assert( vec2( 1.0f, 2.0f ) != vec2d( 1.0, 1.0 )); 633 634 assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3( 1.0f, 2.0f, 3.0f )); 635 assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3( 1.0f, 2.0f, 2.0f )); 636 assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3d( 1.0, 2.0, 3.0 )); 637 assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3d( 1.0, 2.0, 2.0 )); 638 639 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4( 1.0f, 2.0f, 3.0f, 4.0f )); 640 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4( 1.0f, 2.0f, 3.0f, 3.0f )); 641 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4d( 1.0, 2.0, 3.0, 4.0 )); 642 assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4d( 1.0, 2.0, 3.0, 3.0 )); 643 644 assert( !( vec4( float.nan ))); 645 if ( vec4( 1.0f )) {} 646 else { assert( false ); } 647 } 648 649 // swizzling based on opDispatch, source: https://github.com/d-gamedev-team/gfm/blob/master/math/gfm/math/vector.d#L238 650 // drawback is that lib is always recompiled if any swizzle operator changes, implementation at bottom of file 651 652 /// Implements swizzling. 653 /// 654 /// Example: 655 /// --- 656 /// vec4i vi = [4, 1, 83, 10]; 657 /// assert(vi.zxxyw == [83, 4, 4, 1, 10]); 658 /// --- 659 @nogc auto opDispatch( string op, U = void )() pure const nothrow if( isValidSwizzle!( op )) { 660 //pragma( msg, "Swizzle is Valid: ", op ); 661 Vector!( type, op.length ) result = void; 662 //pragma( msg, "Typeof Result", typeof( result ).stringof ); 663 enum indexTuple = swizzleTuple!op; 664 //pragma( msg, "Index Tuple: ", indexTuple ); 665 foreach( i, index; indexTuple ) 666 result.data[ i ] = data[ index ]; 667 return result; 668 } 669 670 /// Support swizzling assignment like in shader languages. 671 /// 672 /// Example: 673 /// --- 674 /// vec3f v = [0, 1, 2]; 675 /// v.yz = v.zx; 676 /// assert(v == [0, 2, 0]); 677 /// --- 678 @nogc void opDispatch( string op, RHS )( RHS rhs ) pure 679 if( isValidSwizzleOperand!( op, RHS ) && 680 is( typeof( Vector!( type, op.length )( rhs )))) { // can be converted to a small vector of the right size 681 Vector!( type, op.length ) array = rhs; // convert atomar type (float, int, etc. ) into array with one element 682 enum indexTuple = swizzleTuple!op; 683 foreach( i, index; indexTuple ) 684 data[ index ] = array[ i ]; 685 } 686 687 private: 688 689 template saticArrayCount( T ) { 690 static if( is( T : A[n], A, size_t n )) 691 enum size_t saticArrayCount = n; 692 else enum size_t saticArrayCount = 0; 693 } 694 695 template isValidSwizzleOperand( string op, RHS ) { 696 //pragma( msg, "isValidSwizzleUnique!op : ", isValidSwizzleUnique!op ); 697 //pragma( msg, "isScalar!RHS : ", __traits( isScalar, RHS )); 698 //pragma( msg, "saticArrayCount : ", saticArrayCount!RHS ); 699 //pragma( msg, "op.length : ", op.length ); 700 //pragma( msg, "isSameLength : ", saticArrayCount!RHS == op.length ); 701 //pragma( msg, "" ); 702 703 704 enum bool isValidSwizzleOperand = ( 705 isValidSwizzleUnique!op && ( // reject lhs.xyy 706 __traits( isScalar, RHS ) || // accept lhs.yzw = rhs.x 707 saticArrayCount!RHS == op.length // accept lhs.yzw = rhs.xzy ( rhs can also be a static array[3] ) 708 ) // reject lhs.yzw = rhs.xz 709 ); 710 } 711 712 template isValidSwizzle( string op, int lastSwizzleClass = -1 ) { 713 enum len = op.length; 714 static if( len == 0 ) enum bool isValidSwizzle = true; // terminator 715 else static if( lastSwizzleClass == -1 && ( len < 2 || len > 4 )) { 716 //pragma( msg, "Only 2, 3 and 4 swizzle operators supported!" ); 717 enum bool isValidSwizzle = false; 718 } else { 719 enum bool indexValid = swizzleIndex!( op[0] ) != -1; 720 static if( !indexValid ) 721 pragma( msg, "Invalid swizzle element: ", op[0] ); 722 enum int swizzleClass = swizzleClassify!( op[0] ); 723 enum bool classValid = ( lastSwizzleClass == -1 || ( swizzleClass == lastSwizzleClass )); 724 static if( !classValid ) 725 pragma( msg, "Swizzle elements must be from one swizzle class! Classe: 'xyzw', 'rgba', 'stpq'." ); 726 727 enum bool isValidSwizzle = classValid && indexValid && isValidSwizzle!( op[ 1 .. len ], swizzleClass ); 728 } 729 } 730 731 template searchElement( char c, string s ) { 732 static if( s.length == 0 ) { 733 enum bool result = false; 734 } else { 735 enum string tail = s[ 1 .. s.length ]; 736 enum bool result = ( s[0] == c ) || searchElement!( c, tail ).result; 737 } 738 } 739 740 template hasNoDuplicates( string s ) { 741 static if( s.length == 1 ) { 742 enum bool result = true; 743 } else { 744 enum tail = s[ 1 .. s.length ]; 745 enum bool result = !( searchElement!( s[0], tail ).result ) && hasNoDuplicates!( tail ).result; 746 } 747 } 748 749 // true if the swizzle has at the maximum one time each letter 750 template isValidSwizzleUnique( string op ) { 751 static if ( isValidSwizzle!op ) { 752 enum isValidSwizzleUnique = hasNoDuplicates!op.result; 753 static if( !isValidSwizzleUnique ) 754 pragma( msg, "Left hand swizzle operator must have unique swizzle elements!" ); 755 } else { 756 enum bool isValidSwizzleUnique = false; 757 } 758 } 759 760 761 762 template swizzleIndex( char c ) { 763 static if( ( c == 'x' || c == 'r' || c == 's' )) enum swizzleIndex = 0; 764 else static if( ( c == 'y' || c == 'g' || c == 't' )) enum swizzleIndex = 1; 765 else static if( dim >= 3 && ( c == 'z' || c == 'b' || c == 'p' )) enum swizzleIndex = 2; 766 else static if( dim == 4 && ( c == 'w' || c == 'a' || c == 'q' )) enum swizzleIndex = 3; 767 else enum swizzleIndex = -1; 768 } 769 770 template swizzleClassify( char c ) { 771 static if( c == 'x' || c == 'y' || c == 'z' || c == 'w' ) enum swizzleClassify = 0; 772 else static if( c == 'r' || c == 'g' || c == 'b' || c == 'a' ) enum swizzleClassify = 1; 773 else static if( c == 's' || c == 't' || c == 'p' || c == 'q' ) enum swizzleClassify = 2; 774 else enum swizzleClassify = -1; 775 } 776 777 template swizzleTuple( string op ) { 778 static if( op.length == 2 ) enum swizzleTuple = [ swizzleIndex!( op[0] ), swizzleIndex!( op[1] ) ]; 779 else static if( op.length == 3 ) enum swizzleTuple = [ swizzleIndex!( op[0] ), swizzleIndex!( op[1] ), swizzleIndex!( op[2] ) ]; 780 else static if( op.length == 4 ) enum swizzleTuple = [ swizzleIndex!( op[0] ), swizzleIndex!( op[1] ), swizzleIndex!( op[2] ), swizzleIndex!( op[3] ) ]; 781 } 782 } 783 784 785 786 ///////////////////////////////// 787 // free functions akin to glsl // 788 ///////////////////////////////// 789 790 pure nothrow @nogc @safe: 791 792 /// Vector dot product 793 V.valueType dot( V )( in V a, in V b ) if ( isVector!V ) { 794 static if ( !isFloatingPoint!( V.valueType ) && V.valueType.sizeof < 32 ) { 795 V.valueType result = cast( V.valueType )( a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ] ); 796 static if ( V.dimension >= 3 ) { result += cast( V.valueType )( a.data[ 2 ] * b.data[ 2 ] ); } 797 static if ( V.dimension == 4 ) { result += cast( V.valueType )( a.data[ 3 ] * b.data[ 3 ] ); } 798 return result; 799 } else { 800 V.valueType result = a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ]; 801 static if ( V.dimension >= 3 ) { result += a.data[ 2 ] * b.data[ 2 ]; } 802 static if ( V.dimension == 4 ) { result += a.data[ 3 ] * b.data[ 3 ]; } 803 return result; 804 } 805 } 806 807 808 /// Vector cross product 809 V cross( V )( in V a, in V b ) if ( isVector!V && ( V.dimension == 3 )) { 810 return V( a.y * b.z - b.y * a.z, 811 a.z * b.x - b.z * a.x, 812 a.x * b.y - b.x * a.y ); 813 } 814 815 816 /// Vector squared length floating point valueType, essentially dot( vec, vec ); 817 auto lengthSquared( V )( in V v ) if ( isVector!V ) { 818 static if ( isFloatingPoint!( V.valueType )) return dot( v, v ); 819 else return cast( real )dot( v, v ); 820 } 821 822 /// Vector length floating point valueType 823 auto length( V )( in V v ) if ( isVector!V ) { 824 return v.lengthSquared.sqrt; 825 } 826 827 828 /// Vector normalize 829 V normalize( V )( in V v ) if ( isVector!V ) { 830 auto l = v.lengthSquared; 831 import dlsl.math : almostEqual; 832 if( l == 0 || almostEqual( 1.0f, l, 0.00000001f )) 833 return v; 834 auto recipLength = 1.0 / l.sqrt; 835 V result = v; 836 result.data[0] *= recipLength; 837 result.data[1] *= recipLength; 838 static if( V.dimension >= 3 ) { result.data[2] *= recipLength; } 839 static if( V.dimension == 4 ) { result.data[3] *= recipLength; } 840 return result; 841 } 842 843 844 /// Distance between two vectors 845 V.valueType distance( V )( const V a, const V b ) if ( isVector!V ) { 846 return length( a - b ); 847 } 848 849 /// Calculates the absolute value per component. 850 V abs( V )( in V v ) if ( isVector!V ) { 851 V result; 852 import dlsl.math : abs; 853 foreach( i, ref c; v ) 854 result[i] = c.abs; 855 return result; 856 } 857 858 859 /// Flip the Vector N based on an incident vector I and a reference Vector Nref 860 V faceforward( V )( in V N, in V I, in V Nref ) if ( isVector!V ) { 861 return dot( Nref, I ) < 0 ? N : -N; 862 } 863 864 865 /// Reflect the Vector I on a plane with normal N 866 /// The normal N must already to be normalized 867 V reflect( V )( in V I, in V N ) if ( isVector!V ) { 868 return I - 2 * dot( N, I ) * N; 869 } 870 871 872 /// For the incident vector I and surface normal N, and the ratio of indices of refraction eta, return the refraction vector 873 /// The input parameters for the incident vector I and the surface normal N must already be normalized 874 V.valueType refract( V )( V I, V N, V.valueType eta ) if ( isVector!V ) { 875 auto dotNI = dot( N, I ); 876 auto k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI ); 877 if ( k < 0.0 ) return 0.0; 878 return eta * I - ( eta * dotNI + sqrt( k )) * N; 879 } 880 881 882 /// Unittest Geometric functions 883 /// TODO : add tests for faceforward, reflect and refract 884 unittest { 885 886 // dot 887 vec2 v2 = vec2( 1.0f, 3.0f ); 888 assert( dot( v2, vec2( 2.0f, 2.0f )) == 8.0f ); 889 890 vec3 v3 = vec3( 1.0f, 3.0f, 5.0f ); 891 assert( dot( v3, vec3( 2.0f, 2.0f, 2.0f )) == 18.0f ); 892 893 vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f ); 894 assert( dot( v4, vec4( 2.0f, 2.0f, 2.0f, 2.0f )) == 32.0f ); 895 896 vec3 v3_1 = vec3( 1.0f, 2.0f, -3.0f ); 897 vec3 v3_2 = vec3( 1.0f, 3.0f, 2.0f ); 898 899 assert( dot( v3_1, v3_2 ) == 1.0f ); 900 assert( dot( v3_1, v3_2 ) == dot( v3_2, v3_1 )); 901 assert( v3_1 * v3_2 == v3_1 * v3_2 ); 902 assert( v3_1 * v3_2 == vec3( 1.0f, 6.0f, -6.0f )); 903 904 // cross 905 assert( cross( v3_1, v3_2 ).data == [ 13.0f, -5.0f, 1.0f ] ); 906 assert( cross( v3_2, v3_1 ).data == [ -13.0f, 5.0f, -1.0f ] ); 907 908 // normalize 909 assert( normalize( vec2( 1 )) == [ 1.0f / sqrt( 2.0f ), 1.0f / sqrt( 2.0f ) ] ); 910 assert( vec3( 1 ).normalize == [ 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ) ] ); 911 assert( normalize( vec4( 1 )) == [ 0.5, 0.5, 0.5, 0.5 ] ); 912 913 // length 914 assert( length( v2 ) == sqrt( 10.0f )); 915 assert( v3.length == sqrt( 35.0f )); 916 assert( length( v4 ) == sqrt( 84.0f )); 917 918 // distance 919 assert( distance( vec2( 0.0f, 0.0f ), vec2( 0.0f, 10.0f )) == 10.0 ); 920 } 921 922 923 /// query if any entry is nan 924 alias isNaN = isnan; // as std.math.isNaN 925 bool isnan( V )( const ref V vec ) if( isVector!V && isFloatingPoint!( V.valueType )) { 926 import std.math : isNaN; 927 foreach( const ref val; vec ) 928 if( std.math.isNaN( val )) 929 return true; 930 return false; 931 } 932 933 934 /// query if any entry is inf 935 alias isInfinity = isinf; // as std.math.isInfinity 936 bool isinf( V )( const ref V vec ) if( isVector!V && isFloatingPoint!( V.valueType )) { 937 import std.math : isInfinity; 938 foreach( const ref val; vec ) 939 if( std.math.isInfinity( val )) 940 return true; 941 return false; 942 } 943 944 945 /// query if all entries are not nan and not inf 946 alias isValid = isvalid; // consistency 947 bool isvalid( V )( const ref V vec ) if( isVector!V && isFloatingPoint!( V.valueType )) { 948 return !( vec.isinf || vec.isnan ); 949 } 950 951 952 bool almostEqual( T, S )( T a, S b, float epsilon = 0.000001f ) if( isVector!T && isVector!S && T.dimension == S.dimension ) { 953 import dlsl.math : almostEqual; 954 foreach( i; 0 .. T.dimension) 955 if( !dlsl.math.almostEqual( a.vector[i], b.vector[i], epsilon )) 956 return false; 957 return true; 958 } 959 960 961 unittest { 962 963 // almostEqual 964 assert( almostEqual(vec2i( 0, 0), vec2( 0.0f, 0.0f ))); 965 assert( almostEqual(vec2( 0.0f, 0.0f ), vec2( 0.000001f, 0.000001f ))); 966 assert( almostEqual(vec3( 0.0f, 1.0f, 2.0f ), vec3i( 0, 1, 2 ))); 967 } 968 969 970 971 972 // this is another swizzle getter/setter variant based on opDispatch, source: http://www.mmartins.me/view/2015/9/27/vector-swizzle-in-d 973 // drawback is that it is always recompiled if any swizzle operator changes, implementation at bottom of file 974 /* private enum vec_swizz_get_xyzw = "xyzw"; 975 private enum vec_swizz_get_rgba = "rgba"; 976 private enum vec_swizz_get_stpq = "stpq"; 977 auto opDispatch(string swizzle)() if (swizzle.length > 0 && swizzle.length < 5) { 978 import std.string : indexOf, join; 979 980 static if (swizzle.length == 1) { 981 pragma( msg, "length = 1" ); 982 static if (vec_swizz_get_xyzw.indexOf( swizzle[0] ) >= 0 ) 983 enum index = vec_swizz_get_xyzw.indexOf( swizzle[0] ); 984 else static if (vec_swizz_get_rgba.indexOf( swizzle[0] ) >= 0 ) 985 enum index = vec_swizz_get_rgba.indexOf( swizzle[0] ); 986 else static if (vec_swizz_get_stpq.indexOf( swizzle[0] ) >= 0 ) 987 enum index = vec_swizz_get_stpq.indexOf( swizzle[0] ); 988 else { 989 char[128] formatBuffer; 990 //auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ); 991 static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) ); 992 } 993 return data[index]; 994 } else { 995 import std.conv : to; 996 //import std.array : array; 997 import std.algorithm : map; 998 pragma( msg, "length > 1, ", swizzle ); 999 static if (vec_swizz_get_xyzw.indexOf(swizzle[0]) >= 0) { 1000 pragma( msg, vec_swizz_get_xyzw ); 1001 enum indices = swizzle.map!(x => vec_swizz_get_xyzw.indexOf(x)).array; 1002 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 1003 pragma( msg, to!string( indices ) ); 1004 return mixin(args); 1005 } else static if(vec_swizz_get_rgba.indexOf(swizzle[0]) >= 0) { 1006 pragma( msg, vec_swizz_get_rgba ); 1007 enum indices = swizzle.map!(x => vec_swizz_get_rgba.indexOf(x)).array; 1008 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 1009 return mixin(args); 1010 } else static if(vec_swizz_get_stpq.indexOf(swizzle[0]) >= 0) { 1011 pragma( msg, vec_swizz_get_stpq ); 1012 enum indices = swizzle.map!(x => vec_swizz_get_stpq.indexOf(x)).array; 1013 enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")"; 1014 return mixin(args); 1015 } else { 1016 char[128] formatBuffer; 1017 //auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ); 1018 static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) ); 1019 } 1020 1021 //pragma( msg, args ); 1022 //return mixin(args); 1023 } 1024 } 1025 1026 */