1 /** 
2  * This module contains the `Vec` templated struct representing vectors and
3  * their operations.
4  */
5 module dvec.vector;
6 
7 import std.traits : isNumeric, isFloatingPoint;
8 
9 /** 
10  * Generic struct that represents a vector holding `size` elements of type `T`.
11  * A vector must contain at least 1 element, and has no upper-bound on the size
12  * beyond restrictions imposed by the system.
13  */
14 struct Vec(T, size_t size) if (isNumeric!T && size > 0) {
15     /** 
16      * A static contstant that can be used to get the size of the vector.
17      */
18     public static const size_t SIZE = size;
19 
20     /** 
21      * The internal static array storing the elements of this vector.
22      */
23     public T[size] data;
24 
25     /** 
26      * Constructs a vector from an array of elements.
27      * Params:
28      *   elements = The elements to put in the vector.
29      */
30     public this(T[size] elements) @nogc {
31         static foreach (i; 0 .. size) data[i] = elements[i];
32     }
33     unittest {
34         import dvec.vector_types;
35         Vec3f v = Vec3f([1f, 2f, 3f]);
36         assert(v.data == [1f, 2f, 3f]);
37     }
38 
39     /** 
40      * Constructs a vector from a variadic list of elements.
41      * Params:
42      *   elements = The elements to put in the vector.
43      */
44     public this(T[] elements...) @nogc {
45         if (elements.length != size) assert(false, "Invalid number of elements provided to Vec constructor.");
46         static foreach (i; 0 .. size) data[i] = elements[i];
47     }
48     unittest {
49         import dvec.vector_types;
50         Vec3i v = Vec3i(5, 4, 3);
51         assert(v.data == [5, 4, 3]);
52     }
53 
54     /** 
55      * Constructs a vector where all elements have the given value.
56      * Params:
57      *   value = The value to assign to all elements in the vector.
58      */
59     public this(T value) @nogc {
60         static foreach (i; 0 .. size) data[i] = value;
61     }
62     unittest {
63         import dvec.vector_types;
64         Vec2f v = Vec2f(1f);
65         assert(v.data == [1f, 1f]);
66         Vec!(float, 25) v2 = Vec!(float, 25)(3f);
67         foreach (value; v2.data) {
68             assert(value == 3f);
69         }
70     }
71 
72     /** 
73      * Constructs a vector as a copy of the other.
74      * Params:
75      *   other = The vector to copy.
76      */
77     public this(Vec!(T, size) other) @nogc {
78         this(other.data);
79     }
80     unittest {
81         import dvec.vector_types;
82         Vec2f vOriginal = Vec2f(5f, 16f);
83         Vec2f vCopy = Vec2f(vOriginal);
84         assert(vCopy.data == [5f, 16f]);
85     }
86 
87     /** 
88      * Constructs a vector containing all 0's.
89      * Returns: An vector containing all 0's.
90      */
91     public static Vec!(T, size) empty() @nogc {
92         Vec!(T, size) v;
93         static foreach (i; 0 .. size) v[i] = 0;
94         return v;
95     }
96     unittest {
97         import dvec.vector_types;
98         Vec4f v = Vec4f.empty();
99         assert(v.data == [0f, 0f, 0f, 0f]);
100     }
101 
102     /** 
103      * Computes the sum of a given array of vectors.
104      * Params:
105      *   vectors = The list of vectors to compute the sum of.
106      * Returns: The sum of all vectors.
107      */
108     public static Vec!(T, size) sum(Vec!(T, size)[] vectors) @nogc {
109         Vec!(T, size) v = Vec!(T, size)(0);
110         foreach (vector; vectors) v.add(vector);
111         return v;
112     }
113     unittest {
114         import dvec.vector_types;
115         Vec2i v1 = Vec2i(1, 1);
116         Vec2i v2 = Vec2i(2, 2);
117         Vec2i v3 = Vec2i(3, 3);
118         Vec2i v4 = Vec2i(-1, -4);
119         assert(Vec2i.sum([v1, v2]) == v3);
120         assert(Vec2i.sum([v1, v2, v3]) == Vec2i(6, 6));
121         assert(Vec2i.sum([v3, v4]) == Vec2i(2, -1));
122     }
123 
124     /** 
125      * Gets a copy of this vector.
126      * Returns: A copy of this vector.
127      */
128     public Vec!(T, size) copy() const @nogc {
129         return Vec!(T, size)(this);
130     }
131     unittest {
132         import dvec.vector_types;
133         Vec3f v = Vec3f(0.5f, 1.0f, 0.75f);
134         Vec3f vCopy = v.copy();
135         assert(v.data == vCopy.data);
136         v.data[0] = -0.5f;
137         assert(vCopy.data[0] == 0.5f);
138     }
139 
140     /** 
141      * Sets all elements of the vector to those in the specified array.
142      * Params:
143      *   elements = The elements to set.
144      * Returns: A reference to this vector.
145      */
146     public ref Vec!(T, size) set(T[size] elements) @nogc {
147         static foreach (i; 0 .. size) data[i] = elements[i];
148         return this;
149     }
150     unittest {
151         import dvec.vector_types;
152         assert(Vec2i(1, 2).set([3, 4]).data == [3, 4]);
153     }
154 
155     /** 
156      * Sets all the elements of the vector to those in the specified variadic
157      * array. Note that if the given list of elements is shorter than the
158      * vector's size, only the first `elements.length` elements will be set,
159      * and if the list of elements is larger than the vector's size, any extra
160      * elements will be ignored.
161      * Returns: A reference to this vector.
162      */
163     public ref Vec!(T, size) set(T[] elements...) @nogc {
164         import std.algorithm : min;
165         foreach (i; 0 .. min(size, elements.length)) data[i] = elements[i];
166         return this;
167     }
168     unittest {
169         import dvec.vector_types;
170         assert(Vec2i(1, 2).set(3, 4).data == [3, 4]);
171     }
172 
173     /** 
174      * Adds the given vector to this one.
175      * Params:
176      *   other = The vector to add to this one.
177      * Returns: A reference to this vector, for method chaining.
178      */
179     public ref Vec!(T, size) add(V)(Vec!(V, size) other) @nogc if (isNumeric!V) {
180         static foreach (i; 0 .. size) data[i] += other[i];
181         return this;
182     }
183     unittest {
184         import dvec.vector_types;
185         Vec3i v1 = Vec3i(1);
186         Vec3i v2 = Vec3i(2);
187         v1.add(v2);
188         assert(v1.data == [3, 3, 3]);
189         assert(v2.data == [2, 2, 2]);
190     }
191 
192     /** 
193      * Adds the given scalar value to this vector.
194      * Params:
195      *   scalar = The scalar value to add to this vector.
196      * Returns: A reference to this vector.
197      */
198     public ref Vec!(T, size) add(V)(V scalar) @nogc if (isNumeric!V) {
199         static foreach (i; 0 .. size) data[i] += scalar;
200         return this;
201     }
202     unittest {
203         import dvec.vector_types;
204         Vec3i v = Vec3i(-2, 4, 3);
205         v.add(2);
206         assert(v.data == [0, 6, 5]);
207     }
208 
209     /** 
210      * Subtracts the given vector from this one.
211      * Params:
212      *   other = The vector to subtract from this one.
213      * Returns: A reference to this vector, for method chaining.
214      */
215     public ref Vec!(T, size) sub(V)(Vec!(V, size) other) @nogc if (isNumeric!V) {
216         static foreach (i; 0 .. size) data[i] -= other[i];
217         return this;
218     }
219     unittest {
220         import dvec.vector_types;
221         Vec3i v1 = Vec3i(1);
222         Vec3i v2 = Vec3i(2);
223         v1.sub(v2);
224         assert(v1.data == [-1, -1, -1]);
225         assert(v2.data == [2, 2, 2]);
226     }
227 
228     /** 
229      * Subtracts the given scalar value from this vector.
230      * Params:
231      *   scalar = The scalar value to subtract from this vector.
232      * Returns: A reference to this vector.
233      */
234     public ref Vec!(T, size) sub(V)(V scalar) @nogc if (isNumeric!V) {
235         static foreach (i; 0 .. size) data[i] -= scalar;
236         return this;
237     }
238     unittest {
239         import dvec.vector_types;
240         Vec3i v = Vec3i(0, 3, -1);
241         v.sub(-2);
242         assert(v.data == [2, 5, 1]);
243     }
244 
245     /** 
246      * Multiplies this vector by a factor, element-wise.
247      * Params:
248      *   factor = The factor to multiply by.
249      * Returns: A reference to this vector, for method chaining.
250      */
251     public ref Vec!(T, size) mul(V)(V factor) @nogc if (isNumeric!V) {
252         static foreach (i; 0 .. size) data[i] *= factor;
253         return this;
254     }
255     unittest {
256         import dvec.vector_types;
257         assert(Vec2i(2).mul(2).data == [4, 4]);
258     }
259 
260     /** 
261      * Multiplies this vector by another vector, element-wise.
262      * Params:
263      *   other = The other vector to multiply with.
264      * Returns: A reference to this vector, for method chaining.
265      */
266     public ref Vec!(T, size) mul(V)(Vec!(V, size) other) @nogc if (isNumeric!V) {
267         static foreach (i; 0 .. size) data[i] *= other[i];
268         return this;
269     }
270     unittest {
271         import dvec.vector_types;
272         assert(Vec2i(1, 2).mul(Vec2i(3, 2)).data == [3, 4]);
273     }
274 
275     /** 
276      * Divides this vector by a factor, element-wise.
277      * Params:
278      *   factor = The factor to divide by.
279      * Returns: A reference to this vector, for method chaining.
280      */
281     public ref Vec!(T, size) div(V)(V factor) @nogc if (isNumeric!V) {
282         static foreach (i; 0 .. size) data[i] /= factor;
283         return this;
284     }
285     unittest {
286         import dvec.vector_types;
287         assert(Vec2i(8).div(2).data == [4, 4]);
288     }
289 
290     /** 
291      * Divides this vector by another vector, element-wise.
292      * Params:
293      *   other = The other vector to divide with.
294      * Returns: A reference to this vector, for method chaining.
295      */
296     public ref Vec!(T, size) div(V)(Vec!(V, size) other) @nogc if (isNumeric!V) {
297         static foreach (i; 0 .. size) data[i] /= other[i];
298         return this;
299     }
300     unittest {
301         import dvec.vector_types;
302         assert(Vec2i(8).div(Vec2i(2, 4)).data == [4, 2]);
303     }
304 
305     /** 
306      * Determines the squared magnitude of this vector.
307      * Returns: The squared magnitude of this vector.
308      */
309     public double mag2() const @nogc {
310         double sum = 0;
311         static foreach (i; 0 .. size) sum += data[i] * data[i];
312         return sum;
313     }
314     unittest {
315         import dvec.vector_types;
316         assert(Vec2i(2).mag2() == 8);
317         assert(Vec2f(3f, 4f).mag2() == 5f * 5f);
318     }
319 
320     /** 
321      * Determines the magnitude of this vector.
322      * Returns: The magnitude of this vector.
323      */
324     public double mag() const @nogc {
325         import std.math : sqrt;
326         return sqrt(mag2());
327     }
328     unittest {
329         import dvec.vector_types;
330         import std.math : sqrt;
331         assert(Vec2i(1, 0).mag() == 1);
332         assert(Vec2f(3f, 4f).mag() == 5f);
333         assert(Vec2i(1, 1).mag() == sqrt(2.0));
334     }
335 
336     /** 
337      * Determines the [dot product](https://en.wikipedia.org/wiki/Dot_product)
338      * of this vector and another vector.
339      * Params:
340      *   other = The other vector.
341      * Returns: The dot product of the vectors.
342      */
343     public T dot(Vec!(T, size) other) const @nogc {
344         T sum = 0;
345         static foreach (i; 0 .. size) sum += data[i] * other[i];
346         return sum;
347     }
348     unittest {
349         import dvec.vector_types;
350         assert(Vec3i(1, 3, -5).dot(Vec3i(4, -2, -1)) == 3);
351         assert(Vec2f(1.0f, 0.0f).dot(Vec2f(0.0f, 1.0f)) == 0f);
352         assert(Vec2f(1.0f, 0.0f).dot(Vec2f(1.0f, 0.0f)) == 1f);
353         assert(Vec2f(1.0f, 0.0f).dot(Vec2f(-1.0f, 0.0f)) == -1f);
354     }
355 
356     /** 
357      * Implements the basic unary operators on a vector. This supports:
358      * - Negation `-v`: Negates all a vector's components.
359      * - Incrementation `++v`: Increments all a vector's components by 1.
360      * - Decrementation `--v`: Decrements all a vector's components by 1.
361      * Returns: A new vector with the operation applied.
362      */
363     public Vec!(T, size) opUnary(string op)() const @nogc {
364         Vec!(T, size) result = copy();
365         static if (op == "-") {
366             result.mul(-1);
367         } else static if (op == "+") {
368             // Skip
369         } else static if (op == "++") {
370             result.add(1);
371         } else static if (op == "--") {
372             result.sub(1);
373         } else static assert(false, "Operator " ~ op ~ " is not implemented.");
374         return result;
375     }
376     unittest {
377         import dvec.vector_types;
378         assert(-Vec2i(3) == Vec2i(-3));
379         assert(+Vec2i(2) == Vec2i(2));
380         assert(++Vec3f(0.5f) == Vec3f(1.5f));
381         assert(--Vec3f(0.5f) == Vec3f(-0.5f));
382     }
383 
384     /** 
385      * Implements the basic binary operators between two vectors. It supports
386      * element-wise addition, subtraction, multiplication, and division.
387      * Params:
388      *   other = The other vector operand.
389      * Returns: A new vector that is the result of applying this vector and
390      * the other to the given operation.
391      */
392     public Vec!(T, size) opBinary(string op, V)(Vec!(V, size) other) const @nogc if (isNumeric!V) {
393         Vec!(T, size) result = copy();
394         static if (op == "+") {
395             result.add(other);
396         } else static if (op == "-") {
397             result.sub(other);
398         } else static if (op == "*") {
399             result.mul(other);
400         } else static if (op == "/") {
401             result.div(other);
402         } else static assert(false, "Operator " ~ op ~ " is not implemented.");
403         return result;
404     }
405     unittest {
406         import dvec.vector_types;
407         assert(Vec2i(2) + Vec2i(1) == Vec2i(3));
408         assert(Vec2d(0.5, 0.25) + Vec2i(-1, 2) == Vec2d(-0.5, 2.25));
409         assert(Vec2i(1, 2) * Vec2i(3, 4) == Vec2i(3, 8));
410         assert(Vec2d(0.5, 0.25) / Vec2d(0.25, 0.1) == Vec2d(2.0, 2.5));
411     }
412 
413     /** 
414      * Implements the basic binary operators between a vector and a scalar
415      * value. It supports element-wise addition, subtraction multiplication,
416      * and division.
417      * Params:
418      *   scalar = The scalar value to apply to each element of the vector.
419      * Returns: A new vector that is the result of applying the scalar value
420      * to this vector's elements, using the given operation.
421      */
422     public Vec!(T, size) opBinary(string op, V)(V scalar) const @nogc if (isNumeric!V) {
423         Vec!(T, size) result = copy();
424         static if (op == "+") {
425             result.add(scalar);
426         } else static if (op == "-") {
427             result.sub(scalar);
428         } else static if (op == "*") {
429             result.mul(scalar);
430         } else static if (op == "/") {
431             result.div(scalar);
432         } else static assert(false, "Operator " ~ op ~ " not implemented.");
433         return result;
434     }
435     unittest {
436         // TODO
437     }
438 
439     /** 
440      * Right-hand binary operator implementation. See `opBinary` above for more info.
441      * Params:
442      *   scalar = The scalar value to apply to each element of the vector.
443      * Returns: A new vector that is the result of applying the scalar value
444      * to this vector's elements, using the given operation.
445      */
446     public Vec!(T, size) opBinaryRight(string op, V)(V scalar) const @nogc if (isNumeric!V) {
447         return opBinary!(op, V)(scalar);
448     }
449     unittest {
450         import dvec.vector_types;
451         assert(3 + Vec2i(1) == Vec2i(4));
452         // TODO: add more tests.
453     }
454 
455     /** 
456      * Gets the element at a specified index.
457      * Params:
458      *   i = The index of the element.
459      * Returns: The element at the specified index.
460      */
461     public T opIndex(size_t i) const @nogc {
462         return data[i];
463     }
464     unittest {
465         import dvec.vector_types;
466         Vec3f v = Vec3f(1f, 2f, 3f);
467         assert(v[0] == 1f);
468         assert(v[1] == 2f);
469         assert(v[2] == 3f);
470     }
471 
472     /** 
473      * Inserts an element at the specified index.
474      * Params:
475      *   value = The value to assign.
476      *   i = The index of the element.
477      */
478     public void opIndexAssign(T value, size_t i) @nogc {
479         data[i] = value;
480     }
481     unittest {
482         import dvec.vector_types;
483         Vec3i v;
484         assert(v[0] == 0);
485         v[0] = 42;
486         assert(v[0] == 42);
487     }
488 
489     /** 
490      * Implements op-assignments for indexed values of the vector, so you can
491      * do things like `v[2] *= 10;`. Supports addition, subtraction,
492      * multiplication, and division.
493      * Params:
494      *   value = The value to apply.
495      *   i = The index in the vector's array.
496      */
497     public void opIndexOpAssign(string op, V)(V value, size_t i) @nogc if (isNumeric!V) {
498         static if (op == "+") {
499             data[i] += value;
500         } else static if (op == "-") {
501             data[i] -= value;
502         } else static if (op == "*") {
503             data[i] *= value;
504         } else static if (op == "/") {
505             data[i] /= value;
506         } else static assert(false, "Operator " ~ op ~ " not implemented.");
507     }
508     unittest {
509         import dvec.vector_types;
510         Vec3i v = Vec3i(1);
511         v[0] += 2;
512         assert(v[0] == 3);
513         v[1] -= 3;
514         assert(v[1] == -2);
515         v[0] *= 2;
516         assert(v[0] == 6);
517     }
518 
519     /** 
520      * Named accessor for common vector fields, which allows you to get the
521      * value of specific elements in the vector according to conventional
522      * names.
523      * - `x` for the first element.
524      * - `y` for the second element.
525      * - `z` for the third element.
526      * - `w` for the fourth element.
527      * Returns: The value of the specified element.
528      */
529     public T opDispatch(string s)() const @nogc {
530         static if (s == "x" && size >= 1) {
531             return data[0];
532         } else static if (s == "y" && size >= 2) {
533             return data[1];
534         } else static if (s == "z" && size >= 3) {
535             return data[2];
536         } else static if (s == "w" && size >= 4) {
537             return data[3];
538         } else {
539             static assert(false, "Invalid vector named accessor: " ~ s);
540         }
541     }
542     unittest {
543         import dvec.vector_types;
544         Vec4i v = Vec4i(1, 2, 3, 4);
545         assert(v.x == 1);
546         assert(v.y == 2);
547         assert(v.z == 3);
548         assert(v.w == 4);
549     }
550 
551     /** 
552      * Named setter for common vector fields, which allows you to set the value
553      * of specific elements in the vector according to conventional names.
554      * - `x` for the first element.
555      * - `y` for the second element.
556      * - `z` for the third element.
557      * - `w` for the fourth element.
558      * Params:
559      *   value = The value to set. It can be any numeric value, but it'll be
560      *           cast to the vector's type.
561      */
562     public void opDispatch(string s, V)(V value) @nogc if (isNumeric!V) {
563         static if (s == "x" && size >= 1) {
564             data[0] = cast(T) value;
565         } else static if (s == "y" && size >= 2) {
566             data[1] = cast(T) value;
567         } else static if (s == "z" && size >= 3) {
568             data[2] = cast(T) value;
569         } else static if (s == "w" && size >= 4) {
570             data[3] = cast(T) value;
571         } else {
572             static assert(false, "Invalid vector named setter: " ~ s);
573         }
574         // TODO: Find some way to make `v.x += 1` work.
575     }
576     unittest {
577         import dvec.vector_types;
578         Vec4i v;
579         v.x = 1;
580         v.y = 2;
581         v.z = 3;
582         v.w = 4;
583         assert(v.data == [1, 2, 3, 4]);
584     }
585 
586     /** 
587      * Implements explicit casting between vector types. You may cast to a
588      * type with a different `T` element type, or a different size, or both.
589      * Casting to a different element type will call a normal `cast(newType) oldValue`
590      * for each value. When casting to a smaller vector size, extra elements
591      * will be truncated, and when casting to a larger size, missing elements
592      * are initialized to zero.
593      *
594      * Keep in mind that casting floating-point vectors to integer vectors is
595      * possible, and that casting in general can lead to a loss of data.
596      * 
597      * Returns: The resulting vector of the new type.
598      */
599     public NewVecType opCast(NewVecType : Vec!(newType, newSize), newType, size_t newSize)() const @nogc {
600         NewVecType v;
601         static foreach (i; 0 .. newSize) {
602             static if (i < size) {
603                 v.data[i] = cast(newType) this.data[i];
604             } else {
605                 v.data[i] = 0;
606             }
607         }
608         return v;
609     }
610     unittest {
611         import dvec.vector_types;
612         Vec2f v1 = Vec2f(0.5f, 3.1f);
613         Vec3i v2 = cast(Vec3i) v1;
614         assert(v2.data == [0, 3, 0]);
615         auto v3 = cast(Vec!(byte, 1)) v1;
616         assert(v3.data == [0]);
617     }
618 
619     /** 
620      * Compares this vector to another, based on their magnitudes.
621      * Params:
622      *   other = The vector to compare to.
623      * Returns: 0 if the vectors have equal magnitude, 1 if this vector's
624      * magnitude is bigger, and -1 if the other's is bigger.
625      */
626     public int opCmp(Vec!(T, size) other) const @nogc {
627         const double a = this.mag2();
628         const double b = other.mag2();
629         if (a == b) return 0;
630         if (a < b) return -1;
631         return 1;
632     }
633     unittest {
634         import dvec.vector_types;
635         Vec3i v1 = Vec3i(1);
636         Vec3i v2 = Vec3i(2);
637         Vec3i v3 = Vec3i(3);
638         assert(v1 < v2);
639         assert(v2 > v1);
640         assert(v3 > v2);
641     }
642 
643     /** 
644      * Determines if two vectors are equal. Vectors are considered equal when
645      * all of their components are equal.
646      * Params:
647      *   other = The vector to check equality against.
648      * Returns: True if the vectors are equal, or false otherwise.
649      */
650     public bool opEquals(Vec!(T, size) other) const @nogc {
651         return this.data == other.data;
652     }
653     unittest {
654         import dvec.vector_types;
655         Vec3f v1 = Vec3f(0.5f, 1.0f, 1.5f);
656         assert(v1 == v1);
657         Vec3f v2 = Vec3f(1f, 2f, 3f);
658         assert(v2 != v1);
659         assert(v1 == v2 / 2f);
660     }
661 
662     /** 
663      * Gets a simple string representation of this vector.
664      * Returns: The string representation of this vector.
665      */
666     public string toString() const {
667         import std.array : appender;
668         import std.conv : to;
669         auto s = appender!string;
670         s ~= "[";
671         static foreach (i; 0 .. size - 1) {
672             s ~= data[i].to!string;
673             s ~= ", ";
674         }
675         s ~= data[size - 1].to!string;
676         s ~= "]";
677         return s[];
678     }
679 
680     static if (isFloatingPoint!T) {
681         /** 
682          * [Normalizes](https://en.wikipedia.org/wiki/Unit_vector) this vector,
683          * such that it will have a magnitude of 1.
684          * Returns: A reference to this vector, for method chaining.
685          */
686         public ref Vec!(T, size) norm() @nogc {
687             const double mag = mag();
688             static foreach (i; 0 .. size) {
689                 data[i] /= mag;
690             }
691             return this;
692         }
693     }
694 
695     static if (isFloatingPoint!T && size == 2) {
696         /** 
697          * Converts this 2-dimensional vector from [Cartesian](https://en.wikipedia.org/wiki/Cartesian_coordinate_system)
698          * to [Polar](https://en.wikipedia.org/wiki/Polar_coordinate_system)
699          * coordinates. It is assumed that the first element is the **x**
700          * coordinate, and the second element is the **y** coordinate. The
701          * first element becomes the **radius** and the second becomes the
702          * angle **theta**.
703          * - The angle is normalized to be within the range [0, 2*PI).
704          * Returns: A reference to this vector.
705          */
706         public ref Vec!(T, size) toPolar() @nogc {
707             import std.math : atan2, PI;
708             T radius = mag();
709             T angle = atan2(data[1], data[0]);
710             if (angle < 0) {
711                 angle += 2 * PI;
712             } else if (angle >= 2 * PI) {
713                 angle -= 2 * PI;
714             }
715             data[0] = radius;
716             data[1] = angle;
717             return this;
718         }
719         unittest {
720             import dvec.vector_types;
721             import std.math;
722             assert(Vec2f(1f, 0f).toPolar() == Vec2f(1f, 0f));
723             assert(Vec2f(0f, 1f).toPolar() == Vec2f(1f, PI_2));
724             assert(Vec2f(-1f, 0f).toPolar() == Vec2f(1f, PI));
725             assert(Vec2f(0f, -1f).toPolar() == Vec2f(1f, 3f * PI / 2f));
726             assert(Vec2f(1f, 1f).toPolar() == Vec2f(SQRT2, PI_4));
727             assert(Vec2f(-50f, -50f).toPolar() == Vec2f(10f * sqrt(50f), 5 * PI_4));
728         }
729 
730         /** 
731          * Converts this 2-dimensional vector from [Polar](https://en.wikipedia.org/wiki/Polar_coordinate_system)
732          * to [Cartesian](https://en.wikipedia.org/wiki/Cartesian_coordinate_system)
733          * coordinates. It is assumed that the first element is the **radius**
734          * and the second element is the angle **theta**. The first element
735          * becomes the **x** coordinate, and the second becomes the **y**
736          * coordinate.
737          * Returns: A reference to this vector.
738          */
739         public ref Vec!(T, size) toCartesian() @nogc {
740             import std.math : cos, sin;
741             T x = data[0] * cos(data[1]);
742             T y = data[0] * sin(data[1]);
743             data[0] = x;
744             data[1] = y;
745             return this;
746         }
747     }
748 
749     static if (isFloatingPoint!T && size == 3) {
750         /** 
751          * Computes the [cross product](https://en.wikipedia.org/wiki/Cross_product) of this vector and another, and stores
752          * the result in this vector.
753          * Params:
754          *   other = The other vector.
755          * Returns: A reference to this vector, for method chaining.
756          */
757         public ref Vec!(T, size) cross(Vec!(T, size) other) @nogc {
758             Vec!(T, size) tmp;
759             tmp[0] = data[1] * other[2] - data[2] * other[1];
760             tmp[1] = data[2] * other[0] - data[0] * other[2];
761             tmp[2] = data[0] * other[1] - data[1] * other[0];
762             data[0] = tmp[0];
763             data[1] = tmp[1];
764             data[2] = tmp[2];
765             return this;
766         }
767     }
768 }
769 
770 unittest {
771     import std.stdio;
772     import std.math;
773     import dvec.vector_types;
774 
775     void assertFP(double actual, double expected, double delta = 1e-06, string msg = "Assertion failed.") {
776         double lowBound = expected - delta;
777         double highBound = expected + delta;
778         assert(actual > lowBound && actual < highBound, msg);
779     }
780 
781     // Test floating-point specific methods.
782     auto v6 = Vec2f(3, 3);
783     v6.norm();
784     assertFP(v6.mag, 1);
785     assertFP(v6[0], sqrt(2f) / 2f);
786     v6 = Vec2f(1, 0);
787     auto v6Copy = Vec2f(v6);
788     v6.norm();
789     assert(v6.mag == 1);
790     assert(v6.data == v6Copy.data);
791 
792     // Test toPolar
793     auto vCart = Vec2d(1, 0);
794     vCart.toPolar();
795     assert(vCart.data == [1.0, 0.0]);
796     vCart.toCartesian();
797     assert(vCart.data == [1.0, 0.0]);
798     vCart = Vec2d(1, 1);
799     vCart.toPolar();
800     assertFP(vCart[0], sqrt(2f));
801     assertFP(vCart[1], PI_4);
802     vCart.toCartesian();
803     assertFP(vCart[0], 1);
804     assertFP(vCart[1], 1);
805     vCart = Vec2d(-1, 1);
806     vCart.toPolar();
807     assertFP(vCart[0], sqrt(2f));
808     assertFP(vCart[1], 3 * PI_4);
809 }