1 module sily.vector;
2 
3 import std.math;
4 import std.numeric;
5 import std.conv;
6 import std.traits;
7 import std.typetuple;
8 import std.algorithm;
9 import std.stdio;
10 import std.string;
11 import std.format;
12 
13 import sily.meta;
14 import sily.math;
15 import sily.array;
16 
17 alias Vector2(T) = Vector!(T, 2);
18 alias Vector3(T) = Vector!(T, 3);
19 alias Vector4(T) = Vector!(T, 4);
20 
21 alias Vector2f = Vector2!float;
22 // alias Vector2d = Vector2!double;
23 // alias Vector2r = Vector2!real;
24 alias Vector2i = Vector2!int;
25 // alias Vector2l = Vector2!long;
26 
27 alias Vector3f = Vector3!float;
28 // alias Vector3d = Vector3!double;
29 // alias Vector3r = Vector3!real;
30 alias Vector3i = Vector3!int;
31 // alias Vector3l = Vector3!long;
32 
33 alias Vector4f = Vector4!float;
34 // alias Vector4d = Vector4!double;
35 // alias Vector4r = Vector4!real;
36 alias Vector4i = Vector4!int;
37 // alias Vector4l = Vector4!long;
38 
39 alias vec2 = Vector2f;
40 alias vec3 = Vector3f;
41 alias vec4 = Vector4f;
42 
43 alias vec2i = Vector2i;
44 alias vec3i = Vector3i;
45 alias vec4i = Vector4i;
46 
47 struct Vector(T, size_t N) if (isNumeric!T && N > 0)  {
48     public T[N] data = fill!T(1, size);
49 
50     alias data this;
51     alias dataType = T;
52     alias VecType = Vector!(T, N);
53     enum size_t size = N;
54 
55     // this() {
56     //     foreach (i; 0 .. size) { data[i] = T.init; }
57     // }
58 
59     this(in T val) {
60         foreach (i; 0 .. size) { data[i] = val; }
61     }
62 
63     this(in T[N] vals...) {
64         data = vals;
65     }
66 
67     /* -------------------------------------------------------------------------- */
68     /*                         UNARY OPERATIONS OVERRIDES                         */
69     /* -------------------------------------------------------------------------- */
70     
71     // opBinary x [+, -, *, /, %] y
72     auto opBinary(string op, R)(in Vector!(R, N) b) const if ( isNumeric!R ) {
73         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
74         VecType ret = VecType();
75         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b.data[i];" ); }
76         return ret;
77     }
78 
79     // opBinaryRight y [+, -, *, /, %] x
80     auto opBinaryRight(string op, R)(in Vector!(R, N) b) const if ( isNumeric!R ) {
81         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
82         VecType ret = VecType();
83         foreach (i; 0 .. size) { mixin( "ret[i] = b.data[i] " ~ op ~ " data[i];" ); }
84         return ret;
85     }
86 
87     auto opBinary(string op, R)(in R b) const if ( isNumeric!R ) {
88         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
89         VecType ret = VecType();
90         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
91         return ret;
92     }
93 
94     auto opBinaryRight(string op, R)(in R b) const if ( isNumeric!R ) {
95         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
96         VecType ret = VecType();
97         foreach (i; 0 .. size) { mixin( "ret[i] = b " ~ op ~ " data[i];" ); }
98         return ret;
99     }
100 
101     // opEquals x == y
102     bool opEquals(R)(in Vector!(R, size) b) const if ( isNumeric!R ) {
103         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
104         bool eq = true;
105         foreach (i; 0 .. size) { eq = eq && data[i] == b.data[i]; }
106         return eq;
107     }
108 
109     // opCmp x [< > ==] y
110     int opCmp(R)(in Vector!(R, N) b) const if ( isNumeric!R ) {
111         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
112         T al = length;
113         T bl = b.length;
114         if (al == bl) return 0;
115         if (al < bl) return -1;
116         return 1;
117     }
118 
119     // opUnary [-, +, --, ++] x
120     auto opUnary(string op)() if(op == "-"){
121         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
122         VecType ret = VecType();
123         if (op == "-")
124             foreach (i; 0 .. size) { data[i] = -data[i]; }
125         return ret;
126     }
127     
128     // opOpAssign x [+, -, *, /, %]= y
129     auto opOpAssign(string op, R)( in Vector!(R, N) b ) if ( isNumeric!R ) { 
130         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
131         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b.data[i];" ); }
132         return this;
133     }
134     
135     auto opOpAssign(string op, R)( in R b ) if ( isNumeric!R ) { 
136         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
137         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
138         return this;
139     }
140 
141     size_t toHash() const @nogc @safe pure nothrow {
142         T s = 0;
143         foreach (i; 0 .. size) { s += data[i]; }
144         return cast(size_t) s;
145     }
146 
147     public string toString() const {
148         import std.conv : to;
149         string s;
150         s ~= "[";
151         foreach (i; 0 .. size) {
152             s ~= isFloatingPoint!T ? format("%.2f", data[i]) : format("%d", data[i]);
153             if (i != size - 1) s ~= ", ";
154         }
155         s ~= "]";
156         return s;
157     }
158 
159     T* ptr() {
160         return data.ptr;
161     }
162 
163     static if (N == 2 || N == 3 || N == 4) {
164         static if (N == 2) enum AccessString = "x y|w h|u v";
165         else
166         static if (N == 3) enum AccessString = "x y z|w h d|u v t|r g b";
167         else
168         static if (N == 4) enum AccessString = "x y z w|r g b a";
169 
170         mixin accessByString!(T, N, "data", AccessString);
171     }
172 
173     /* -------------------------------------------------------------------------- */
174     /*                         STATIC GETTERS AND SETTERS                         */
175     /* -------------------------------------------------------------------------- */
176     
177     static alias zero  = () => VecType(0);
178     static alias one   = () => VecType(1);
179 
180     static if(isFloatingPoint!T) {
181         static alias inf   = () => VecType(float.infinity);
182     }
183 
184     static if(N == 2) {
185         static alias left  = () => VecType(-1, 0);
186         static alias right = () => VecType(1, 0);
187         static alias up    = () => VecType(0, -1);
188         static alias down  = () => VecType(0, 1);
189     }
190 
191     static if(N == 3) {
192         static alias forward = () => VecType(0, 0, -1);
193         static alias back    = () => VecType(0, 0, 1);
194         static alias left    = () => VecType(-1, 0, 0);
195         static alias right   = () => VecType(1, 0, 0);
196         static alias up      = () => VecType(0, 1, 0);
197         static alias down    = () => VecType(0, -1, 0);
198     }
199 
200     public VecType copyof() {
201         return VecType(data);
202     }
203 
204     /* -------------------------------------------------------------------------- */
205     /*                                    MATH                                    */
206     /* -------------------------------------------------------------------------- */
207 
208     /** 
209      * Returns squared vector length
210      */
211     public T lengthSquared() {
212         T l = 0;
213         foreach (i; 0 .. size) { l += data[i] * data[i]; }
214         return l;
215     }
216 
217     /** 
218      * Returns squared distance from vector to `b`
219      * Params:
220      *   b = Vector to calculate distance to
221      * Returns: Distance
222      */
223     public T distanceSquaredTo(VecType b) {
224         T dist = 0;
225         foreach (i; 0 .. size) { dist += (data[i] - b.data[i]) * (data[i] - b.data[i]); }
226         return dist;
227     }
228 
229     /* -------------------------------------------------------------------------- */
230     /*                             FLOATING POINT MATH                            */
231     /* -------------------------------------------------------------------------- */
232     // Int math is still might be accessible
233     // if I'll need it. All I'd need to do
234     // Is to add another `static if` 
235 
236     // FLOAT VECTORS
237     static if(isFloatingPoint!T) {
238         /** 
239          * Is vector approximately close to `v`
240          * Params:
241          *   v = Vector to compare
242          * Returns: 
243          */
244         public bool isClose(VecType v) {
245             bool eq = true;
246             foreach (i; 0 .. size) { eq = eq && data[i].isClose(v[i], float.epsilon); }
247             return eq;
248         }
249 
250         /** 
251          * Returns vector length
252          */
253         public T length() {
254             return sqrt(lengthSquared);
255         }
256 
257         /** 
258          * Normalises vector
259          */
260         public void normalize() {
261             T l = lengthSquared;
262             if (l != 0) {
263                 l = sqrt(lengthSquared);
264                 foreach (i; 0 .. size) { data[i] /= l; }
265             }
266         }
267         alias normalise = normalize;
268         
269         /** 
270          * Returns true if vector is normalised
271          */
272         public bool isNormalized() {
273             return lengthSquared.isClose(1, float.epsilon);
274         }
275         alias isNormalised = isNormalized;
276 
277         /** 
278          * Calculates distance to vector `b`
279          * Params:
280          *   b = Vector
281          * Returns: Distance
282          */
283         public T distanceTo(VecType b) {
284             return sqrt(distanceSquaredTo(b));
285         }
286 
287         /** 
288          * Performs dot product
289          * Params:
290          *   b = Vector
291          * Returns: dot product
292          */
293         public float dot(VecType b) {
294             T d = 0;
295             foreach (i; 0 .. size) { d += data[i] * b.data[i]; }
296             return d;
297         }
298 
299         /** 
300          * Signs current vector
301          */
302         public void sign() {
303             foreach (i; 0 .. size) { data[i] = data[i].sgn(); }
304         }
305 
306         /** 
307          * Floors vector values
308          */
309         public void floor() {
310             foreach (i; 0 .. size) { data[i] = data[i].floor(); }
311         }
312 
313         /** 
314          * Ceils vector values
315          */
316         public void ceil() {
317             foreach (i; 0 .. size) { data[i] = data[i].ceil(); }
318         }
319 
320         /** 
321          * Rounds vector values
322          */
323         public void round() {
324             foreach (i; 0 .. size) { data[i] = data[i].round(); }
325         }
326 
327         /** 
328          * Abs vector values
329          */
330         public void abs() {
331             foreach (i; 0 .. size) { data[i] = data[i].abs(); }
332         }
333 
334         /** 
335          * Clamps vector values to min
336          * Params:
337          *   b = Minimal Vector
338          */
339         public void min(VecType b) {
340             foreach (i; 0 .. size) { data[i] = data[i].min(b.data[i]); }
341         }
342 
343         /** 
344          * Clamps vector values to max
345          * Params:
346          *   b = Maximal Vector
347          */
348         public void max(VecType b) {
349             foreach (i; 0 .. size) { data[i] = data[i].max(b.data[i]); }
350         }
351 
352         /** 
353          * Clamps vector values
354          * Params:
355          *   b = Minimal Vector
356          *   b = Maximal Vector
357          */
358         public void clamp(VecType p_min, VecType p_max) {
359             foreach (i; 0 .. size) { data[i] = data[i].clamp(p_min.data[i], p_max.data[i]); }
360         }
361 
362         /** 
363          * Snaps vector values
364          * Params:
365          *   p_step = Vector to snap to
366          */
367         public void snap(VecType p_step) {
368             foreach (i; 0 .. size) { 
369                 data[i] = data[i].snap(p_step[i]);
370             }
371         }
372 
373         /** 
374          * Limits vector length
375          * Params:
376          *   p_len = Max length
377          */
378         public void limitLength(T p_len) {
379             T l = length();
380             if (l > 0 && p_len < l) {
381                 for (int i = 0; i < size; ++i) {
382                     data[i] /= l;
383                     data[i] *= p_len;
384                 }
385             }
386         }
387 
388         /** 
389          * Linear interpolates vector
390          * Params:
391          *   to = Vector to interpolate to
392          *   weight = Interpolation weight in range [0.0, 1.0]
393          */
394         public void lerp(VecType to, T weight) {
395             foreach (i; 0 .. size) { data[i] = (weight * (to.data[i] - data[i])); }
396         }
397 
398         // FIXME
399         // TODO
400         // it's in math
401         // public Vector2!T cubicInterpolate(Vector2!T b, Vector2!T prea, Vector2!T postb, float weight) {
402         //     Vector2 res = *this;
403         //     res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
404         //     res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
405         //     return res;
406         // }
407     }
408 
409     /* -------------------------------------------------------------------------- */
410     /*                                  VECTOR2F                                  */
411     /* -------------------------------------------------------------------------- */
412     static if(isFloatingPoint!T && N == 2) {
413         // public static Vector2!T fromAngle(float p_angle) {
414         //     return Vector2!T(cos(p_angle), sin(p_angle));
415         // }
416 
417         // public float cross(VecType b) {
418         //     return this.x * b.y - this.y * b.x;
419         // }
420         
421         // public float angle() {
422         //     return atan2(this.y, this.x);
423         // }
424 
425         // public float angleTo(Vector2!T b) {
426         //     return atan2(cross(b), dot(b));
427         // }
428 
429         // public float angleToPoint(Vector2!T b) {
430         //     return (b - data).angle();
431         // }
432 
433         // public float aspect() {
434         //     return this.x / this.y;
435         // }
436 
437         // public Vector2!T project(Vector2!T b) {
438         //     return b * (dot(b) / b.lengthSquared());
439         // }
440 
441         // public Vector2!T moveToward(Vector2!T p_to, const T p_delta) {
442         //     Vector2!T v = copyof();
443         //     Vector2!T vd = p_to - v;
444         //     T len = vd.length;
445         //     return len <= p_delta || len < float.epsilon ? p_to : v + vd / len * p_delta;
446         // }
447 
448         // public Vector2!T slide(Vector2!T p_normal) {
449         //     if (!p_normal.isNormalized) {
450         //         writeln("Normal vector must be normalized");
451         //         // throw new Error("MATH::ERROR::VECTOR2");
452         //         return copyof();
453         //     }
454         //     return copyof() - p_normal * dot(p_normal);
455         // }
456 
457         // public Vector2!T bounce(Vector2!T p_normal) {
458         //     return -reflect(p_normal);
459         // }
460 
461         // public Vector2!T reflect(Vector2!T p_normal) {
462         //     if (!p_normal.isNormalized) {
463         //         writeln("Normal vector must be normalized");
464         //         // throw new Error("MATH::ERROR::VECTOR2");
465         //         return copyof();
466         //     }
467         //     return  to!T(2) * p_normal * dot(p_normal) - copyof();
468         // }
469 
470         // public Vector2!T orthogonal() {
471         //     return Vector2!T(this.y, -this.x);
472         // }
473 
474         // public Vector2!T rotated(float phi) {
475         //     T sine = sin(phi);
476         //     T cosi = cos(phi);
477         //     return Vector2!T(
478         //         this.x * cosi - this.y * sine,
479         //         this.x * sine + this.y * cosi);
480         // }
481 
482         // public VecType slerp(VecType to, T weight) {
483         //     T stLensq = lengthSquared;
484         //     T enLensq = to.lengthSquared;
485         //     if (stLensq == 0.0f || enLensq == 0.0f) {
486         //         // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
487         //         return lerp(to, weight);
488         //     }
489         //     T stLen = sqrt(stLensq);
490         //     T rsLen = stLen.lerp(sqrt(enLensq), weight);
491         //     T angle = angleTo(to);
492         //     return rotated(angle * weight) * (rsLen / stLen);
493         // }
494 
495         // LINK https://glmatrix.net/docs/vec2.js
496         // LINK https://github.com/godotengine/godot/blob/master/core/math/vector2.cpp
497     }
498 
499     /* -------------------------------------------------------------------------- */
500     /*                                  VECTOR3F                                  */
501     /* -------------------------------------------------------------------------- */
502     static if(isFloatingPoint!T && N == 3) {
503         // TODO
504         // VecType cross(VecType b) {
505         //     VecType cr = VecType();            
506         //     T ax = data[0],
507         //       ay = data[1],
508         //       az = data[2];
509         //     T bx = b.data[0],
510         //       by = b.data[1],
511         //       bz = b.data[2];
512         //     cr.data[0] = ay * bz - az * by;
513         //     cr.data[1] = az * bx - ax * bz;
514         //     cr.data[2] = ax * by - ay * bx;
515         //     return cr;
516         // }
517 
518         // TODO
519 
520         // LINK https://glmatrix.net/docs/vec3.js.html
521         // LINK https://github.com/godotengine/godot/blob/master/core/math/vector3.cpp
522     }
523 
524     /* -------------------------------------------------------------------------- */
525     /*                                  VECTOR4F                                  */
526     /* -------------------------------------------------------------------------- */
527     // here probably gonna be almost nothing
528     // here be dragons?
529     static if(isFloatingPoint!T && N == 4) {
530         // TODO ?
531     }
532     
533     // Vector2 Vector2::posmod(const real_t p_mod) const {
534     //     return Vector2(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod));
535     // }
536 
537     // Vector2 Vector2::posmodv(const Vector2 &p_modv) const {
538     //     return Vector2(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y));
539     // }
540 }