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