1 /**
2  * Copyright:       Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:         $(Gabriel Gheorghe)
4  * License:         $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:          $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/math/vector.d)
6  * Documentation:
7  * Coverage:
8 **/
9 module liberty.math.vector;
10 
11 import std.traits : isFloatingPoint;
12 
13 import liberty.math.traits;
14 import liberty.math.quaternion ;
15 import liberty.math.functions;
16 
17 /// T = type of elements.
18 /// N = number of elements (2, 3, 4).
19 struct Vector(T, ubyte N) if (N >= 2 && N <= 4) {
20     ///
21     alias size = N;
22     ///
23     alias type = T;
24 	/// Fields definition.
25     union {
26         ///
27         T[N] v;
28 	    ///
29         struct {
30             ///
31             T x;
32             ///
33             alias r = x;
34             ///
35             alias p = y;
36             ///
37             T y;
38             ///
39             alias g = y;
40             ///
41             alias q = y;
42             static if (N >= 3) {
43                 ///
44                 T z;
45                 ///
46                 alias b = z;
47                 ///
48                 alias s = z;
49             }
50             static if (N == 4) {
51                 ///
52                 T w;
53                 ///
54                 alias a = w;
55                 ///
56                 alias t = w;
57             }
58         }
59     }
60     static if (N == 2 && is(T == float) || is(T == int)) {
61         /// Vector2 with values of 0.
62         static const Vector!(T, 2) zero = Vector!(T, 2)(0, 0);
63         /// Vector2 with values of 1.
64         static const Vector!(T, 2) one = Vector!(T, 2)(1, 1);
65         /// Vector2 pointing up.
66         static const Vector!(T, 2) up = Vector!(T, 2)(0, -1);
67         /// Vector2 pointing right.
68         static const Vector!(T, 2) right = Vector!(T, 2)(1, 0);
69         /// Vector2 pointing down.
70         static const Vector!(T, 2) down = Vector!(T, 2)(0, 1);
71         /// Vector2 pointing left.
72         static const Vector!(T, 2) left = Vector!(T, 2)(-1, 0);
73         ///
74         unittest {
75             assert(Vector!(T, 2).zero == Vector!(T, 2)(0, 0));
76             assert(Vector!(T, 2).one == Vector!(T, 2)(1, 1));
77             assert(Vector!(T, 2).up == Vector!(T, 2)(0, -1));
78             assert(Vector!(T, 2).right == Vector!(T, 2)(1, 0));
79             assert(Vector!(T, 2).down == Vector!(T, 2)(0, 1));
80             assert(Vector!(T, 2).left == Vector!(T, 2)(-1, 0));
81         }
82     } else static if (N == 3 && is(T == float)) {
83         /// Vector3 with values of 0.
84         static const Vector!(T, 3) zero = Vector!(T, 3)(0, 0, 0);
85         /// Vector3 with values of 1.
86         static const Vector!(T, 3) one = Vector!(T, 3)(1, 1, 1);
87         /// Vector3 pointing up.
88         static const Vector!(T, 3) up = Vector!(T, 3)(0, 1, 0);
89         /// Vector3 pointing right.
90         static const Vector!(T, 3) right = Vector!(T, 3)(1, 0, 0);
91         /// Vector3 pointing down.
92         static const Vector!(T, 3) down = Vector!(T, 3)(0, -1, 0);
93         /// Vector3 pointing left.
94         static const Vector!(T, 3) left = Vector!(T, 3)(-1, 0, 0);
95         /// Vector3 pointing forward.
96         static const Vector!(T, 3) forward = Vector!(T, 3)(0, 0, 1);
97         /// Vector3 pointing backward.
98         static const Vector!(T, 3) backward = Vector!(T, 3)(0, 0, -1);
99         ///
100         unittest {
101             assert(Vector!(T, 3).zero == Vector!(T, 3)(0, 0, 0));
102             assert(Vector!(T, 3).one == Vector!(T, 3)(1, 1, 1));
103             assert(Vector!(T, 3).up == Vector!(T, 3)(0, 1, 0));
104             assert(Vector!(T, 3).down == Vector!(T, 3)(0, -1, 0));
105             assert(Vector!(T, 3).right == Vector!(T, 3)(1, 0, 0));
106             assert(Vector!(T, 3).left == Vector!(T, 3)(-1, 0, 0));
107             assert(Vector!(T, 3).forward == Vector!(T, 3)(0, 0, 1));
108             assert(Vector!(T, 3).backward == Vector!(T, 3)(0, 0, -1));
109         }
110     }
111     ///
112     this(U...)(U values)   {
113         import std.meta : allSatisfy;
114         import std.traits : isAssignable;
115         enum bool isAsgn(U) = isAssignable!(T, U);
116         static if ((U.length == N) && allSatisfy!(isAsgn, U)) {
117             static foreach (i, x; values) {
118                 v[i] = x;
119             }
120         } /*else static if ((U.length == 2) && is(U[0] : Vector2!T) && is(U[1] : T) && is(this == Vector3!T)) {
121             v[0] = U[0].x;
122 			v[1] = U[0].y;
123 			v[2] = U[1];
124         }*/
125         else static if ((U.length == 1) && isAsgn!U && (!is(U[0] : Vector))) {
126             v[] = values[0];
127         } else static if (U.length == 1 && is(U[0] : T[])) {
128             v = values[0];
129         } else static if (U.length == 0) {
130             v[] = cast(T)0;
131         } else {
132             static assert(false, "Cannot create a vector from given arguments!");
133         }
134     }
135     ///
136       unittest {
137         const v1 = Vector2I(4, 2);
138         assert(v1.v == [4, 2]);
139         const v2 = Vector2I(5);
140         assert(v2.v == [5, 5]);
141         const v3 = Vector3I([1, 2, 3]);
142         assert(v3.v == [1, 2, 3]);
143         const v4 = Vector4I();
144         assert(v4.v == [0, 0, 0, 0]);
145         const v5 = Vector3I(Vector2I(1, 3), 5);
146         assert(v5.v == [1, 3, 5]);
147         const v6 = Vector4I(Vector3I(-2, 1, 3), 5);
148         assert(v6.v == [-2, 1, 3, 5]);
149         const v7 = Vector4I(Vector2I(1, 3), Vector2I(5, 4));
150         assert(v7.v == [1, 3, 5, 4]);
151     }
152     // TODO: remove this constructors
153 	static if (N == 3) {
154 		this(Vector2!T xy, T z) { v[0] = xy.x; v[1] = xy.y; v[2] = z; }
155 	} else static if (N == 4) {
156 		this(Vector2!T xy, Vector2!T zw) { v[0] = xy.x; v[1] = xy.y; v[2] = zw.x; v[3] = zw.y; }
157 		this(Vector3!T xyz, T w) { v[0] = xyz.x; v[1] = xyz.y; v[2] = xyz.z; v[3] = w; }
158 	}
159     /// Assign a Vector from a compatible type.
160     ref Vector opAssign(U)(U rhs)   {
161         static if (is(U : Vector)) {
162             static foreach (i; 0..N) {
163                 v[i] = rhs.v[i];
164             }
165         } else static if (is(U : T)) {
166             v[] = rhs;
167         } else static if (is(U : T[])) {
168             assert(rhs.length == N, "Static array's lenght must be equal to vector's length!");
169             v = rhs;
170         } else {
171             static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Vector.stringof);
172         }
173         return this;
174     }
175     ///
176       unittest {
177         auto v1 = Vector3I();
178         v1 = Vector3I(7, 8, 9);
179         assert(v1.v == [7, 8, 9]);
180         auto v2 = Vector2F();
181         v2 = 3.4f;
182         assert(v2.v == [3.4f, 3.4f]);
183         auto v3 = Vector4I();
184         v3 = [1, 3, 5, -6];
185         assert(v3.v == [1, 3, 5, -6]);
186     }
187     ///
188     bool opEquals(U)(U rhs)   const {
189         static if (is(U : Vector)) {
190             static foreach (i; 0..N) {
191                 if (v[i] != rhs.v[i]) {
192                     return false;
193                 }
194             }
195         } else static if (is(U : T)) {
196             static foreach (i; 0..N) {
197                 if (v[i] != rhs) {
198                     return false;
199                 }
200             }
201         } else {
202             static assert(0, "Cannot compare a variable of type " ~ U.stringof ~ " with a variable of type " ~ Vector.stringof);
203         }
204         return true;
205     }
206     ///
207       unittest {
208         const v1 = Vector2F(4.5f, 6.0f);
209         assert(v1 == Vector2F(4.5f, 6.0f));
210         assert(v1 != Vector2F(4.5f, 8.0f));
211         const v2 = Vector3I(-1, -1, -1);
212         assert(v2 == -1);
213         assert(v2 != 0);
214     }
215 	///
216 	Vector opUnary(string op)()  const  if (op == "+" || op == "-" || op == "~" || op == "!") {
217 		Vector ret = void;
218 		static foreach (i; 0..N) {
219 			mixin("ret.v[i] = " ~ op ~ "v[i];");
220 		}
221 		return ret;
222 	}
223     ///
224       unittest {
225         const v1 = Vector2I(2, -5);
226         assert((+v1).v == [2, -5]);
227         assert((-v1).v == [-2, 5]);
228         assert((~v1).v == [-3, 4]);
229         // *** BUG ***
230         //auto v2 = Vector!(bool, 2)(true, false);
231         //assert((!v2).v == [false, true]);
232     }
233 	///
234 	Vector opBinary(string op, U)(U rhs)   const {
235 		Vector ret = void;
236         static if (is(U : Vector)) {
237             static foreach (i; 0..N) {
238 				mixin("ret.v[i] = cast(T)(v[i] " ~ op ~ " rhs.v[i]);");
239 			}
240         } else static if (is(U : T)) {
241 			static foreach (i; 0..N) {
242 				mixin("ret.v[i] = cast(T)(v[i] " ~ op ~ " rhs);");
243 			}
244 		} else {
245             static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Vector.stringof);
246 		}
247 		return ret;
248 	}
249     ///
250       unittest {
251         const v1 = Vector2I(4, -5);
252         const v2 = Vector2I(7, 2);
253         auto v3 = v1 + v2;
254         assert(v3.v == [11, -3]);
255         v3 = v2 - 2;
256         assert(v3.v == [5, 0]);
257     }
258 	///
259     Vector opBinaryRight(string op, U)(U lhs)   const {
260         Vector ret = void;
261         static if (is(U : Vector)) {
262             static foreach (i; 0..N) {
263 		        mixin("ret.v[i] = cast(T)(lhs.v[i] " ~ op ~ " v[i]);");
264 	        }
265         } else static if (is(U : T)) {
266 	        static foreach (i; 0..N) {
267 		        mixin("ret.v[i] = cast(T)(lhs " ~ op ~ " v[i]);");
268 	        }
269         } else  {
270             static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Vector.stringof);
271         }
272 	    return ret;
273     }
274     ///
275       unittest {
276         const v1 = Vector2I(4, -5);
277         const v2 = 3 + v1;
278         assert(v2.v == [7, -2]);
279     }
280     ///
281 	ref Vector opOpAssign(string op, U)(U rhs)   {
282         static if (is(U : Vector) || is(U : T)) {
283             mixin("this = this " ~ op ~ " rhs;");
284         } else {
285             static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof);
286         }
287 		return this;
288 	}
289 	///
290       unittest {
291         auto v1 = Vector2I(2, -8);
292         v1 += 3;
293         assert(v1.v == [5, -5]);
294         const v2 = Vector2I(1, 2);
295         v1 -= v2;
296         assert(v1.v == [4, -7]);
297     }
298 	///
299 	ref T opIndex(size_t i)   {
300 		return v[i];
301 	}
302 	///
303 	ref const(T) opIndex(size_t i)   const {
304 		return v[i];
305 	}
306 	///
307 	T opIndexAssign(U : T)(U x, size_t i)   {
308 		return v[i] = x;
309 	}
310 	///
311 	U opCast(U)()   const if (isVector!U && U.size == size) {
312 		U ret = void;
313 		static foreach (i; 0..N) {
314 			mixin("ret.v[i] = cast(U.type)v[i];");
315 		}
316 		return ret;
317 	}
318     ///
319       unittest {
320         const v1 = Vector2F(1.3f, -5.7f);
321         auto v2 = cast(Vector2I)v1;
322         assert(v2.v == [1, -5]);
323     }
324 	///
325 	int opDollar()   const {
326 		return N;
327 	}
328 	///
329 	T[] opSlice()   {
330 		return v[];
331 	}
332 	///
333 	T[] opSlice(int a, int b)   {
334 		return v[a..b];
335 	}
336 	/// Returns a pointer to content.
337     inout(T)* ptr()   inout {
338         return v.ptr;
339     }
340     /// Converts current vector to a string.
341     string toString()  const {
342         try {
343             import std..string : format;
344             return format("%s", v);
345         } catch (Exception e) {
346             assert(0);
347         }
348     }
349     ///
350       unittest {
351         auto v = Vector!(uint, 2)(2, 4);
352         assert(v.toString() == "[2, 4]");
353     }
354 	///
355 	T squaredMagnitude()   const {
356 		T sumSquares = 0;
357 		static foreach (i; 0..N) {
358 			mixin("sumSquares += v[i] * v[i];");
359 		}
360 		return sumSquares;
361 	}
362 	///
363 	T squaredDistanceTo(Vector v)   const {
364 		return (v - this).squaredMagnitude();
365 	}
366 	static if (isFloatingPoint!T) {
367 		/// Returns Euclidean length.
368 		T magnitude()   const {
369             import liberty.math.functions : sqrt;
370 			return sqrt(squaredMagnitude());
371 		}
372 		/// Returns inverse of Euclidean length.
373 		T inverseMagnitude()   const {
374             import liberty.math.functions : sqrt;
375 			return 1 / sqrt(squaredMagnitude());
376 		}
377 		/// Faster but less accurate inverse of Euclidean length. Returns inverse of Euclidean length.
378 		T fastInverseMagnitude()   const {
379             import liberty.math.functions : inverseSqrt;
380 			return inverseSqrt(squaredMagnitude());
381 		}
382 		/// Returns Euclidean distance between this and other.
383 		T distanceTo(Vector other)   const {
384 			return (other - this).magnitude();
385 		}
386 		/// In-place normalization.
387 		void normalize()   {
388 			const invMag = inverseMagnitude();
389 			v[] *= invMag;
390 		}
391 		/// Returns normalized vector.
392 		Vector normalized()   const {
393 			Vector res = this;
394 			res.normalize();
395 			return res;
396 		}
397 		/// Faster but less accurate in-place normalization.
398 		void fastNormalize()   {
399 			const invLength = fastInverseMagnitude();
400 			v[] *= invLength;
401 		}
402 		/// Faster but less accurate vector normalization. Returns normalized vector.
403 		Vector fastNormalized()   const {
404 			Vector ret = this;
405 			ret.fastNormalize();
406 			return ret;
407 		}
408 		static if (N == 3) {
409 			/// Gets an orthogonal vector from a 3D vector.
410 			Vector getOrthogonalVector()   const {
411         import liberty.math.functions : abs;
412 				return abs(x) > abs(z) ? Vector(-y, x, 0.0) : Vector(0.0, -z, y);
413 			}
414 		}
415 	}
416 
417   static if (is(T == float) && N == 3) {
418     /**
419      *
420     **/
421     ref Vector!(T, 3) rotate(float angle, Vector!(T, 3) axis) {
422       const sinHalfAngle = sin(radians(angle / 2));
423       const cosHalfAngle = cos(radians(angle / 2));
424 
425       const rX = axis.x * sinHalfAngle;
426       const rY = axis.y * sinHalfAngle;
427       const rZ = axis.z * sinHalfAngle;
428       const rW = cosHalfAngle;
429 
430       Quaternion!T rotation = Quaternion!T(rX, rY, rZ, rW);
431       const Quaternion!T conjugate = rotation.inversed();
432       const Quaternion!T w = rotation.mul(this) * conjugate;
433 
434       x = w.x;
435       y = w.y;
436       z = w.z;
437 
438       return this;
439     }
440   }
441 }
442 ///
443 template Vector2(T) {
444 	alias Vector2 = Vector!(T, 2);
445 }
446 ///
447 template Vector3(T) {
448 	alias Vector3 = Vector!(T, 3);
449 }
450 ///
451 template Vector4(T) {
452 	alias Vector4 = Vector!(T, 4);
453 }
454 ///
455 alias Vector2I = Vector2!int ;
456 ///
457 alias Vector2U = Vector2!uint;
458 ///
459 alias Vector2F = Vector2!float;
460 ///
461 alias Vector2D = Vector2!double;
462 ///
463 alias Vector3I = Vector3!int;
464 ///
465 alias Vector3U = Vector3!uint;
466 ///
467 alias Vector3F = Vector3!float;
468 ///
469 alias Vector3D = Vector3!double;
470 ///
471 alias Vector4I = Vector4!int;
472 ///
473 alias Vector4U = Vector4!uint;
474 ///
475 alias Vector4F = Vector4!float;
476 ///
477 alias Vector4D = Vector4!double;
478 ///
479 alias Color3 = Vector3!ubyte;
480 ///
481 alias Color4 = Vector4!ubyte;
482 /// Element-wise minimum.
483 Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b)   {
484     import std.algorithm : min;
485     Vector!(T, N) ret = void;
486     static foreach (i; 0..N) {
487 	    ret.v[i] = min(a.v[i], b.v[i]);
488     }
489     return ret;
490 }
491 /// Element-wise maximum.
492 Vector!(T, N) maxByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b)   {
493     import std.algorithm : max;
494     Vector!(T, N) ret = void;
495     static foreach (i; 0..N) {
496 	    ret.v[i] = max(a.v[i], b.v[i]);
497     }
498     return ret;
499 }
500 /// Element-wise absolute value.
501 Vector!(T, N) absByElem(T, int N)(const Vector!(T, N) a)   {
502     import liberty.math.functions : abs;
503     Vector!(T, N) ret = void;
504     static foreach (i; 0..N) {
505 	    ret.v[i] = abs(a.v[i]);
506     }
507     return ret;
508 }
509 /// Returns dot product.
510 T dot(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b)   {
511     T sum = 0;
512     static foreach (i; 0..N) {
513 	    sum += a.v[i] * b.v[i];
514     }
515     return sum;
516 }
517 /// Returns 3D cross product.
518 Vector!(T, 3) cross(T)(const Vector!(T, 3) a, const Vector!(T, 3) b)   {
519     return Vector!(T, 3)(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
520 }
521 /// Returns 3D reflect.
522 Vector!(T, N) reflect(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b)   {
523     return a - (2 * dot(b, a)) * b;
524 }
525 ///
526 static assert(Vector2F.sizeof == 8);
527 ///
528 static assert(Vector3D.sizeof == 24);
529 ///
530 static assert(Vector4I.sizeof == 16);