1 module godot.vector;
2 
3 import godot.vector2: Vector2, Vector2i;
4 import godot.vector3: Vector3, Vector3i;
5 import godot.vector4: Vector4, Vector4i;
6 
7 import std.traits;
8 
9 /// Vector structure with data accesible with `[N]` or swizzling
10 struct Vector(T, size_t N) if (isNumeric!T && N > 0)  {
11     /// Vector data
12     public T[N] data = [ 0 ];
13 
14     /// Alias to allow easy `data` access
15     alias data this;
16     /// Alias to data type (e.g. float, int)
17     alias dataType = T;
18     /** 
19     Alias to vector type. Can be used to contruct vectors
20     of same type
21     ---
22     auto rvec7 = Vector!(real, 7)(10);
23     auto rvec7s = rvec7.VecType(20);
24     ---
25     */
26     alias VecType = Vector!(T, N);
27     /// Alias to vector size
28     enum size_t size = N;
29 
30     /**
31     Constructs Vector from components. If no components present
32     vector will be filled with 0
33     Example:
34     ---
35     // Vector can be constructed manually or with aliases
36     auto v1 = Vector!(int, 2)(10, 20);
37     auto v2 = ivec2(10, 20);
38     auto v3 = Vector2i(10, 20);
39     auto v4 = Vector2!int(10, 20);
40     // Also vector can be given only one value,
41     // in that case it'll be filled with that value
42     auto v5 = ivec4(13);
43     auto v6 = vec4(0.3f);
44     // Vector values can be accessed with array slicing,
45     // by using color symbols or swizzling
46     float v6x = v6.x;
47     float v6z = v6.z;
48     float[] v6yzx = v6.yzx;
49     float v6y = v6[1];
50     // Valid vector accessors are:
51     // Vector2 - [x, y], [w, h], [u, v]
52     // Vector3 - [x, y, z], [w, h, d], [u, v, t], [r, g, b]
53     // Vector4 - [x, y, z, w], [r, g, b, a]
54     // Other sizes must be accessed with index
55     ---
56     */
57     this(in T val) {
58         foreach (i; 0 .. size) { data[i] = val; }
59     }
60     /// Ditto
61     this(in T[N] vals...) {
62         data = vals;
63     }
64 
65     /* -------------------------------------------------------------------------- */
66     /*                         UNARY OPERATIONS OVERRIDES                         */
67     /* -------------------------------------------------------------------------- */
68     
69     /// opBinary x [+, -, *, /, %] y
70     auto opBinary(string op, R)(in Vector!(R, N) b) const if ( isNumeric!R ) {
71         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
72         VecType ret = VecType();
73         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b.data[i];" ); }
74         return ret;
75     }
76 
77     /// Ditto
78     auto opBinaryRight(string op, R)(in Vector!(R, N) b) const if ( isNumeric!R ) {
79         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
80         VecType ret = VecType();
81         foreach (i; 0 .. size) { mixin( "ret[i] = b.data[i] " ~ op ~ " data[i];" ); }
82         return ret;
83     }
84 
85     /// Ditto
86     auto opBinary(string op, R)(in R b) const if ( isNumeric!R ) {
87         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
88         VecType ret = VecType();
89         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
90         return ret;
91     }
92 
93     /// Ditto
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     /// Ditto
136     auto opOpAssign(string op, R)( in R b ) if ( isNumeric!R ) { 
137         // assert(this !is null, "\nOP::ERROR nullptr Vector!" ~ size.to!string ~ ".");
138         foreach (i; 0 .. size) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
139         return this;
140     }
141 
142     /// Returns hash 
143     size_t toHash() const @safe nothrow {
144         return typeid(data).getHash(&data);
145     }
146 
147     // incredible magic from sily.meta
148     // idk how it works but it works awesome
149     // and im not going to touch it at all
150     static if (N == 2 || N == 3 || N == 4) {
151         static if (N == 2) enum AccessString = "x y|w h|u v"; 
152         else
153         static if (N == 3) enum AccessString = "x y z|w h d|u v t|r g b"; 
154         else
155         static if (N == 4) enum AccessString = "x y z w|r g b a"; 
156 
157         mixin accessByString!(T, N, "data", AccessString); 
158     }
159 
160     /// Returns copy of vector
161     public VecType copyof() {
162         return VecType(data);
163     }
164 
165     /// Returns string representation of vector: `[1.00, 1.00,... , 1.00]`
166     public string toString() const {
167         import std.conv : to;
168         import std.string: format;
169         string s;
170         s ~= "[";
171         foreach (i; 0 .. size) {
172             s ~= isFloatingPoint!T ? format("%.2f", data[i]) : format("%d", data[i]);
173             if (i != size - 1) s ~= ", ";
174         }
175         s ~= "]";
176         return s;
177     }
178 
179     /// Returns pointer to data
180     T* ptr() return {
181         return data.ptr;
182     }
183 
184     /* ------------------------------ Godot Vectors ----------------------------- */
185 
186     static if(N == 2) {
187         static if (isFloatingPoint!T) {
188             Vector2 godotVector() {
189                 return Vector2(data);
190             }
191         } else {
192             Vector2i godotVector() {
193                 return Vector2i(data);
194             }
195         }
196     }
197 
198     static if(N == 3) {
199         static if (isFloatingPoint!T) {
200             Vector3 godotVector() {
201                 return Vector3(data);
202             }
203         } else {
204             Vector3i godotVector() {
205                 return Vector3i(data);
206             }
207         }
208     }
209 
210     static if(N == 4) {
211         static if (isFloatingPoint!T) {
212             Vector4 godotVector() {
213                 return Vector4(data);
214             }
215         } else {
216             Vector4i godotVector() {
217                 return Vector4i(data);
218             }
219         }
220     }
221 
222     /* -------------------------------------------------------------------------- */
223     /*                         STATIC GETTERS AND SETTERS                         */
224     /* -------------------------------------------------------------------------- */
225     
226     /// Constructs predefined vector
227     static alias zero  = () => VecType(0);
228     /// Ditto
229     static alias one   = () => VecType(1);
230 
231     static if(isFloatingPoint!T) {
232         /// Ditto
233         static alias inf   = () => VecType(float.infinity);
234     }
235 
236     static if(N == 2) {
237         /// Ditto
238         static alias left  = () => VecType(-1, 0);
239         /// Ditto
240         static alias right = () => VecType(1, 0);
241         /// Ditto
242         static alias up    = () => VecType(0, -1);
243         /// Ditto
244         static alias down  = () => VecType(0, 1);
245     }
246 
247     static if(N == 3) {
248         static alias forward = () => VecType(0, 0, -1);
249         /// Ditto
250         static alias back    = () => VecType(0, 0, 1);
251         /// Ditto
252         static alias left    = () => VecType(-1, 0, 0);
253         /// Ditto
254         static alias right   = () => VecType(1, 0, 0);
255         /// Ditto
256         static alias up      = () => VecType(0, 1, 0);
257         /// Ditto
258         static alias down    = () => VecType(0, -1, 0);
259     }
260 }