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 */