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