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