1 module dath.vector;
2 
3 version(unittest) import fluent.asserts;
4 
5 import std.traits;
6 
7 public alias Vec2f = Vec!(float, 2);
8 public alias Vec3f = Vec!(float, 3);
9 public alias Vec4f = Vec!(float, 4);
10 
11 public alias Vec2 = Vec2f;
12 public alias Vec3 = Vec3f;
13 public alias Vec4 = Vec4f;
14 
15 public alias Vec2d = Vec!(double, 2);
16 public alias Vec3d = Vec!(double, 3);
17 public alias Vec4d = Vec!(double, 4);
18 
19 public alias Vec2i = Vec!(int, 2);
20 public alias Vec3i = Vec!(int, 3);
21 public alias Vec4i = Vec!(int, 4);
22 
23 public alias Vec2u = Vec!(uint, 2);
24 public alias Vec3u = Vec!(uint, 3);
25 public alias Vec4u = Vec!(uint, 4);
26 
27 /++
28  + Numeric Vector type with an optional amount of components.
29  +/
30 public struct Vec(T, ulong n) if (n >= 1 && isNumeric!T)
31 {
32     union {
33         /++
34          + Internal data.
35          +/
36         T[n] v;
37 
38         struct
39         {
40             // Predefined component names.
41 
42             static if (n >= 1)
43             {
44                 T x;
45             }
46 
47             static if (n >= 2)
48             {
49                 T y;
50             }
51 
52             static if (n >= 3)
53             {
54                 T z;
55             }
56 
57             static if (n >= 4)
58             {
59                 T w;
60             }
61         }
62     }
63 
64     private alias _t = T;
65     private enum _n = n;
66 
67     public this(U)(U[] elements) @nogc pure nothrow
68     {
69         assert(isNumeric!U, "All components must be numeric.");
70 
71         assert(elements.length > 0, "No components provided.");
72 
73         assert(elements.length == 1 || elements.length == n,
74             "Number of components must be either 1 or the number of components the vector holds.");
75         
76         v = elements;
77     }
78 
79     public this(U...)(U args) @nogc pure nothrow
80     {
81         static foreach (arg; args)
82         {
83             static assert(isNumeric!(typeof(arg)), "All components must be numeric");
84         }
85 
86         static assert(args.length > 0, "No components provided.");
87 
88         static assert(args.length == 1 || args.length == n,
89             "Number of components must be either 1 or the number of components the vector holds.");
90 
91         static if (args.length == 1)
92         {
93             v[] = args[0];
94         }
95         else
96         {
97             v = [args];
98         }
99     }
100 
101     /++
102      + Internal data as a pointer, use for sending data to shaders.
103      +/
104     public auto ptr() @nogc pure nothrow const
105     {
106         return v.ptr;
107     }
108 
109     /++ 
110      + Vector magnitude.
111      +/
112     public real magnitude() @nogc pure nothrow const
113     {
114         import std.math : sqrt;
115 
116         real sum = 0;
117         for (int i = 0; i < n; i++)
118         {
119             sum += v[i] * v[i];
120         }
121 
122         return sqrt(sum);
123     }
124 
125     /++
126      + Normalizes the vectors. Changes the current struct!
127      +/
128     public void normalize() @nogc pure nothrow
129     {
130         this = normalized();
131     }
132 
133     /++
134      + Returns the normalized vector. Doesn't change the current struct!
135      +/
136     public Vec!(T, n) normalized() @nogc pure nothrow const
137     {
138         real mag = magnitude();
139 
140         if (mag == 0)
141         {
142             return Vec!(T, n)(0);
143         }
144 
145         return this / mag;
146     }
147 
148     /++
149      + Returns the negated vector.
150      +/
151     public Vec!(T, n) opUnary(string s)() @nogc pure nothrow const if (s == "-")
152     {
153         Vec!(T, n) res;
154         res.v = -v[];
155         return res;
156     }
157 
158     /++
159      + Returns the mul of this * scalar.
160      +/
161     public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "*")
162     {
163         Vec!(T, n) res;
164         res.v = v[] * scalar;
165         return res;
166     }
167 
168     /++
169      + Returns the mul of this * scalar.
170      +/
171     public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "*")
172     {
173         v[] *= scalar;
174     }
175 
176     /++
177      + Returns the sum of this + scalar.
178      +/
179     public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "+")
180     {
181         Vec!(T, n) res;
182         res.v = v[] + scalar;
183         return res;
184     }
185 
186     /++
187      + Returns the sum of this + scalar.
188      +/
189     public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "+")
190     {
191         v[] += scalar;
192     }
193 
194     /++
195      + Returns the sub of this - scalar.
196      +/
197     public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "-")
198     {
199         Vec!(T, n) res;
200         res.v = v[] - scalar;
201         return res;
202     }
203 
204     /++
205      + Returns the sub of this - scalar.
206      +/
207     public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "-")
208     {
209         v[] -= scalar;
210     }
211 
212     /++
213      + Returns the div of this / scalar.
214      +/
215     public Vec!(T, n) opBinary(string s) (in float scalar) @nogc pure nothrow const if (s == "/")
216     {
217         Vec!(T, n) res;
218         res.v = v[] / cast(T) scalar;
219         return res;
220     }
221 
222     /++
223      + Returns the div of this / scalar.
224      +/
225     public void opOpAssign(string s) (in float scalar) @nogc pure nothrow if (s == "/")
226     {
227         v[] /= scalar;
228     }
229 
230     /++
231      + Returns the sum of 2 vectors.
232      +/
233     public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "+")
234     {
235         Vec!(T, n) res;
236         res.v = v[] + other.v[];
237         return res;
238     }
239 
240     /++
241      + Returns the sum of 2 vectors.
242      +/
243     public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "+")
244     {
245         v[] += other.v[];
246     }
247 
248     /++
249      + Returns the sub of 2 vectors.
250      +/
251     public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "-")
252     {
253         Vec!(T, n) res;
254         res.v = v[] - other.v[];
255         return res;
256     }
257 
258     /++
259      + Returns the sub of 2 vectors.
260      +/
261     public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "-")
262     {
263         v[] -= other.v[];
264     }
265 
266     /++
267      + Returns the mul of 2 vectors.
268      +/
269     public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "*")
270     {
271         Vec!(T, n) res;
272         res.v = v[] * other.v[];
273         return res;
274     }
275 
276     /++
277      + Returns the mul of 2 vectors.
278      +/
279     public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "*")
280     {
281         v[] *= other.v[];
282     }
283 
284     /++
285      + Returns the div of 2 vectors.
286      +/
287     public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "/")
288     {
289         Vec!(T, n) res;
290         res.v = v[] / other.v[];
291         return res;
292     }
293 
294     /++
295      + Returns the div of 2 vectors.
296      +/
297     public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "/")
298     {
299         v[] /= other.v[];
300     }
301 
302     /++
303      + Get the N-th component.
304      +/
305     public T opIndex(int n) @nogc pure const nothrow
306     {
307         return v[n];
308     }
309 
310     /++
311      + Get the whole internal array.
312      +/
313     public T[n] opIndex() @nogc pure const nothrow
314     {
315         return v[];
316     }
317 
318     /++
319      + Set the nth component
320      +/
321     public T opIndexAssign(T value, int n) @nogc pure nothrow
322     {
323         return v[n] = value;
324     }
325 
326     /++
327      + Cast to a vector of a different type.
328      +/
329     public U opCast(U)() pure nothrow const if (is(U : Vec!R, R...) && (U._n == n))
330     {
331         U res;
332         foreach (i, el; v)
333         {
334             res.v[i] = cast(U._t) v[i];
335         }
336         return res;
337     }
338 
339     /++ 
340      + Swizzling.
341      +/
342     public Vec!(T, swizzle.length) opDispatch(const string swizzle)() @nogc const pure nothrow
343     {
344         T[swizzle.length] arr;
345 
346         static foreach (i, c; swizzle)
347         {
348             static assert(coordToIdx!(c) <= n-1,
349                 "Trying to swizzle the " ~ c ~ " component, but this vector is too small.");
350 
351             arr[i] = v[coordToIdx!(c)];
352         }
353 
354         Vec!(T, swizzle.length) res;
355         res.v = arr;
356         return res;
357     }
358 
359     private template coordToIdx(char c)
360     {
361         static if (c == 'x') enum coordToIdx = 0;
362         else static if (c == 'y') enum coordToIdx = 1;
363         else static if (c == 'z') enum coordToIdx = 2;
364         else static if (c == 'w') enum coordToIdx = 3;
365         else static assert(false, "Unknown vector component " ~ c);
366     }
367 }
368 
369 /++
370  + Returns the dot product of 2 vectors.
371  +/
372 public real dot(T, ulong n)(Vec!(T, n) a, Vec!(T, n) b) @nogc pure nothrow
373 {
374     real res = 0f;
375     static foreach (i; 0..n)
376     {
377         res += a.v[i] * b.v[i];
378     }
379     return res;
380 }
381 
382 /++
383  + Returns the cross product of 2 Vec3. The result is always a float Vec3.
384  +/
385 public Vec!(float, 3) cross(T)(Vec!(T, 3) a, Vec!(T, 3) b) @nogc pure nothrow
386 {
387     return Vec!(float, 3)(a.y * b.z - a.z * b.y,
388                           a.z * b.x - a.x * b.z,
389                           a.x * b.y - a.y * b.x);
390 }
391 
392 @("Creating vectors")
393 unittest
394 {
395     const t1 = Vec2(2f, 3f);
396 
397     t1.x.should.equal(2f);
398     t1.y.should.equal(3f);
399 
400     const t2 = Vec2(2f);
401 
402     t2.x.should.equal(2f);
403     t2.y.should.equal(2f);
404 
405     const t3 = Vec4d(1f, 2f, 3f, 4f);
406 
407     t3.x.should.equal(1f);
408     t3.y.should.equal(2f);
409     t3.z.should.equal(3f);
410     t3.w.should.equal(4f);
411 
412     const t4 = Vec2i(5, 6);
413 
414     t4.x.should.equal(5);
415     t4.y.should.equal(6);
416 
417     const float[] arr = [1f, 2f];
418     const t5 = Vec2(arr);
419 
420     t5.x.should.equal(1);
421     t5.y.should.equal(2);
422 }
423 
424 @("Vector magnitude")
425 unittest
426 {
427     const t1 = Vec3(5, 6, 8);
428 
429     t1.magnitude().should.be.approximately(11.180, 0.01);
430 
431     const t2 = Vec2d(65, 76);
432 
433     t2.magnitude().should.be.approximately(100, 0.01);
434 }
435 
436 @("Vector normalization")
437 unittest
438 {
439     const t1 = Vec3(75, 64, 23);
440     const t2 = t1.normalized();
441 
442     t2.x.should.be.approximately(0.74, 0.01);
443     t2.y.should.be.approximately(0.63, 0.01);
444     t2.z.should.be.approximately(0.22, 0.01);
445 }
446 
447 @("Vector scalar operations")
448 unittest
449 {
450     auto t1 = Vec3(63, 75, 38);
451 
452     // negation
453     (-t1).should.equal(Vec3(-63, -75, -38));
454 
455     // multiplication with a scalar
456     (t1 * 2).should.equal(Vec3(126, 150, 76));
457     t1 *= 3;
458     t1.should.equal(Vec3(189, 225, 114));
459 
460     // sum with a scalar
461     (t1 + 2).should.equal(Vec3(191, 227, 116));
462     t1 += 3;
463     t1.should.equal(Vec3(192, 228, 117));
464 
465     // sub with a scalar
466     (t1 - 2).should.equal(Vec3(190, 226, 115));
467     t1 -= 3;
468     t1.should.equal(Vec3(189, 225, 114));
469 
470     // division with a scalar
471     (t1 / 2).should.equal(Vec3(94.5, 112.5, 57));
472     t1 /= 3;
473     t1.should.equal(Vec3(63, 75, 38));
474 }
475 
476 @("Vector operations with other vectors")
477 unittest
478 {
479     auto t1 = Vec3(63, 75, 38);
480     auto t2 = Vec3(37, 98, 100);
481 
482     // sum of 2 vectors
483     (t1 + t2).should.equal(Vec3(100, 173, 138));
484     t1 += t2;
485     t1.should.equal(Vec3(100, 173, 138));
486 
487     // sub of 2 vectors
488     (t1 - t2).should.equal(Vec3(63, 75, 38));
489     t1 -= t2;
490     t1.should.equal(Vec3(63, 75, 38));
491 
492     // multiplication of 2 vectors
493     (t1 * t2).should.equal(Vec3(2331, 7350, 3800));
494     t1 *= t2;
495     t1.should.equal(Vec3(2331, 7350, 3800));
496 
497     // division of 2 vectors
498     (t1 / t2).should.equal(Vec3(63, 75, 38));
499     t1 /= t2;
500     t1.should.equal(Vec3(63, 75, 38));
501 }
502 
503 @("Vector components and casting")
504 unittest
505 {
506     auto t1 = Vec3(63, 75, 38);
507 
508     t1[0].should.equal(63);
509 
510     t1[1] = 100;
511     t1[1].should.equal(100);
512     t1.y.should.equal(100);
513 
514     auto t2 = cast(Vec3d) t1;
515     t2.should.equal(Vec3d(63, 100, 38));
516 
517     auto t3 = cast(Vec3i) t2;
518     t3.should.equal(Vec3i(63, 100, 38));
519 }
520 
521 @("Vector dot and cross product")
522 unittest
523 {
524     const t1 = Vec3(63, 75, 38);
525     const t2 = Vec3(37, 98, 100);
526 
527     t1.dot(t2).should.equal(13_481);
528 
529     t1.cross(t2).should.equal(Vec3(3776, -4894, 3399));
530 }
531 
532 @("Vector swizzling")
533 unittest
534 {
535     const t1 = Vec3(4, 5, 6);
536     const t2 = t1.xz;
537 
538     t2.should.equal(Vec2(4, 6));
539 
540     const t3 = Vec4(1f, 2f, 3f, 4f);
541     const t4 = t3.xxyyww;
542 
543     t4.should.equal(Vec!(float, 6)(1f, 1f, 2f, 2f, 4f, 4f));
544 }