1 /// N-dimension vector mathematical object
2 module gfm.math.vector;
3 
4 import std.traits,
5        std.math,
6        std.conv,
7        std.array,
8        std..string;
9 
10 import gfm.math.funcs;
11 
12 /**
13  * Generic 1D small vector.
14  * Params:
15  *    N = number of elements
16  *    T = type of elements
17  */
18 struct Vector(T, int N)
19 {
20 nothrow:
21     public
22     {
23         static assert(N >= 1);
24 
25         // fields definition
26         union
27         {
28             T[N] v;
29             struct
30             {
31                 static if (N >= 1)
32                 {
33                     T x;
34                     alias x r;
35                 }
36                 static if (N >= 2)
37                 {
38                     T y;
39                     alias y g;
40                 }
41                 static if (N >= 3)
42                 {
43                     T z;
44                     alias z b;
45                 }
46                 static if (N >= 4)
47                 {
48                     T w;
49                     alias w a;
50                 }
51             }
52         }
53 
54         /// Construct a Vector with a `T[]` or the values as arguments
55         @nogc this(Args...)(Args args) pure nothrow
56         {
57             static if (args.length == 1)
58             {
59                 // Construct a Vector from a single value.
60                 opAssign!(Args[0])(args[0]);
61             }
62             else
63             {
64                 // validate the total argument count across scalars and vectors
65                 template argCount(T...) {
66                     static if(T.length == 0)
67                         enum argCount = 0; // done recursing
68                     else static if(isVector!(T[0]))
69                         enum argCount = T[0]._N + argCount!(T[1..$]);
70                     else
71                         enum argCount = 1 + argCount!(T[1..$]);
72                 }
73 
74                 static assert(argCount!Args <= N, "Too many arguments in vector constructor");
75 
76                 int index = 0;
77                 foreach(arg; args)
78                 {
79                     static if (isAssignable!(T, typeof(arg)))
80                     {
81                         v[index] = arg;
82                         index++; // has to be on its own line (DMD 2.068)
83                     }
84                     else static if (isVector!(typeof(arg)) && isAssignable!(T, arg._T))
85                     {
86                         mixin(generateLoopCode!("v[index + @] = arg[@];", arg._N)());
87                         index += arg._N;
88                     }
89                     else
90                         static assert(false, "Unrecognized argument in Vector constructor");
91                 }
92                 assert(index == N, "Bad arguments in Vector constructor");
93             }
94         }
95 
96         /// Assign a Vector from a compatible type.
97         @nogc ref Vector opAssign(U)(U x) pure nothrow if (isAssignable!(T, U))
98         {
99             mixin(generateLoopCode!("v[@] = x;", N)()); // copy to each component
100             return this;
101         }
102 
103         /// Assign a Vector with a static array type.
104         @nogc ref Vector opAssign(U)(U arr) pure nothrow if ((isStaticArray!(U) && isAssignable!(T, typeof(arr[0])) && (arr.length == N)))
105         {
106             mixin(generateLoopCode!("v[@] = arr[@];", N)());
107             return this;
108         }
109 
110         /// Assign with a dynamic array.
111         /// Size is checked in debug-mode.
112         @nogc ref Vector opAssign(U)(U arr) pure nothrow if (isDynamicArray!(U) && isAssignable!(T, typeof(arr[0])))
113         {
114             assert(arr.length == N);
115             mixin(generateLoopCode!("v[@] = arr[@];", N)());
116             return this;
117         }
118 
119         /// Assign from a samey Vector.
120         @nogc ref Vector opAssign(U)(U u) pure nothrow if (is(U : Vector))
121         {
122             v[] = u.v[];
123             return this;
124         }
125 
126         /// Assign from other vectors types (same size, compatible type).
127         @nogc ref Vector opAssign(U)(U x) pure nothrow if (isVector!U
128                                                        && isAssignable!(T, U._T)
129                                                        && (!is(U: Vector))
130                                                        && (U._N == _N))
131         {
132             mixin(generateLoopCode!("v[@] = x.v[@];", N)());
133             return this;
134         }
135 
136         /// Returns: a pointer to content.
137         @nogc inout(T)* ptr() pure inout nothrow @property
138         {
139             return v.ptr;
140         }
141 
142         /// Converts to a pretty string.
143         string toString() const nothrow
144         {
145             try
146                 return format("%s", v);
147             catch (Exception e)
148                 assert(false); // should not happen since format is right
149         }
150 
151         @nogc bool opEquals(U)(U other) pure const nothrow
152             if (is(U : Vector))
153         {
154             for (int i = 0; i < N; ++i)
155             {
156                 if (v[i] != other.v[i])
157                 {
158                     return false;
159                 }
160             }
161             return true;
162         }
163 
164         @nogc bool opEquals(U)(U other) pure const nothrow
165             if (isConvertible!U)
166         {
167             Vector conv = other;
168             return opEquals(conv);
169         }
170 
171         @nogc Vector opUnary(string op)() pure const nothrow
172             if (op == "+" || op == "-" || op == "~" || op == "!")
173         {
174             Vector res = void;
175             mixin(generateLoopCode!("res.v[@] = " ~ op ~ " v[@];", N)());
176             return res;
177         }
178 
179         @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow
180             if (is(U : Vector))
181         {
182             mixin(generateLoopCode!("v[@] " ~ op ~ "= operand.v[@];", N)());
183             return this;
184         }
185 
186         @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U)
187         {
188             Vector conv = operand;
189             return opOpAssign!op(conv);
190         }
191 
192         @nogc Vector opBinary(string op, U)(U operand) pure const nothrow
193             if (is(U: Vector) || (isConvertible!U))
194         {
195             Vector result = void;
196             static if (is(U: T))
197                 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " operand);", N)());
198             else
199             {
200                 Vector other = operand;
201                 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " other.v[@]);", N)());
202             }
203             return result;
204         }
205 
206         @nogc Vector opBinaryRight(string op, U)(U operand) pure const nothrow if (isConvertible!U)
207         {
208             Vector result = void;
209             static if (is(U: T))
210                 mixin(generateLoopCode!("result.v[@] = cast(T)(operand " ~ op ~ " v[@]);", N)());
211             else
212             {
213                 Vector other = operand;
214                 mixin(generateLoopCode!("result.v[@] = cast(T)(other.v[@] " ~ op ~ " v[@]);", N)());
215             }
216             return result;
217         }
218 
219         @nogc ref T opIndex(size_t i) pure nothrow
220         {
221             return v[i];
222         }
223 
224         @nogc ref const(T) opIndex(size_t i) pure const nothrow
225         {
226             return v[i];
227         }
228 
229         @nogc T opIndexAssign(U : T)(U x, size_t i) pure nothrow
230         {
231             return v[i] = x;
232         }
233 
234 
235         /// Implements swizzling.
236         ///
237         /// Example:
238         /// ---
239         /// vec4i vi = [4, 1, 83, 10];
240         /// assert(vi.zxxyw == [83, 4, 4, 1, 10]);
241         /// ---
242         @nogc @property auto opDispatch(string op, U = void)() pure const nothrow if (isValidSwizzle!(op))
243         {
244             alias Vector!(T, op.length) returnType;
245             returnType res = void;
246             enum indexTuple = swizzleTuple!op;
247             foreach(i, index; indexTuple)
248                 res.v[i] = v[index];
249             return res;
250         }
251 
252         /// Support swizzling assignment like in shader languages.
253         ///
254         /// Example:
255         /// ---
256         /// vec3f v = [0, 1, 2];
257         /// v.yz = v.zx;
258         /// assert(v == [0, 2, 0]);
259         /// ---
260         @nogc @property void opDispatch(string op, U)(U x) pure
261             if ((op.length >= 2)
262                 && (isValidSwizzleUnique!op)                   // v.xyy will be rejected
263                 && is(typeof(Vector!(T, op.length)(x)))) // can be converted to a small vector of the right size
264         {
265             Vector!(T, op.length) conv = x;
266             enum indexTuple = swizzleTuple!op;
267             foreach(i, index; indexTuple)
268                 v[index] = conv[i];
269         }
270 
271         /// Casting to small vectors of the same size.
272         /// Example:
273         /// ---
274         /// vec4f vf;
275         /// vec4d vd = cast!(vec4d)vf;
276         /// ---
277         @nogc U opCast(U)() pure const nothrow if (isVector!U && (U._N == _N))
278         {
279             U res = void;
280             mixin(generateLoopCode!("res.v[@] = cast(U._T)v[@];", N)());
281             return res;
282         }
283 
284         /// Implement slices operator overloading.
285         /// Allows to go back to slice world.
286         /// Returns: length.
287         @nogc int opDollar() pure const nothrow
288         {
289             return N;
290         }
291 
292         /// Slice containing vector values
293         /// Returns: a slice which covers the whole Vector.
294         @nogc T[] opSlice() pure nothrow
295         {
296             return v[];
297         }
298 
299         /// vec[a..b]
300         @nogc T[] opSlice(int a, int b) pure nothrow
301         {
302             return v[a..b];
303         }
304 
305         deprecated("Use squaredMagnitude instead") alias squaredLength = squaredMagnitude;
306         /// Squared Euclidean length of the Vector
307         /// Returns: squared length.
308         @nogc T squaredMagnitude() pure const nothrow
309         {
310             T sumSquares = 0;
311             mixin(generateLoopCode!("sumSquares += v[@] * v[@];", N)());
312             return sumSquares;
313         }
314 
315         /// Squared Euclidean distance between this vector and another one
316         /// Returns: squared Euclidean distance.
317         @nogc T squaredDistanceTo(Vector v) pure const nothrow
318         {
319             return (v - this).squaredMagnitude();
320         }
321 
322         static if (isFloatingPoint!T)
323         {
324             deprecated("Use magnitude instead") alias length = magnitude;
325             /// Euclidean length of the vector
326             /// Returns: Euclidean length
327             @nogc T magnitude() pure const nothrow
328             {
329                 return sqrt(squaredMagnitude());
330             }
331 
332             deprecated("Use inverseMagnitude instead") alias inverseLength = inverseMagnitude;
333             /// Inverse Euclidean length of the vector
334             /// Returns: Inverse of Euclidean length.
335             @nogc T inverseMagnitude() pure const nothrow
336             {
337                 return 1 / sqrt(squaredMagnitude());
338             }
339 
340             deprecated("Use fastInverseMagnitude instead") alias fastInverseLength = fastInverseMagnitude;
341             /// Faster but less accurate inverse of Euclidean length.
342             /// Returns: Inverse of Euclidean length.
343             @nogc T fastInverseMagnitude() pure const nothrow
344             {
345                 return inverseSqrt(squaredMagnitude());
346             }
347 
348             /// Euclidean distance between this vector and another one
349             /// Returns: Euclidean distance between this and other.
350             @nogc T distanceTo(Vector other) pure const nothrow
351             {
352                 return (other - this).magnitude();
353             }
354 
355             /// In-place normalization.
356             @nogc void normalize() pure nothrow
357             {
358                 auto invMag = inverseMagnitude();
359                 mixin(generateLoopCode!("v[@] *= invMag;", N)());
360             }
361 
362             /// Returns a normalized copy of this Vector
363             /// Returns: Normalized vector.
364             @nogc Vector normalized() pure const nothrow
365             {
366                 Vector res = this;
367                 res.normalize();
368                 return res;
369             }
370 
371             /// Faster but less accurate in-place normalization.
372             @nogc void fastNormalize() pure nothrow
373             {
374                 auto invLength = fastInverseMagnitude();
375                 mixin(generateLoopCode!("v[@] *= invLength;", N)());
376             }
377 
378             /// Faster but less accurate vector normalization.
379             /// Returns: Normalized vector.
380             @nogc Vector fastNormalized() pure const nothrow
381             {
382                 Vector res = this;
383                 res.fastNormalize();
384                 return res;
385             }
386 
387             static if (N == 3)
388             {
389                 /// Gets an orthogonal vector from a 3-dimensional vector.
390                 /// Doesn’t normalize the output.
391                 /// Authors: Sam Hocevar
392                 /// See_also: Source at $(WEB lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts).
393                 @nogc Vector getOrthogonalVector() pure const nothrow
394                 {
395                     return abs(x) > abs(z) ? Vector(-y, x, 0.0) : Vector(0.0, -z, y);
396                 }
397             }
398         }
399     }
400 
401     private
402     {
403         enum _N = N;
404         alias T _T;
405 
406         // define types that can be converted to this, but are not the same type
407         template isConvertible(T)
408         {
409             enum bool isConvertible = (!is(T : Vector))
410             && is(typeof(
411                 {
412                     T x;
413                     Vector v = x;
414                 }()));
415         }
416 
417         // define types that can't be converted to this
418         template isForeign(T)
419         {
420             enum bool isForeign = (!isConvertible!T) && (!is(T: Vector));
421         }
422 
423         template isValidSwizzle(string op, int lastSwizzleClass = -1)
424         {
425             static if (op.length == 0)
426                 enum bool isValidSwizzle = true;
427             else
428             {
429                 enum len = op.length;
430                 enum int swizzleClass = swizzleClassify!(op[0]);
431                 enum bool swizzleClassValid = (lastSwizzleClass == -1 || (swizzleClass == lastSwizzleClass));
432                 enum bool isValidSwizzle = (swizzleIndex!(op[0]) != -1)
433                                          && swizzleClassValid
434                                          && isValidSwizzle!(op[1..len], swizzleClass);
435             }
436         }
437 
438         template searchElement(char c, string s)
439         {
440             static if (s.length == 0)
441             {
442                 enum bool result = false;
443             }
444             else
445             {
446                 enum string tail = s[1..s.length];
447                 enum bool result = (s[0] == c) || searchElement!(c, tail).result;
448             }
449         }
450 
451         template hasNoDuplicates(string s)
452         {
453             static if (s.length == 1)
454             {
455                 enum bool result = true;
456             }
457             else
458             {
459                 enum tail = s[1..s.length];
460                 enum bool result = !(searchElement!(s[0], tail).result) && hasNoDuplicates!(tail).result;
461             }
462         }
463 
464         // true if the swizzle has at the maximum one time each letter
465         template isValidSwizzleUnique(string op)
466         {
467             static if (isValidSwizzle!op)
468                 enum isValidSwizzleUnique = hasNoDuplicates!op.result;
469             else
470                 enum bool isValidSwizzleUnique = false;
471         }
472 
473         template swizzleIndex(char c)
474         {
475             static if((c == 'x' || c == 'r') && N >= 1)
476                 enum swizzleIndex = 0;
477             else static if((c == 'y' || c == 'g') && N >= 2)
478                 enum swizzleIndex = 1;
479             else static if((c == 'z' || c == 'b') && N >= 3)
480                 enum swizzleIndex = 2;
481             else static if ((c == 'w' || c == 'a') && N >= 4)
482                 enum swizzleIndex = 3;
483             else
484                 enum swizzleIndex = -1;
485         }
486 
487         template swizzleClassify(char c)
488         {
489             static if(c == 'x' || c == 'y' || c == 'z' || c == 'w')
490                 enum swizzleClassify = 0;
491             else static if(c == 'r' || c == 'g' || c == 'b' || c == 'a')
492                 enum swizzleClassify = 1;
493             else
494                 enum swizzleClassify = -1;
495         }
496 
497         template swizzleTuple(string op)
498         {
499             enum opLength = op.length;
500             static if (op.length == 0)
501                 enum swizzleTuple = [];
502             else
503                 enum swizzleTuple = [ swizzleIndex!(op[0]) ] ~ swizzleTuple!(op[1..op.length]);
504         }
505     }
506 }
507 
508 /// True if `T` is some kind of `Vector`
509 enum isVector(T) = is(T : Vector!U, U...);
510 
511 // Previous name, but the alias doesn't seem to show deprecation messages
512 deprecated("Use isVector instead") alias isVectorInstantiation(T) = isVector!T;
513 
514 ///
515 unittest
516 {
517     static assert(isVector!vec2f);
518     static assert(isVector!vec3d);
519     static assert(isVector!(vec4!real));
520     static assert(!isVector!float);
521 }
522 
523 /// Get the numeric type used to measure a vectors's coordinates.
524 alias DimensionType(T : Vector!U, U...) = U[0];
525 
526 ///
527 unittest
528 {
529     static assert(is(DimensionType!vec2f == float));
530     static assert(is(DimensionType!vec3d == double));
531 }
532 
533 ///
534 template vec2(T) { alias Vector!(T, 2) vec2; }
535 ///
536 template vec3(T) { alias Vector!(T, 3) vec3; }
537 ///
538 template vec4(T) { alias Vector!(T, 4) vec4; }
539 
540 alias vec2!byte   vec2b;  ///
541 alias vec2!ubyte  vec2ub; ///
542 alias vec2!short  vec2s;  ///
543 alias vec2!ushort vec2us; ///
544 alias vec2!int    vec2i;  ///
545 alias vec2!uint   vec2ui; ///
546 alias vec2!long   vec2l;  ///
547 alias vec2!ulong  vec2ul; ///
548 alias vec2!float  vec2f;  ///
549 alias vec2!double vec2d;  ///
550 
551 alias vec3!byte   vec3b;  ///
552 alias vec3!ubyte  vec3ub; ///
553 alias vec3!short  vec3s;  ///
554 alias vec3!ushort vec3us; ///
555 alias vec3!int    vec3i;  ///
556 alias vec3!uint   vec3ui; ///
557 alias vec3!long   vec3l;  ///
558 alias vec3!ulong  vec3ul; ///
559 alias vec3!float  vec3f;  ///
560 alias vec3!double vec3d;  ///
561 
562 alias vec4!byte   vec4b;  ///
563 alias vec4!ubyte  vec4ub; ///
564 alias vec4!short  vec4s;  ///
565 alias vec4!ushort vec4us; ///
566 alias vec4!int    vec4i;  ///
567 alias vec4!uint   vec4ui; ///
568 alias vec4!long   vec4l;  ///
569 alias vec4!ulong  vec4ul; ///
570 alias vec4!float  vec4f;  ///
571 alias vec4!double vec4d;  ///
572 
573 private
574 {
575     static string generateLoopCode(string formatString, int N)() pure nothrow
576     {
577         string result;
578         for (int i = 0; i < N; ++i)
579         {
580             string index = ctIntToString(i);
581             // replace all @ by indices
582             result ~= formatString.replace("@", index);
583         }
584         return result;
585     }
586 
587     // Speed-up CTFE conversions
588     static string ctIntToString(int n) pure nothrow
589     {
590         static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
591         if (n < 10)
592             return table[n];
593         else
594             return to!string(n);
595     }
596 }
597 
598 
599 /// Element-wise minimum.
600 @nogc Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
601 {
602     import std.algorithm: min;
603     Vector!(T, N) res = void;
604     mixin(generateLoopCode!("res.v[@] = min(a.v[@], b.v[@]);", N)());
605     return res;
606 }
607 
608 /// Element-wise maximum.
609 @nogc Vector!(T, N) maxByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
610 {
611     import std.algorithm: max;
612     Vector!(T, N) res = void;
613     mixin(generateLoopCode!("res.v[@] = max(a.v[@], b.v[@]);", N)());
614     return res;
615 }
616 
617 /// Element-wise absolute value.
618 @nogc Vector!(T, N) absByElem(T, int N)(const Vector!(T, N) a) pure nothrow
619 {
620     Vector!(T, N) res = void;
621     mixin(generateLoopCode!("res.v[@] = abs(a.v[@]);", N)());
622     return res;
623 }
624 
625 /// Dot product of two vectors
626 /// Returns: Dot product.
627 @nogc T dot(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
628 {
629     T sum = 0;
630     mixin(generateLoopCode!("sum += a.v[@] * b.v[@];", N)());
631     return sum;
632 }
633 
634 /// Cross product of two 3D vectors
635 /// Returns: 3D cross product.
636 /// Thanks to vuaru for corrections.
637 @nogc Vector!(T, 3) cross(T)(const Vector!(T, 3) a, const Vector!(T, 3) b) pure nothrow
638 {
639     return Vector!(T, 3)(a.y * b.z - a.z * b.y,
640                          a.z * b.x - a.x * b.z,
641                          a.x * b.y - a.y * b.x);
642 }
643 
644 /// 3D reflect, like the GLSL function.
645 /// Returns: a reflected by normal b.
646 @nogc Vector!(T, N) reflect(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
647 {
648     return a - (2 * dot(b, a)) * b;
649 }
650 
651 ///
652 @nogc unittest
653 {
654     // reflect a 2D vector across the x axis (the normal points along the y axis)
655     assert(vec2f(1,1).reflect(vec2f(0,1)) == vec2f(1,-1));
656     assert(vec2f(1,1).reflect(vec2f(0,-1)) == vec2f(1,-1));
657 
658     // note that the normal must be, well, normalized:
659     assert(vec2f(1,1).reflect(vec2f(0,20)) != vec2f(1,-1));
660 
661     // think of this like a ball hitting a flat floor at an angle.
662     // the x and y components remain unchanged, and the z inverts
663     assert(vec3f(2,3,-0.5).reflect(vec3f(0,0,1)) == vec3f(2,3,0.5));
664 }
665 
666 /// Angle between two vectors
667 /// Returns: angle between vectors.
668 /// See_also: "The Right Way to Calculate Stuff" at $(WEB www.plunk.org/~hatch/rightway.php)
669 @nogc T angleBetween(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
670 {
671     auto aN = a.normalized();
672     auto bN = b.normalized();
673     auto dp = dot(aN, bN);
674 
675     if (dp < 0)
676         return PI - 2 * asin((-bN-aN).magnitude / 2);
677     else
678         return 2 * asin((bN-aN).magnitude / 2);
679 }
680 
681 static assert(vec2f.sizeof == 8);
682 static assert(vec3d.sizeof == 24);
683 static assert(vec4i.sizeof == 16);
684 
685 unittest
686 {
687     static assert(vec2i.isValidSwizzle!"xyx");
688     static assert(!vec2i.isValidSwizzle!"xyz");
689     static assert(vec4i.isValidSwizzle!"brra");
690     static assert(!vec4i.isValidSwizzle!"rgyz");
691     static assert(vec2i.isValidSwizzleUnique!"xy");
692     static assert(vec2i.isValidSwizzleUnique!"yx");
693     static assert(!vec2i.isValidSwizzleUnique!"xx");
694 
695     assert(vec2l(0, 1) == vec2i(0, 1));
696 
697     int[2] arr = [0, 1];
698     int[] arr2 = new int[2];
699     arr2[] = arr[];
700     vec2i a = vec2i([0, 1]);
701     vec2i a2 = vec2i(0, 1);
702     immutable vec2i b = vec2i(0);
703     assert(b[0] == 0 && b[1] == 0);
704     vec2i c = arr;
705     vec2l d = arr2;
706     assert(a == a2);
707     assert(a == c);
708     assert(vec2l(a) == vec2l(a));
709     assert(vec2l(a) == d);
710 
711     vec4i x = [4, 5, 6, 7];
712     assert(x == x);
713     --x[0];
714     assert(x[0] == 3);
715     ++x[0];
716     assert(x[0] == 4);
717     x[1] &= 1;
718     x[2] = 77 + x[2];
719     x[3] += 3;
720     assert(x == [4, 1, 83, 10]);
721     assert(x.xxywz == [4, 4, 1, 10, 83]);
722     assert(x.xxxxxxx == [4, 4, 4, 4, 4, 4, 4]);
723     assert(x.abgr == [10, 83, 1, 4]);
724     assert(a != b);
725     x = vec4i(x.xyz, 166);
726     assert(x == [4, 1, 83, 166]);
727 
728     vec2l e = a;
729     vec2l f = a + b;
730     assert(f == vec2l(a));
731 
732     vec3ui g = vec3i(78,9,4);
733     g ^= vec3i(78,9,4);
734     assert(g == vec3ui(0));
735     //g[0..2] = 1u;
736     //assert(g == [2, 1, 0]);
737 
738     assert(vec2i(4, 5) + 1 == vec2i(5,6));
739     assert(vec2i(4, 5) - 1 == vec2i(3,4));
740     assert(1 + vec2i(4, 5) == vec2i(5,6));
741     assert(vec3f(1,1,1) * 0 == 0);
742     assert(1.0 * vec3d(4,5,6) == vec3f(4,5.0f,6.0));
743 
744     auto dx = vec2i(1,2);
745     auto dy = vec2i(4,5);
746     auto dp = dot(dx, dy);
747     assert(dp == 14 );
748 
749     vec3i h = cast(vec3i)(vec3d(0.5, 1.1, -2.2));
750     assert(h == [0, 1, -2]);
751     assert(h[] == [0, 1, -2]);
752     assert(h[1..3] == [1, -2]);
753     assert(h.zyx == [-2, 1, 0]);
754 
755     h.yx = vec2i(5, 2); // swizzle assignment
756 
757     assert(h.xy == [2, 5]);
758     assert(-h[1] == -5);
759     assert(++h[0] == 3);
760 
761     //assert(h == [-2, 1, 0]);
762     assert(!__traits(compiles, h.xx = h.yy));
763     vec4ub j;
764 
765     assert(lerp(vec2f(-10, -1), vec2f(10, 1), 0.5) == vec2f(0, 0));
766 
767     // larger vectors
768     alias Vector!(float, 5) vec5f;
769     vec5f l = vec5f(1, 2.0f, 3.0, 4u, 5.0L);
770     l = vec5f(l.xyz, vec2i(1, 2));
771 
772     // the ctor should not compile if given too many arguments
773     static assert(!is(typeof(vec2f(1, 2, 3))));
774     static assert(!is(typeof(vec2f(vec2f(1, 2), 3))));
775     static assert( is(typeof(vec3f(vec2f(1, 2), 3))));
776     static assert( is(typeof(vec3f(1, 2, 3))));
777 
778     assert(absByElem(vec3i(-1, 0, 2)) == vec3i(1, 0, 2));
779 }
780