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