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 	/// Implements dynamic swizzling.
501 	/// Returns: a static valueType array
502 	/// TODO : Limit to access sets, characters can be combined within one set only, set1: xyzw, set2: rgba, set3: stpq
503 	@property valueType[ s.length ] opDispatch( string s )() const if ( s.length <= dimension )  {
504 		valueType[ s.length ] result;
505 		dispatchImpl!( 0, s )( result );
506 		return result;
507 	}
508 
509 	void dispatchImpl( int i, string s, int size )( ref valueType[ size ] result ) const  {
510 		static if( s.length > 0 )  {
511 			result[i] = data[  coord_to_index!( s[0] ) ];
512 			dispatchImpl!( i + 1, s[ 1 .. $ ] )( result );
513 		}
514 	}
515 */
516 	/// TODO : patch unittest according to access sets !!!
517 	unittest  {
518 		vec2 v2 = vec2( 1.0f, 2.0f );
519 		assert( v2.ts == [ 2.0f, 1.0f ] );
520 
521 		assert( vec3( 1.0f, 2.0f, 3.0f ).xyz == [ 1.0f, 2.0f, 3.0f ] );
522 		assert( vec4( v2, 3.0f, 4.0f  ).xyzw == [ 1.0f, 2.0f, 3.0f, 4.0f ] );		
523 		assert( vec4( v2, 3.0f, 4.0f  ).wxyz == [ 4.0f, 1.0f, 2.0f, 3.0f ] );
524 		assert( vec4( 1.0f, v2.yx, 2.0f ).data == [ 1.0f, 2.0f, 1.0f, 2.0f ] );
525 	}
526 
527 
528 	/// Returns the euclidean length of the vector
529 	@property auto length() const  {	// Required to overwrite array.length
530 		return .length( this );			// .operator calls free module scope length function
531 	}
532 
533 
534 	/// TODO : This does not work
535 	//XForm.xy += vec2( md.rx, md.ry );
536 	
537 	/// Negate the vector
538 	Vector opUnary( string op : "-" )() const  {
539 		Vector result;
540 		result.data[0] = - data[0];
541 		result.data[1] = - data[1];
542 		static if( dimension >= 3 )  {  result.data[2] = - data[2];  }
543 		static if( dimension == 4 )  {  result.data[3] = - data[3];  }
544 		return result;
545 	}
546 
547 
548 	/// Unittest OpUnary Negate
549 	unittest  {
550 		assert( vec2(  1.0f, 1.0f ) == -vec2( -1.0f, -1.0f ));
551 		assert( vec2( -1.0f, 1.0f ) == -vec2(  1.0f, -1.0f ));
552 
553 		assert( - vec3(  1.0f, 1.0f,  1.0f ) == vec3( -1.0f, -1.0f, -1.0f ));
554 		assert( - vec3( -1.0f, 1.0f, -1.0f ) == vec3(  1.0f, -1.0f,  1.0f ));
555 
556 		assert( vec4(  1.0f, 1.0f,  1.0f, 1.0f ) == -vec4( -1.0f, -1.0f, -1.0f, -1.0f ));
557 		assert( vec4( -1.0f, 1.0f, -1.0f, 1.0f ) == -vec4(  1.0f, -1.0f,  1.0f, -1.0f ));
558 	}
559 
560 
561 	/// Componentwise binary vector-skalar operation: addition, subtraction, multiplication, division
562 	Vector opBinary( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" ))  {
563 		Vector result;
564 		result.data[0] = mixin( "data[0]" ~ op ~ "s" );
565 		result.data[1] = mixin( "data[1]" ~ op ~ "s" );
566 		static if( dimension >= 3 )  {  result.data[2] = mixin( "data[2]" ~ op ~ "s" );  }
567 		static if( dimension == 4 )  {  result.data[3] = mixin( "data[3]" ~ op ~ "s" );  }
568 		return result;
569 	}
570 
571 
572 	/// Componentwise binary skalar-vector operation: addition, subtraction, multiplication, division
573 	auto opBinaryRight( string op )( valueType s ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" ))  {
574 		Vector result;
575 		result.data[0] = mixin( "s" ~ op ~ "data[0]" );
576 		result.data[1] = mixin( "s" ~ op ~ "data[1]" );
577 		static if( dimension >= 3 )  {  result.data[2] = mixin( "s" ~ op ~ "data[2]" );  }
578 		static if( dimension == 4 )  {  result.data[3] = mixin( "s" ~ op ~ "data[3]" );  }
579 		return result;
580 	}
581 
582 
583 	/// Componentwise binary operation with aonther vector: addition, subtraction, multiplication, division
584 	Vector opBinary( string op )( Vector v ) const if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" ))  {
585 		Vector result;
586 		result.data[0] = mixin( "data[0]" ~ op ~ "v.data[0]" );
587 		result.data[1] = mixin( "data[1]" ~ op ~ "v.data[1]" );
588 		static if( dimension >= 3 )  {  result.data[2] = mixin( "data[2]" ~ op ~ "v.data[2]" );  }
589 		static if( dimension == 4 )  {  result.data[3] = mixin( "data[3]" ~ op ~ "v.data[3]" );  }
590 		return result;
591 	}
592 
593 
594 	/// Unittest OpBinary
595 	unittest  {
596 		vec2 v2 = vec2( 1.0f, 3.0f );
597 		cast( void )( 2 * v2 );
598 		assert(( v2 * 2.5f ).data == [ 2.5f, 7.5f ] );
599 		assert(( v2 + vec2( 3.0f, 1.0f )).data == [ 4.0f, 4.0f ] );
600 		assert(( v2 - vec2( 1.0f, 3.0f )).data == [ 0.0f, 0.0f ] );
601 		assert(( v2 * vec2( 2.0f, 2.0f ))  == vec2( 2.0f, 6.0f ));
602 
603 		vec3 v3 = vec3( 1.0f, 3.0f, 5.0f );
604 		assert(( v3 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f ] );
605 		assert(( v3 + vec3( 3.0f, 1.0f,  - 1.0f )).data == [ 4.0f, 4.0f, 4.0f ] );
606 		assert(( v3 - vec3( 1.0f, 3.0f, 5.0f )).data == [ 0.0f, 0.0f,  0.0f ] );
607 		assert(( v3 * vec3( 2.0f, 2.0f, 2.0f ))  == vec3( 2.0f, 6.0f, 10.0f ));
608 
609 		vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f );
610 		assert(( v4 * 2.5f ).data == [ 2.5f, 7.5f, 12.5f, 17.5 ] );
611 		assert(( v4 + vec4( 3.0f, 1.0f, - 1.0f, - 3.0f )).data == [ 4.0f, 4.0f, 4.0f, 4.0f ] );
612 		assert(( v4 - vec4( 1.0f, 3.0f, 5.0f, 7.0f )).data == [ 0.0f, 0.0f,  0.0f,  0.0f ] );
613 		assert(( v4 * vec4( 2.0f, 2.0f, 2.0f, 2.0f ))  == vec4( 2.0f, 6.0f, 10.0f, 14.0f ));
614 
615 	}
616 
617 	/// Op= Operation with a scalar
618 	void opOpAssign( string op )( valueType val ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" ))  {
619 		mixin( "data[0] " ~ op ~ "= val;" );
620 		mixin( "data[1] " ~ op ~ "= val;" );
621 		static if( dimension >= 3 )  mixin( "data[2] " ~ op ~ "= val;" );
622 		static if( dimension == 4 )  mixin( "data[3] " ~ op ~ "= val;" );
623 	}
624 
625 	/// Componentwise Op= Operation with another vector
626 	void opOpAssign( string op )( Vector vec ) if (( op == "+" ) || ( op == "-" ) || ( op == "*" ) || ( op == "/" ))  {
627 		mixin( "data[0] " ~ op ~ "= vec.data[0];" );
628 		mixin( "data[1] " ~ op ~ "= vec.data[1];" );
629 		static if( dimension >= 3 )  mixin( "data[2] " ~ op ~ "= vec.data[2];" );
630 		static if( dimension == 4 )  mixin( "data[3] " ~ op ~ "= vec.data[3];" );
631 	}
632 
633 	unittest  {
634 		vec2 v2 = vec2( 1.0f, 3.0f );
635 		v2 *= 2.5f;
636 		assert( v2.data == [ 2.5f, 7.5f ] );
637 		v2 -= vec2( 2.5f, 7.5f );
638 		assert( v2.data == [ 0.0f, 0.0f ] );
639 		v2 += vec2( 1.0f, 3.0f );
640 		assert( v2.data == [ 1.0f, 3.0f ] );
641 		//assert( almost_equal( v2.normalized, vec2( 1.0f/sqrt( 10.0f ), 3.0f/sqrt( 10.0f ))));
642 
643 		vec3 v3 = vec3( 1.0f, 3.0f, 5.0f );
644 		v3 *= 2.5f;
645 		assert( v3.data == [ 2.5f, 7.5f, 12.5f ] );
646 		v3 -= vec3( 2.5f, 7.5f, 12.5f );
647 		assert( v3.data == [ 0.0f, 0.0f, 0.0f ] );
648 		v3 += vec3( 1.0f, 3.0f, 5.0f );
649 		assert( v3.data == [ 1.0f, 3.0f, 5.0f ] );
650 		//assert( almost_equal( v3.normalized, vec3( 1.0f/sqrt( 35.0f ), 3.0f/sqrt( 35.0f ), 5.0f/sqrt( 35.0f ))));
651 
652 		vec4 v4 = vec4( 1.0f, 3.0f, 5.0f, 7.0f );
653 		v4 *= 2.5f;
654 		assert( v4.data == [ 2.5f, 7.5f, 12.5f, 17.5 ] );
655 		v4 -= vec4( 2.5f, 7.5f, 12.5f, 17.5f );
656 		assert( v4.data == [ 0.0f, 0.0f, 0.0f, 0.0f ] );
657 		v4 += vec4( 1.0f, 3.0f, 5.0f, 7.0f );
658 		assert( v4.data == [ 1.0f, 3.0f, 5.0f, 7.0f ] );
659 		//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 ))));
660 	}
661 
662 	//void opAssign( A )( A a )  if ( isArray! A )  {
663 	//	data[] = cast( valueType[] )a[];
664 	//}
665 
666 	/// Comparisson Operator
667 //	const bool opEquals( T )( T vec ) if ( T.dimension == dimension )  {  return data == vec.data;  }
668 
669 	/// Unittest Comparisson Operator
670 /*	unittest  {
671 		assert( vec2( 1.0f, 2.0f ) == vec2( 1.0f, 2.0f ));
672 		assert( vec2( 1.0f, 2.0f ) != vec2( 1.0f, 1.0f ));
673 		assert( vec2( 1.0f, 2.0f ) == vec2d( 1.0, 2.0 ));
674 		assert( vec2( 1.0f, 2.0f ) != vec2d( 1.0, 1.0 ));
675 
676 		assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3( 1.0f, 2.0f, 3.0f ));
677 		assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3( 1.0f, 2.0f, 2.0f ));
678 		assert( vec3( 1.0f, 2.0f, 3.0f ) == vec3d( 1.0, 2.0, 3.0 ));
679 		assert( vec3( 1.0f, 2.0f, 3.0f ) != vec3d( 1.0, 2.0, 2.0 ));
680 
681 		assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4( 1.0f, 2.0f, 3.0f, 4.0f ));
682 		assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4( 1.0f, 2.0f, 3.0f, 3.0f ));
683 		assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) == vec4d( 1.0, 2.0, 3.0, 4.0 ));
684 		assert( vec4( 1.0f, 2.0f, 3.0f, 4.0f ) != vec4d( 1.0, 2.0, 3.0, 3.0 ));
685 
686 		assert( !( vec4( float.nan )));
687 		if ( vec4( 1.0f )) {}
688 		else {  assert( false );  }
689 	}*/
690 }
691 
692 
693 @safe pure nothrow:
694 
695 /// Vector dot product
696 genType.valueType dot( genType )( in genType a, in genType b ) if ( isVector!genType )  {
697 	static if ( !isFloatingPoint!( genType.valueType ) && genType.valueType.sizeof < 32 )  {
698 		genType.valueType result = cast( genType.valueType )( a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ] );
699 		static if ( genType.dimension >= 3 ) { result += cast( genType.valueType )( a.data[ 2 ] * b.data[ 2 ] ); }
700 		static if ( genType.dimension == 4 ) { result += cast( genType.valueType )( a.data[ 3 ] * b.data[ 3 ] ); }
701 		return result;
702 	} else {
703 		genType.valueType result = a.data[ 0 ] * b.data[ 0 ] + a.data[ 1 ] * b.data[ 1 ];
704 		static if ( genType.dimension >= 3 ) { result += a.data[ 2 ] * b.data[ 2 ]; }
705 		static if ( genType.dimension == 4 ) { result += a.data[ 3 ] * b.data[ 3 ]; }
706 		return result;
707 	}
708 }
709 
710 
711 /// Vector cross product
712 genType cross( genType )( in genType a, in genType b ) if ( isVector!genType && ( genType.dimension == 3 ))  {
713    return genType( a.y * b.z - b.y * a.z,
714 				   a.z * b.x - b.z * a.x,
715 				   a.x * b.y - b.x * a.y );
716 }
717 
718 
719 /// Vector length floating point valueType
720 auto length( genType )( in genType v ) if ( isVector!genType )  {
721 	static if ( isFloatingPoint!( genType.valueType ))	return sqrt( dot( v, v ));
722 	else												return sqrt( cast( real )dot( v, v )); 
723 }
724 
725 
726 /// Vector normalize
727 genType normalize( genType )( in genType v ) if ( isVector!genType )  {
728 	auto l = v.length;
729 	if ( l == 0 )  return v;
730 	auto invLength = 1.0 / l;
731 	genType result = v;
732 	result.data[0] *= invLength;
733 	result.data[1] *= invLength;
734 	static if( genType.dimension >= 3 )  {  result.data[2] *= invLength;  }
735 	static if( genType.dimension == 4 )  {  result.data[3] *= invLength;  }
736 	return result;
737 }
738 
739 
740 /// Distance between two vectors
741 genType.valueType distance( genType )( const genType a, const genType b ) if ( isVector!genType ) {
742 	return length( a - b );
743 }
744 
745 
746 /// Flip the Vector N based on an incident vector I and a reference Vector Nref 
747 genType faceforward( genType )( in genType N, in genType I, in genType Nref ) if ( isVector!genType )  {
748 	return  dot( Nref, I ) < 0 ? N : -N;
749 }
750 
751 
752 /// Reflect the Vector I on a plane with normal N 
753 /// The normal N must already to be normalized
754 genType reflect( genType )( in genType I, in genType N ) if ( isVector!genType )  {
755 	return I - 2 * dot( N, I ) * N;
756 }
757 
758 
759 /// For the incident vector I and surface normal N, and the ratio of indices of refraction eta, return the refraction vector
760 /// The input parameters for the incident vector I and the surface normal N must already be normalized
761 genType.valueType refract( genType )( genType I, genType N, genType.valueType eta ) if ( isVector!genType )  {
762 	auto dotNI = dot( N, I );
763 	auto k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI );
764 	if ( k < 0.0 ) return 0.0;
765 	return eta * I - ( eta * dotNI + sqrt( k )) * N;
766 }
767 
768 
769 /// Unittest Geometric functions
770 /// TODO : add tests for faceforward, reflect and refract
771 unittest  {
772 	
773 	// dot
774 	vec2 v2 = vec2( 1.0f,  3.0f );
775 	assert( dot( v2, vec2( 2.0f, 2.0f )) == 8.0f );
776 
777 	vec3 v3 = vec3( 1.0f,  3.0f, 5.0f );
778 	assert( dot( v3, vec3( 2.0f, 2.0f, 2.0f )) == 18.0f );
779 
780 	vec4 v4 = vec4( 1.0f,  3.0f, 5.0f, 7.0f );
781 	assert( dot( v4, vec4( 2.0f, 2.0f, 2.0f, 2.0f )) == 32.0f );
782 
783 	vec3 v3_1 = vec3( 1.0f, 2.0f, -3.0f );
784 	vec3 v3_2 = vec3( 1.0f, 3.0f,  2.0f );
785 
786 	assert( dot( v3_1, v3_2 ) == 1.0f );
787 	assert( dot( v3_1, v3_2 ) == dot( v3_2, v3_1 ));
788 	assert( v3_1 * v3_2 == v3_1 * v3_2 );
789 	assert( v3_1 * v3_2 == vec3( 1.0f, 6.0f, -6.0f ));
790 
791 	// cross
792 	assert( cross( v3_1, v3_2 ).data == [ 13.0f, -5.0f, 1.0f ] );
793 	assert( cross( v3_2, v3_1 ).data == [ -13.0f, 5.0f, -1.0f ] );
794 
795 	// normalize
796 	assert( normalize( vec2( 1 )) == [ 1.0f / sqrt( 2.0f ), 1.0f / sqrt( 2.0f ) ] );
797 	assert( vec3( 1 ).normalize   == [ 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ), 1.0f / sqrt( 3.0f ) ] );
798 	assert( normalize( vec4( 1 )) == [ 0.5, 0.5, 0.5, 0.5 ] );
799 
800 	// length
801 	assert( length( v2 ) == sqrt( 10.0f ));
802 	assert( v3.length    == sqrt( 35.0f ));
803 	assert( length( v4 ) == sqrt( 84.0f ));
804 
805 	// distance
806 	assert( distance( vec2( 0.0f, 0.0f ), vec2( 0.0f, 10.0f )) == 10.0 );        
807 }
808 
809 
810 /// query if any entry is nan
811 alias isNaN = isnan;	// as std.math.isNaN
812 bool isnan( genType )( const ref genType vec ) if( isVector!genType && isFloatingPoint!( genType.valueType )) {
813 	import std.math : isNaN; 
814 	foreach( const ref val; vec )
815 		if( std.math.isNaN( val ))
816 			return true;
817 	return false;
818 }
819 
820 
821 /// query if any entry is inf
822 alias isInfinity = isinf;	// as std.math.isInfinity
823 bool isinf( genType )( const ref genType vec ) if( isVector!genType && isFloatingPoint!( genType.valueType )) {
824 	import std.math : isInfinity;
825 	foreach( const ref val; vec )
826 		if( std.math.isInfinity( val )) 
827 			return true;
828 	return false;
829 }
830 
831 
832 /// query if all entries are not nan and not inf
833 alias isValid = isvalid;	// consistency
834 bool isvalid( genType )( const ref genType vec ) if( isVector!genType && isFloatingPoint!( genType.valueType )) {
835 	return !( vec.isinf || vec.isnan );
836 }
837 
838 
839 unittest {		/// Matrix.isvalid
840 /*
841 	mat2 m2 = mat2( 1.0f, 1.0f, vec2( 2.0f, 2.0f ));
842 	assert( m2.data == [[ 1.0f, 1.0f ], [ 2.0f, 2.0f ]] );
843 	m2.clear( 3.0f );
844 	assert( m2.data == [[ 3.0f, 3.0f ], [ 3.0f, 3.0f ]] );
845 	assert( m2.isvalid );
846 	m2.clear( float.nan );
847 	assert( !m2.isvalid );
848 	m2.clear( float.infinity );
849 	assert( !m2.isvalid );
850 	m2.clear( 0.0f );
851 	assert( m2.isvalid );
852 
853 	mat3 m3 = mat3( 1.0f );
854 	assert( m3.data == [[ 1.0f, 0.0f, 0.0f ],
855 						[ 0.0f, 1.0f, 0.0f ],
856 						[ 0.0f, 0.0f, 1.0f ]] );
857 
858 	mat4 m4 = mat4( vec4( 	1.0f, 1.0f, 1.0f, 1.0f ),
859 							2.0f, 2.0f, 2.0f, 2.0f,
860 							3.0f, 3.0f, 3.0f, 3.0f,
861 						 vec4( 4.0f, 4.0f, 4.0f, 4.0f ));
862 	assert( m4.data == [[ 1.0f, 1.0f, 1.0f, 1.0f ],
863 						[ 2.0f, 2.0f, 2.0f, 2.0f ],
864 						[ 3.0f, 3.0f, 3.0f, 3.0f ],
865 						[ 4.0f, 4.0f, 4.0f, 4.0f ]] );
866 	assert( mat3( m4 ).data == [[ 1.0f, 1.0f, 1.0f ],
867 								[ 2.0f, 2.0f, 2.0f ],
868 								[ 3.0f, 3.0f, 3.0f ]] );
869 	assert( mat2( mat3( m4 )).data == [[ 1.0f, 1.0f ],
870 													[ 2.0f, 2.0f ] ] );
871 	assert( mat2( m4 ).data == mat2( mat3( m4 )).data );
872 	assert( mat4( mat3( m4 )).data ==  [[ 1.0f, 1.0f, 1.0f, 0.0f ],
873 										[ 2.0f, 2.0f, 2.0f, 0.0f ],
874 										[ 3.0f, 3.0f, 3.0f, 0.0f ],
875 										[ 0.0f, 0.0f, 0.0f, 1.0f ]] );
876 
877 	Matrix!( float, 2, 3 ) mt1 = Matrix!( float, 2, 3 )( 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f );
878 	Matrix!( float, 3, 2 ) mt2 = Matrix!( float, 3, 2 )( 6.0f, - 1.0f, 3.0f, 2.0f, 0.0f, - 3.0f );
879 
880 	assert( mt1.data == [[ 1.0f, 2.0f, 3.0f ], [ 4.0f, 5.0f, 6.0f ]] );
881 	assert( mt2.data == [[ 6.0f, - 1.0f ], [ 3.0f, 2.0f ], [ 0.0f, - 3.0f ]] );
882 */
883 }
884 
885 
886 
887 
888 	// this is another variant based on opDispatch, source: http://www.mmartins.me/view/2015/9/27/vector-swizzle-in-d
889 	// drawback is that it is always recompiled if any swizzle operator changes, implementation at bottom of file
890 /*	private enum vec_swizz_get_xyzw = "xyzw";
891 	private enum vec_swizz_get_rgba = "rgba";
892 	private enum vec_swizz_get_stpq = "stpq";
893 	@property auto opDispatch(string swizzle)() if (swizzle.length > 0 && swizzle.length < 5) {
894 		import std.string : indexOf, join;
895 
896 		static if (swizzle.length == 1) {
897 			pragma( msg, "length = 1" );
898 			static if		(vec_swizz_get_xyzw.indexOf( swizzle[0] ) >= 0 )
899 				enum index = vec_swizz_get_xyzw.indexOf( swizzle[0] );
900 			else static if	(vec_swizz_get_rgba.indexOf( swizzle[0] ) >= 0 )
901 				enum index = vec_swizz_get_rgba.indexOf( swizzle[0] );
902 			else static if	(vec_swizz_get_stpq.indexOf( swizzle[0] ) >= 0 )
903 				enum index = vec_swizz_get_stpq.indexOf( swizzle[0] );
904 			else {
905 				char[128] formatBuffer;
906 				//auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle );
907 				static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) );
908 			}
909 			return data[index];
910 		} else {
911 			import std.conv : to;
912 			import std.array : array;
913 			import std.algorithm : map;
914 			pragma( msg, "length > 1, ", swizzle );
915 			static if		(vec_swizz_get_xyzw.indexOf(swizzle[0]) >= 0) {
916 				pragma( msg, vec_swizz_get_xyzw );
917 				enum indices = swizzle.map!(x => vec_swizz_get_xyzw.indexOf(x)).array;
918 				enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")";
919 				pragma( msg, to!string( indices ) );
920 				return mixin(args);
921 			} else static if(vec_swizz_get_rgba.indexOf(swizzle[0]) >= 0) {
922 				pragma( msg, vec_swizz_get_rgba );
923 				enum indices = swizzle.map!(x => vec_swizz_get_rgba.indexOf(x)).array;
924 				enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")";
925 				return mixin(args);
926 			} else static if(vec_swizz_get_stpq.indexOf(swizzle[0]) >= 0) {
927 				pragma( msg, vec_swizz_get_stpq );
928 				enum indices = swizzle.map!(x => vec_swizz_get_stpq.indexOf(x)).array;
929 				enum args = "Vector!(valueType, swizzle.length)(" ~ indices.map!(x => "data[" ~ x.to!string ~ "]").join(",") ~ ")";
930 				return mixin(args);
931 			} else {
932 				char[128] formatBuffer;
933 				//auto formatString = sformat( formatBuffer, "Invalid swizzle property: %s", swizzle );
934 				static assert( false, sformat( formatBuffer, "Invalid swizzle property: %s", swizzle ) );
935 			}
936 
937 			//pragma( msg, args );
938 			//return mixin(args);
939 		}
940 	}
941 
942 */