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