1 /**
2  * Type and dimension generic Vector backed by a static array.
3  */
4 module bettercmath.vector;
5 
6 import std.algorithm : among, copy, max, min, sum;
7 import std.range;
8 import std.traits : CommonType;
9 
10 import bettercmath.cmath;
11 import bettercmath.misc : FloatType;
12 /// `lerp` function for UFCS
13 public import bettercmath.misc : lerp;
14 
15 @safe @nogc nothrow:
16 
17 version (unittest)
18 {
19     private alias Vec1 = Vector!(float, 1);
20     private alias Vec2 = Vector!(float, 2);
21     private alias Vec2i = Vector!(int, 2);
22     private alias Vec3 = Vector!(float, 3);
23     private alias Vec4 = Vector!(float, 4);
24     private alias Vec4bool = Vector!(bool, 4);
25 }
26 
27 
28 /**
29  * Generic Vector backed by a static array.
30  * 
31  * Params:
32  *   T = Element type
33  *   N = Vector dimension, must be positive
34  */
35 struct Vector(T, uint N)
36 if (N > 0)
37 {
38     /// Alias for Vector element type.
39     alias ElementType = T;
40     /// Vector dimension.
41     enum dimension = N;
42     /// Element array.
43     T[N] elements = 0;
44     alias elements this;
45 
46     private ref inout(T) _get(size_t i)() inout pure
47     in { assert(i < N, "Index out of bounds"); }
48     do
49     {
50         return elements[i];
51     }
52     private ref inout(T[to - from]) _slice(size_t from, size_t to)() inout pure
53     in { assert(from <= N - 1 && to <= N, "Index out of bounds"); }
54     do
55     {
56         return elements[from .. to];
57     }
58 
59     /// Get a reference to first element.
60     alias x = _get!(0);
61     /// Ditto
62     alias r = x;
63     /// Ditto
64     alias u = x;
65     /// Ditto
66     alias s = x;
67     /// Ditto
68     alias width = x;
69 
70     static if (N >= 2)
71     {
72         /// Get a reference to second element.
73         alias y = _get!(1);
74         /// Ditto
75         alias g = y;
76         /// Ditto
77         alias v = y;
78         /// Ditto
79         alias t = y;
80         /// Ditto
81         alias height = y;
82 
83         /// Get a reference to the first and second elements.
84         alias xy = _slice!(0, 2);
85         /// Ditto
86         alias rg = xy;
87         /// Ditto
88         alias uv = xy;
89         /// Ditto
90         alias st = xy;
91     }
92     static if (N >= 3)
93     {
94         /// Get a reference to third element.
95         alias z = _get!(2);
96         /// Ditto
97         alias b = z;
98         /// Ditto
99         alias p = z;
100         /// Ditto
101         alias depth = z;
102 
103         /// Get a reference to the second and third elements.
104         alias yz = _slice!(1, 3);
105         /// Ditto
106         alias gb = yz;
107         /// Ditto
108         alias tp = yz;
109 
110         /// Get a reference to the first, second and third elements.
111         alias xyz = _slice!(0, 3);
112         /// Ditto
113         alias rgb = xyz;
114         /// Ditto
115         alias stp = xyz;
116     }
117     static if (N >= 4)
118     {
119         /// Get a reference to fourth element.
120         alias w = _get!(3);
121         /// Ditto
122         alias a = w;
123         /// Ditto
124         alias q = w;
125 
126         /// Get a reference to the third and fourth elements.
127         alias zw = _slice!(2, 4);
128         /// Ditto
129         alias ba = zw;
130         /// Ditto
131         alias pq = zw;
132 
133         /// Get a reference to the second, third and fourth elements.
134         alias yzw = _slice!(1, 4);
135         /// Ditto
136         alias gba = yzw;
137         /// Ditto
138         alias tpq = yzw;
139 
140         /// Get a reference to the first, second, third and fourth elements.
141         alias xyzw = _slice!(0, 4);
142         /// Ditto
143         alias rgba = xyzw;
144         /// Ditto
145         alias stpq = xyzw;
146     }
147 
148     unittest
149     {
150         Vec2 v = [1, 2];
151         assert(v.x == 1);
152         assert(v.x == v[0]);
153         assert(v.y == 2);
154         assert(v.y == v[1]);
155         v.x = 2;
156         assert(v.r == 2);
157 
158         Vec4 v2 = [1, 2, 3, 4];
159         assert(v2.xy == [1, 2]);
160         assert(v2.yz == [2, 3]);
161         assert(v2.zw == [3, 4]);
162         v2.xyz = 0;
163         assert(v2 == [0, 0, 0, 4]);
164     }
165 
166     /// Constructs a Vector with all elements equal to `scalar`
167     this(const T scalar) pure
168     {
169         elements[] = scalar;
170     }
171     ///
172     unittest
173     {
174         alias Vec2 = Vector!(float, 2);
175         Vec2 v;
176         v = Vec2(1);
177         assert(v == [1, 1]);
178         v = Vec2(2.0);
179         assert(v == [2, 2]);
180         v = Vec2(3.0f);
181         assert(v == [3, 3]);
182     }
183     /// Constructs a Vector from static array.
184     this()(const auto ref T[N] values) pure
185     {
186         elements = values;
187     }
188     ///
189     unittest
190     {
191         alias Vec2 = Vector!(float, 2);
192         Vec2 v;
193         v = Vec2([1, 2]);
194         assert(v == [1f, 2f]);
195         v = Vec2([2.0, 3.0]);
196         assert(v == [2f, 3f]);
197         v = Vec2([3.0f, 4.0f]);
198         assert(v == [3f, 4f]);
199     }
200     /// Constructs a Vector with elements from an Input Range.
201     /// Values not provided by `range` are initialized to 0,
202     /// while additional values are ignored.
203     this(R)(auto ref R range)
204     if (isInputRange!R)
205     {
206         auto remainder = range.take(N).copy(elements[]);
207         remainder[] = 0;
208     }
209     ///
210     unittest
211     {
212         float[] values = [1, 2];
213         assert(Vec4(values) == [1, 2, 0, 0]);
214 
215         import std.range : iota, stride;
216         assert(Vec4(iota(4)) == [0, 1, 2, 3]);
217         assert(Vec4(iota(8).stride(2)) == [0, 2, 4, 6]);
218         assert(Vec4(iota(6)) == [0, 1, 2, 3]);
219 
220         import std.algorithm : map;
221         auto isEven = iota(1024).map!(x => x % 2 == 0);
222         assert(Vec4bool(isEven) == [true, false, true, false]);
223     }
224     /// Constructs a Vector with all elements initialized separetely
225     this(Args...)(const auto ref Args args)
226     if (args.length <= N)
227     {
228         this(only(args));
229     }
230     ///
231     unittest
232     {
233         Vec3 v;
234         v = Vec3(1, 2);
235         assert(v == [1, 2, 0]);
236         v = Vec3(1, 2, 3);
237         assert(v == [1, 2, 3]);
238     }
239 
240 pure:
241     /// Vector with all zeros
242     enum Vector zeros = 0;
243     alias zeroes = zeros;
244     /// Vector with all ones
245     enum Vector ones = 1;
246 
247     /// Returns a new vector with unary operator applied to all elements
248     Vector opUnary(string op)() const
249     if (op.among("-", "+", "~"))
250     {
251         Vector result;
252         mixin(q{result =} ~ op ~ q{elements[];});
253         return result;
254     }
255     ///
256     unittest
257     {
258         assert(-Vec2(1, -2) == [-1, 2]);
259     }
260 
261     /// Returns a new vector with binary operator applied to all elements and `scalar`
262     Vector opBinary(string op)(const T scalar) const
263     if (!op.among("~", "<<", ">>", ">>>"))
264     {
265         Vector result;
266         mixin(q{result = elements[]} ~ op ~ q{scalar;});
267         return result;
268     }
269     ///
270     unittest
271     {
272         Vec2 a = [1, 2];
273         assert(a + 1 == [1f + 1f, 2f + 1f]);
274         assert(a - 1 == [1f - 1f, 2f - 1f]);
275         assert(a * 2 == [1f * 2f, 2f * 2f]);
276         assert(a / 2 == [1f / 2f, 2f / 2f]);
277         assert(a % 2 == [1f % 2f, 2f % 2f]);
278         assert(a ^^ 2 == [1f ^^ 2f, 2f ^^ 2f]);
279 
280         Vec2i b = [1, 2];
281         assert((b & 1) == [1 & 1, 2 & 1]);
282         assert((b | 1) == [1 | 1, 2 | 1]);
283         assert((b ^ 1) == [1 ^ 1, 2 ^ 1]);
284     }
285     // TODO: shift operations
286 
287     /// Returns a new vector with binary operator applied to all elements and `scalar`
288     Vector opBinaryRight(string op)(const T scalar) const
289     if (!op.among("~", "<<", ">>", ">>>"))
290     {
291         Vector result;
292         mixin(q{result = scalar} ~ op ~ q{elements[];});
293         return result;
294     }
295     ///
296     unittest
297     {
298         Vec2 a = [1, 2];
299         assert(1 + a == [1f + 1f, 1f + 2f]);
300         assert(1 - a == [1f - 1f, 1f - 2f]);
301         assert(2 * a == [2f * 1f, 2f * 2f]);
302         assert(2 / a == [2f / 1f, 2f / 2f]);
303         assert(2 % a == [2f % 1f, 2f % 2f]);
304         assert(2 ^^ a == [2f ^^ 1f, 2f ^^ 2f]);
305 
306         Vec2i b = [1, 2];
307         assert((1 & b) == [1 & 1, 1 & 2]);
308         assert((1 | b) == [1 | 1, 1 | 2]);
309         assert((1 ^ b) == [1 ^ 1, 1 ^ 2]);
310     }
311 
312     /// Returns a new vector with the results of applying operator against elements of `other`.
313     /// If operands dimensions are unequal, copies the values from greater dimension vector.
314     Vector!(T, max(N, M)) opBinary(string op, uint M)(const auto ref T[M] other) const
315     if (op != "~")
316     {
317         enum minDimension = min(N, M);
318         typeof(return) result;
319         mixin(q{result[0 .. minDimension] = elements[0 .. minDimension]} ~ op ~ q{other[0 .. minDimension];});
320         static if (M < N)
321         {
322             result[minDimension .. N] = elements[minDimension .. N];
323         }
324         else static if (N < M)
325         {
326             result[minDimension .. M] = other[minDimension .. M];
327         }
328         return result;
329     }
330     ///
331     unittest
332     {
333         assert(Vec2(1, 2) + Vec2(3, 4) == [1f+3f, 2f+4f]);
334         assert(Vec2(1, 2) - Vec2(3, 4) == [1f-3f, 2f-4f]);
335         assert(Vec2(1, 2) * Vec2(3, 4) == [1f*3f, 2f*4f]);
336         assert(Vec2(1, 2) / Vec2(3, 4) == [1f/3f, 2f/4f]);
337         assert(__traits(compiles, Vec2(1, 2) + [3, 4]));
338 
339         assert(Vec2(1, 2) + Vec1(3) == [1f+3f, 2f]);
340         assert(Vec2(1, 2) - Vec1(3) == [1f-3f, 2f]);
341         assert(Vec2(1, 2) * Vec1(3) == [1f*3f, 2f]);
342         assert(Vec2(1, 2) / Vec1(3) == [1f/3f, 2f]);
343 
344         assert(Vec2(1, 2) + Vec3(3, 4, 5) == [1f+3f, 2f+4f, 5f]);
345         assert(Vec2(1, 2) - Vec3(3, 4, 5) == [1f-3f, 2f-4f, 5f]);
346         assert(Vec2(1, 2) * Vec3(3, 4, 5) == [1f*3f, 2f*4f, 5f]);
347         assert(Vec2(1, 2) / Vec3(3, 4, 5) == [1f/3f, 2f/4f, 5f]);
348     }
349 
350     /// Returns a new vector of greater dimension by copying elements and appending `scalar`.
351     Vector!(T, N + 1) opBinary(string op : "~")(const T scalar) const
352     {
353         typeof(return) result;
354         result[0 .. N] = elements[];
355         result[N] = scalar;
356         return result;
357     }
358     ///
359     unittest
360     {
361         Vec2 v = [1, 2];
362         assert(v ~ 3 == Vec3(1, 2, 3));
363     }
364     /// Returns a new vector of greater dimension by copying elements and prepending `scalar`.
365     Vector!(T, N + 1) opBinaryRight(string op : "~")(const T scalar) const
366     {
367         typeof(return) result;
368         result[0] = scalar;
369         result[1 .. N + 1] = elements[];
370         return result;
371     }
372     ///
373     unittest
374     {
375         Vec2 v = [1, 2];
376         Vec3 v2 = 0f ~ v;
377         assert(0 ~ v == Vec3(0, 1, 2));
378     }
379 
380     /// Returns a new vector of greater dimension by copying elements and appending values from `other`.
381     Vector!(T, N + M) opBinary(string op : "~", uint M)(const auto ref T[M] other) const
382     {
383         typeof(return) result = elements ~ other;
384         return result;
385     }
386     ///
387     unittest
388     {
389         Vec2 v1 = [1, 2];
390         assert(v1 ~ [3f, 4f] == Vec4(1, 2, 3, 4));
391         assert(v1 ~ Vec2(3f, 4f) == Vec4(1, 2, 3, 4));
392     }
393     /// Returns a new vector of greater dimension by copying elements and prepending values from `other`.
394     Vector!(T, N + M) opBinaryRight(string op : "~", uint M)(const auto ref T[M] other) const
395     {
396         typeof(return) result = other ~ elements;
397         return result;
398     }
399     ///
400     unittest
401     {
402         Vec2 v1 = [1, 2];
403         assert([3f, 4f] ~ v1 == Vec4(3, 4, 1, 2));
404         assert(Vec2(3f, 4f) ~ v1 == Vec4(3, 4, 1, 2));
405     }
406 
407     /// Cast to a static array of same dimension, but different element type.
408     U opCast(U : T2[N], T2)() const
409     {
410         typeof(return) result;
411         foreach (i; 0 .. N)
412         {
413             result[i] = cast(T2) elements[i];
414         }
415         return result;
416     }
417     ///
418     unittest
419     {
420         Vec2i intVec = [1, 2];
421         auto floatVec = cast(Vec2) intVec;
422         assert(floatVec == Vec2(1f, 2f));
423         assert(floatVec == intVec);
424 
425         auto floatArray = cast(float[2]) intVec;
426         assert(floatArray == [1f, 2f]);
427         assert(floatArray == intVec);
428     }
429 
430     /// Assign result of applying operator with `scalar` to elements.
431     ref Vector opOpAssign(string op)(const T scalar) return
432     if (!op.among("~", "<<", ">>", ">>>"))
433     {
434         mixin(q{elements[] } ~ op ~ q{= scalar;});
435         return this;
436     }
437     ///
438     unittest
439     {
440         Vec2i v = [1, 2];
441         v += 5;
442         assert(v == [6, 7]);
443         v -= 4;
444         assert(v == [2, 3]);
445         v *= -1;
446         assert(v == [-2, -3]);
447         v %= 3;
448         assert(v == [-2, 0]);
449         v |= 1;
450         assert(v == [-1, 1]);
451         v /= 2;
452         assert(v == [0, 0]); // integer division
453     }
454     /// Assign result of applying operator with `other` to elements.
455     ref Vector opOpAssign(string op, uint M)(const auto ref T[M] other) return
456     if (!op.among("~", "<<", ">>", ">>>"))
457     {
458         enum minDimension = min(N, M);
459         mixin(q{elements[0 .. minDimension] } ~ op ~ q{= other[0 .. minDimension];});
460         return this;
461     }
462     ///
463     unittest
464     {
465         Vec3 v = [1, 2, 3];
466         v += Vec2(1, 2);
467         assert(v == [2, 4, 3]);
468 
469         v += Vec4(1, 2, 3, 4);
470         assert(v == [3, 6, 6]);
471     }
472 
473     unittest
474     {
475         assert(Vec2.sizeof == Vec2.elements.sizeof);
476         assert(Vec3.sizeof == Vec3.elements.sizeof);
477         assert(Vec4.sizeof == Vec4.elements.sizeof);
478 
479         alias Vec2_100 = Vec2[100];
480         assert(Vec2_100.sizeof == 100 * Vec2.elements.sizeof);
481 
482         auto v = Vec2(5);
483         v += 3;
484         assert(v == Vec2(8));
485     }
486 }
487 
488 /// True if `T` is some kind of Vector
489 enum isVector(T) = is(T : Vector!U, U...);
490 
491 /// Construct Vector directly from static array, inferring element type.
492 Vector!(T, N) vector(T, uint N)(const auto ref T[N] elements)
493 {
494     return typeof(return)(elements);
495 }
496 ///
497 unittest
498 {
499     auto v = [1, 2, 3].vector;
500     assert(v.elements == [1, 2, 3]);
501     assert(is(typeof(v) == Vector!(int, 3)));
502 }
503 
504 /// Construct Vector directly from elements, inferring element type.
505 Vector!(CommonType!Args, Args.length) vector(Args...)(const auto ref Args args)
506 if (!is(CommonType!Args == void))
507 {
508     return typeof(return)(args);
509 }
510 ///
511 unittest
512 {
513     auto v = vector(1f, 2, 3);
514     assert(is(typeof(v) == Vector!(float, 3)));
515     assert(v == [1f, 2f, 3f]);
516 }
517 
518 /// Returns the dot product between two Vectors.
519 T dot(T, uint N)(const auto ref Vector!(T, N) a, const auto ref Vector!(T, N) b) pure
520 {
521     auto multiplied = a * b;
522     return multiplied[].sum;
523 }
524 
525 /// Returns the cross product between two 3D Vectors.
526 Vector!(T, 3) cross(T)(const auto ref Vector!(T, 3) a, const auto ref Vector!(T, 3) b) pure
527 {
528     typeof(return) result = [
529         a.y * b.z - a.z * b.y,
530         a.z * b.x - a.x * b.z,
531         a.x * b.y - a.y * b.x,
532     ];
533     return result;
534 }
535 
536 /// Returns a Vector that is the reflection of `vec` against `normal`.
537 Vector!(T, N) reflect(T, uint N)(const auto ref Vector!(T, N) vec, const auto ref Vector!(T, N) normal) pure
538 {
539     return vec - (2 * normal * dot(vec, normal));
540 }
541 
542 /// Returns the squared magnitude (Euclidean length) of a Vector.
543 T magnitudeSquared(T, uint N)(const auto ref Vector!(T, N) vec) pure
544 out (r) { assert(r >= 0, "Vector squared magnitude should be non-negative!"); }
545 do
546 {
547     return dot(vec, vec);
548 }
549 ///
550 unittest
551 {
552     assert(Vec2(0, 0).magnitudeSquared() == 0);
553     assert(Vec2(1, 0).magnitudeSquared() == 1);
554     assert(Vec2(0, 1).magnitudeSquared() == 1);
555     assert(Vec2(1, 1).magnitudeSquared() == 2);
556     assert(Vec2(2, 0).magnitudeSquared() == 4);
557     assert(Vec2(1, 2).magnitudeSquared() == 5);
558 }
559 
560 /// Returns the magnitude (Euclidean length) of a Vector.
561 auto magnitude(T, uint N)(const auto ref Vector!(T, N) vec)
562 out (r) { assert(r >= 0, "Vector magnitude should be non-negative!"); }
563 do
564 {
565     return sqrt(vec.magnitudeSquared());
566 }
567 ///
568 unittest
569 {
570     assert(Vec2(0, 0).magnitude() == 0);
571     assert(Vec2(1, 0).magnitude() == 1);
572     assert(Vec2(0, 1).magnitude() == 1);
573     assert(Vec2(1, 1).magnitude() == sqrt(2f));
574     assert(Vec2(2, 0).magnitude() == 2);
575 }
576 
577 /// Normalize a Vector inplace.
578 ref Vector!(T, N) normalize(T, uint N)(ref return Vector!(T, N) vec)
579 {
580     auto sqMag = vec.magnitudeSquared();
581     if (sqMag != 0)
582     {
583         enum FloatType!T one = 1;
584         const auto inverseMag = one / sqrt(sqMag);
585         vec *= inverseMag;
586     }
587     return vec;
588 }
589 ///
590 unittest
591 {
592     Vec2 v = [5, 0];
593     v.normalize();
594     assert(v == Vec2(1, 0));
595 }
596 
597 /// Returns a normalized copy of Vector.
598 Vector!(T, N) normalized(T, uint N)(const auto ref Vector!(T, N) vec)
599 {
600     typeof(return) copy = vec;
601     return copy.normalize();
602 }
603 ///
604 unittest
605 {
606     Vec2 v = [200, 0];
607     assert(v.normalized() == Vec2(1, 0));
608     assert(v == Vec2(200, 0));
609 }
610 
611 unittest
612 {
613     Vec2 a = [1, 1];
614     Vec2 b = [2, 3];
615     assert(lerp(a, b, 0) == a);
616     assert(lerp(a, b, 0.5) == Vec2(1.5, 2));
617     assert(lerp(a, b, 1) == b);
618 }