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