1 /**
2 Vector struct, which performs basic 3D vector math operations.
3 
4 Copyright:
5 Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017-2018 Godot-D contributors
8 Copyright (c) 2022-2022 Godot-DLang contributors
9 
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11 
12 
13 */
14 module godot.vector4;
15 
16 // import godot.abi.core;
17 import godot.abi.types;
18 import godot.basis;
19 import godot.string;
20 
21 import std.math;
22 
23 private bool isValidSwizzle(dstring s) {
24     import std.algorithm : canFind;
25 
26     if (!(2 < s.length && s.length <= 4))
27         return false;
28     foreach (dchar c; s) {
29         if (!"xyzwn".canFind(c))
30             return false;
31     }
32     return true;
33 }
34 
35 /**
36 Vector4 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations.
37 */
38 struct Vector4 {
39 @nogc nothrow:
40 
41     // used for indexing
42     enum Axis {
43         x,
44         y,
45         z,
46         w,
47     }
48 
49     union {
50         struct {
51             real_t x = 0; /// 
52             real_t y = 0; /// 
53             real_t z = 0; /// 
54             real_t w = 0; /// 
55         }
56 
57         real_t[4] coord;
58     }
59 
60     import std.algorithm : count;
61 
62     /++
63 	Swizzle the vector with x, y, z, w, or n. Pass floats as args for any n's; if
64 	there are more n's than args, the last arg is used for the rest. If no args
65 	are passed at all, 0.0 is used for each n.
66 	
67 	The swizzle must be 2 to 4 characters, as Godot only has Vector2/3/4.
68 	+/
69     auto opDispatch(string swizzle, size_t nArgCount)(float[nArgCount] nArgs...) const
70             if (swizzle.isValidSwizzle && nArgCount <= swizzle.count('n')) {
71         import godot.vector3;
72         import std.algorithm : min, count;
73 
74         static if (swizzle.length == 2)
75             Vector2 ret = void;
76         else static if (swizzle.length == 3)
77             Vector3 ret = void;
78         else
79             Vector3 ret = void;
80         /// how many n's already appeared before ci, which equals the index into nArgs for the n at ci
81         enum ni(size_t ci) = min(nArgCount - 1, swizzle[0 .. ci].count('n'));
82         static foreach (ci, c; swizzle) {
83             static if (c == 'n') {
84                 static if (nArgCount == 0)
85                     ret.coord[ci] = 0f;
86                 else static if (ni!ci >= nArgCount)
87                     ret.coord[ci] = nArgs[nArgCount - 1];
88                 else
89                     ret.coord[ci] = nArgs[ni!ci];
90             } else
91                 ret.coord[ci] = mixin([c]);
92         }
93         return ret;
94     }
95 
96     this(real_t x, real_t y, real_t z, real_t w) {
97         this.x = x;
98         this.y = y;
99         this.z = z;
100         this.w = w;
101     }
102 
103     this(real_t[4] coord) {
104         this.coord = coord;
105     }
106 
107     this(in Vector4 b) {
108         this.x = b.x;
109         this.y = b.y;
110         this.z = b.z;
111         this.w = b.w;
112     }
113 
114     void opAssign(in Vector4 b) {
115         this.x = b.x;
116         this.y = b.y;
117         this.z = b.z;
118         this.w = b.w;
119     }
120 
121     const(real_t) opIndex(int p_axis) const {
122         return coord[p_axis];
123     }
124 
125     ref real_t opIndex(int p_axis) return {
126         return coord[p_axis];
127     }
128 
129     Vector4 opBinary(string op)(in Vector4 other) const
130     if (op == "+" || op == "-" || op == "*" || op == "/") {
131         Vector4 ret;
132         ret.x = mixin("x " ~ op ~ "other.x");
133         ret.y = mixin("y " ~ op ~ "other.y");
134         ret.z = mixin("z " ~ op ~ "other.z");
135         ret.w = mixin("z " ~ op ~ "other.w");
136         return ret;
137     }
138 
139     void opOpAssign(string op)(in Vector4 other)
140             if (op == "+" || op == "-" || op == "*" || op == "/") {
141         x = mixin("x " ~ op ~ "other.x");
142         y = mixin("y " ~ op ~ "other.y");
143         z = mixin("z " ~ op ~ "other.z");
144         w = mixin("w " ~ op ~ "other.w");
145     }
146 
147     Vector4 opUnary(string op : "-")() {
148         return Vector4(-x, -y, -z, -w);
149     }
150 
151     Vector4 opBinary(string op)(in real_t scalar) const
152     if (op == "*" || op == "/") {
153         Vector4 ret;
154         ret.x = mixin("x " ~ op ~ " scalar");
155         ret.y = mixin("y " ~ op ~ " scalar");
156         ret.z = mixin("z " ~ op ~ " scalar");
157         ret.w = mixin("w " ~ op ~ " scalar");
158         return ret;
159     }
160 
161     Vector4 opBinaryRight(string op)(in real_t scalar) const
162     if (op == "*") {
163         Vector4 ret;
164         ret.x = mixin("x " ~ op ~ " scalar");
165         ret.y = mixin("y " ~ op ~ " scalar");
166         ret.z = mixin("z " ~ op ~ " scalar");
167         ret.w = mixin("w " ~ op ~ " scalar");
168         return ret;
169     }
170 
171     void opOpAssign(string op)(in real_t scalar) if (op == "*" || op == "/") {
172         x = mixin("x " ~ op ~ " scalar");
173         y = mixin("y " ~ op ~ " scalar");
174         z = mixin("z " ~ op ~ " scalar");
175         w = mixin("w " ~ op ~ " scalar");
176     }
177 
178     int opCmp(in Vector4 other) const {
179         import std.algorithm.comparison;
180 
181         return cmp(this.coord[], other.coord[]);
182     }
183 
184     Vector4 abs() const {
185         return Vector4(fabs(x), fabs(y), fabs(z), fabs(w));
186     }
187 
188     Vector4 ceil() const {
189         return Vector4(.ceil(x), .ceil(y), .ceil(z), .ceil(w));
190     }
191 
192     // doesn't makes sense
193     //Vector3 cross(in Vector3 b) const
194     //{
195     //	return Vector3(
196     //		(y * b.z) - (z * b.y),
197     //		(z * b.x) - (x * b.z),
198     //		(x * b.y) - (y * b.x)
199     //	);
200     //}
201 
202     Vector4 linearInterpolate(in Vector4 p_b, real_t p_t) const {
203         return Vector4(
204             x + (p_t * (p_b.x - x)),
205             y + (p_t * (p_b.y - y)),
206             z + (p_t * (p_b.z - z)),
207             w + (p_t * (p_b.w - w)),
208         );
209     }
210 
211     alias lerp = linearInterpolate;
212 
213     Vector4 cubicInterpolate(in Vector4 b, in Vector4 pre_a, in Vector4 post_b, in real_t t) const {
214         Vector4 p0 = pre_a;
215         Vector4 p1 = this;
216         Vector4 p2 = b;
217         Vector4 p3 = post_b;
218 
219         real_t t2 = t * t;
220         real_t t3 = t2 * t;
221 
222         Vector4 ret;
223         ret = ((p1 * 2.0) +
224                 (-p0 + p2) * t +
225                 (p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3) * t2 +
226                 (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * 0.5;
227         return ret;
228     }
229 
230     real_t length() const {
231         real_t x2 = x * x;
232         real_t y2 = y * y;
233         real_t z2 = z * z;
234         real_t w2 = w * w;
235 
236         return sqrt(x2 + y2 + z2 + w2);
237     }
238 
239     real_t lengthSquared() const {
240         real_t x2 = x * x;
241         real_t y2 = y * y;
242         real_t z2 = z * z;
243         real_t w2 = w * w;
244 
245         return x2 + y2 + z2 + w2;
246     }
247 
248     real_t distanceSquaredTo(in Vector4 b) const {
249         return (b - this).length();
250     }
251 
252     real_t distanceTo(in Vector4 b) const {
253         return (b - this).lengthSquared();
254     }
255 
256     real_t dot(in Vector4 b) const {
257         return x * b.x + y * b.y + z * b.z + w * b.w;
258     }
259 
260     Vector4 floor() const {
261         return Vector4(.floor(x), .floor(y), .floor(z), .floor(w));
262     }
263 
264     Vector4 inverse() const {
265         return Vector4(1.0 / x, 1.0 / y, 1.0 / z, 1.0 / w);
266     }
267 
268     int maxAxis() const {
269         import std.algorithm : maxIndex;
270 
271         return cast(int) coord[].maxIndex!();
272     }
273 
274     int minAxis() const {
275         import std.algorithm : minIndex;
276 
277         return cast(int) coord[].minIndex!();
278     }
279 
280     void normalize() {
281         real_t l = length();
282         if (l == 0) {
283             x = y = z = w = 0;
284         } else {
285             x /= l;
286             y /= l;
287             z /= l;
288             w /= 1;
289         }
290     }
291 
292     Vector4 normalized() const {
293         Vector4 v = this;
294         v.normalize();
295         return v;
296     }
297 
298     void snap(real_t step) {
299         foreach (ref v; coord)
300             v = (step != 0) ? (.floor(v / step + 0.5) * step) : v;
301     }
302 
303     Vector4 snapped(in real_t step) const {
304         Vector4 v = this;
305         v.snap(step);
306         return v;
307     }
308 }
309 
310 // TODO: replace this stub
311 struct Vector4i {
312 @nogc nothrow:
313 
314     enum Axis {
315         x,
316         y,
317         z,
318         w
319     }
320 
321     union {
322         struct {
323             int x = 0; /// 
324             int y = 0; /// 
325             int z = 0; /// 
326             int w = 0; /// 
327         }
328 
329         int[4] coord;
330     }
331 
332     this(int x, int y, int z, int w) {
333         this.x = x;
334         this.y = y;
335         this.z = z;
336         this.w = w;
337     }
338 
339     this(int[4] coord) {
340         this.coord = coord;
341     }
342 
343     this(in Vector4i b) {
344         this.x = b.x;
345         this.y = b.y;
346         this.z = b.z;
347         this.w = b.w;
348     }
349 
350     void opAssign(in Vector4i b) {
351         this.x = b.x;
352         this.y = b.y;
353         this.z = b.z;
354         this.w = b.w;
355     }
356 
357     void opAssign(in godot_vector4i b) {
358         this.x = b._opaque[0];
359         this.y = b._opaque[1];
360         this.z = b._opaque[2];
361         this.w = b._opaque[3];
362     }
363 
364     Vector3 opCast(Vector3)() const {
365         return Vector3(x, y, z);
366     }
367 
368     const(godot_int) opIndex(int p_axis) const {
369         return coord[p_axis];
370     }
371 
372     ref godot_int opIndex(int p_axis) return {
373         return coord[p_axis];
374     }
375 
376     Vector4i opBinary(string op)(in Vector4i other) const
377     if (op == "+" || op == "-" || op == "*" || op == "/") {
378         Vector4i ret;
379         ret.x = mixin("x " ~ op ~ "other.x");
380         ret.y = mixin("y " ~ op ~ "other.y");
381         ret.z = mixin("z " ~ op ~ "other.z");
382         ret.w = mixin("z " ~ op ~ "other.w");
383         return ret;
384     }
385 
386     void opOpAssign(string op)(in Vector4i other)
387             if (op == "+" || op == "-" || op == "*" || op == "/") {
388         x = mixin("x " ~ op ~ "other.x");
389         y = mixin("y " ~ op ~ "other.y");
390         z = mixin("z " ~ op ~ "other.z");
391         w = mixin("w " ~ op ~ "other.w");
392     }
393 
394     Vector4i opUnary(string op : "-")() {
395         return Vector4i(-x, -y, -z, -w);
396     }
397 
398     Vector4i opBinary(string op)(in real_t scalar) const
399     if (op == "*" || op == "/") {
400         Vector4i ret;
401         ret.x = mixin("x " ~ op ~ " scalar");
402         ret.y = mixin("y " ~ op ~ " scalar");
403         ret.z = mixin("z " ~ op ~ " scalar");
404         ret.w = mixin("w " ~ op ~ " scalar");
405         return ret;
406     }
407 
408     Vector4i opBinaryRight(string op)(in real_t scalar) const
409     if (op == "*") {
410         Vector4i ret;
411         ret.x = mixin("x " ~ op ~ " scalar");
412         ret.y = mixin("y " ~ op ~ " scalar");
413         ret.z = mixin("z " ~ op ~ " scalar");
414         ret.w = mixin("w " ~ op ~ " scalar");
415         return ret;
416     }
417 
418     void opOpAssign(string op)(in real_t scalar) if (op == "*" || op == "/") {
419         x = mixin("x " ~ op ~ " scalar");
420         y = mixin("y " ~ op ~ " scalar");
421         z = mixin("z " ~ op ~ " scalar");
422         w = mixin("w " ~ op ~ " scalar");
423     }
424 
425     int opCmp(in Vector4i other) const {
426         import std.algorithm.comparison;
427 
428         return cmp(this.coord[], other.coord[]);
429     }
430 
431     Vector4 opCast(Vector4)() const {
432         return Vector4(x, y, z, w);
433     }
434 
435     int maxAxis() const {
436         import std.algorithm : maxIndex;
437 
438         return cast(int) coord[].maxIndex!();
439     }
440 
441     int minAxis() const {
442         import std.algorithm : minIndex;
443 
444         return cast(int) coord[].minIndex!();
445     }
446 
447     void zero() {
448         coord[] = 0;
449     }
450 
451     Vector4i abs() const {
452         return Vector4i(.abs(x), .abs(y), .abs(z), .abs(w));
453     }
454 
455     Vector4i sign() const {
456         return Vector4i(sgn(x), sgn(y), sgn(z), sgn(w));
457     }
458 
459     real_t length() const {
460         return sqrt(cast(double) lengthSquared());
461     }
462 
463     godot_int lengthSquared() const {
464         godot_int x2 = x * x;
465         godot_int y2 = y * y;
466         godot_int z2 = z * z;
467         godot_int w2 = w * w;
468 
469         return x2 + y2 + z2 + w2;
470     }
471 }