1 /**
2 Vector used for 2D Math.
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.vector2;
15 
16 import godot.abi.core;
17 import godot.abi.types;
18 
19 
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 (!"xyn".canFind(c))
30             return false;
31     }
32     return true;
33 }
34 
35 /**
36 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values.
37 */
38 struct Vector2 {
39 @nogc nothrow:
40 
41     union {
42         struct {
43             union {
44                 real_t x = 0.0; /// 
45                 real_t width; /// 
46             }
47 
48             union {
49                 real_t y = 0.0; /// 
50                 real_t height; /// 
51             }
52         }
53 
54         real_t[2] coord;
55     }
56 
57     import std.algorithm : count;
58 
59     /++
60 	Swizzle the vector with x, y, 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) {
92         this.x = x;
93         this.y = y;
94     }
95 
96     this(real_t[2] coord) {
97         this.coord = coord;
98     }
99 
100     this(in Vector2 b) {
101         this.x = b.x;
102         this.y = b.y;
103     }
104 
105     this(in Vector2i b) {
106         this.x = b.x;
107         this.y = b.y;
108     }
109 
110     void opAssign(in Vector2 b) {
111         this.x = b.x;
112         this.y = b.y;
113     }
114 
115     // there is cases where this happens in api.json
116     void opAssign(in Vector2i b) {
117         this.x = b.x;
118         this.y = b.y;
119     }
120 
121     ref real_t opIndex(int axis) return {
122         return axis ? y : x;
123     }
124 
125     const(real_t) opIndex(int axis) const {
126         return axis ? y : x;
127     }
128 
129     Vector2 opBinary(string op)(in Vector2 other) const
130     if (op == "+" || op == "-" || op == "*" || op == "/") {
131         Vector2 ret;
132         ret.x = mixin("x " ~ op ~ "other.x");
133         ret.y = mixin("y " ~ op ~ "other.y");
134         return ret;
135     }
136 
137     void opOpAssign(string op)(in Vector2 other)
138             if (op == "+" || op == "-" || op == "*" || op == "/") {
139         x = mixin("x " ~ op ~ "other.x");
140         y = mixin("y " ~ op ~ "other.y");
141     }
142 
143     Vector2 opUnary(string op : "-")() {
144         return Vector2(-x, -y);
145     }
146 
147     Vector2 opBinary(string op)(in real_t scalar) const
148     if (op == "*" || op == "/") {
149         Vector2 ret;
150         ret.x = mixin("x " ~ op ~ " scalar");
151         ret.y = mixin("y " ~ op ~ " scalar");
152         return ret;
153     }
154 
155     Vector2 opBinaryRight(string op)(in real_t scalar) const
156     if (op == "*") {
157         Vector2 ret;
158         ret.x = mixin("x " ~ op ~ " scalar");
159         ret.y = mixin("y " ~ op ~ " scalar");
160         return ret;
161     }
162 
163     void opOpAssign(string op)(in real_t scalar) if (op == "*" || op == "/") {
164         x = mixin("x " ~ op ~ " scalar");
165         y = mixin("y " ~ op ~ " scalar");
166     }
167 
168     int opCmp(in Vector2 other) const {
169         import std.algorithm.comparison;
170         import std.range;
171 
172         return cmp(only(x, y), only(other.x, other.y));
173     }
174 
175     real_t aspect() const {
176         return width / height;
177     }
178 
179     void normalize() {
180         real_t l = x * x + y * y;
181         if (l != 0) {
182             l = sqrt(l);
183             x /= l;
184             y /= l;
185         }
186     }
187 
188     Vector2 normalized() const {
189         Vector2 v = this;
190         v.normalize();
191         return v;
192     }
193 
194     real_t length() const {
195         return sqrt(x * x + y * y);
196     }
197 
198     real_t lengthSquared() const {
199         return x * x + y * y;
200     }
201 
202     real_t distanceTo(in Vector2 p_vector2) const {
203         return sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y));
204     }
205 
206     real_t distanceSquaredTo(in Vector2 p_vector2) const {
207         return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y);
208     }
209 
210     real_t angleTo(in Vector2 p_vector2) const {
211         return atan2(cross(p_vector2), dot(p_vector2));
212     }
213 
214     real_t angleToPoint(in Vector2 p_vector2) const {
215         return atan2(y - p_vector2.y, x - p_vector2.x);
216     }
217 
218     real_t dot(in Vector2 p_other) const {
219         return x * p_other.x + y * p_other.y;
220     }
221 
222     real_t cross(in Vector2 p_other) const {
223         return x * p_other.y - y * p_other.x;
224     }
225 
226     Vector2 cross(real_t p_other) const {
227         return Vector2(p_other * y, -p_other * x);
228     }
229 
230     Vector2 project(in Vector2 p_vec) const {
231         Vector2 v1 = p_vec;
232         Vector2 v2 = this;
233         return v2 * (v1.dot(v2) / v2.dot(v2));
234     }
235 
236     Vector2 planeProject(real_t p_d, in Vector2 p_vec) const {
237         return p_vec - this * (dot(p_vec) - p_d);
238     }
239 
240     Vector2 clamped(real_t p_len) const {
241         real_t l = length();
242         Vector2 v = this;
243         if (l > 0 && p_len < l) {
244             v /= l;
245             v *= p_len;
246         }
247         return v;
248     }
249 
250     static Vector2 linearInterpolate(in Vector2 p_a, in Vector2 p_b, real_t p_t) {
251         Vector2 res = p_a;
252         res.x += (p_t * (p_b.x - p_a.x));
253         res.y += (p_t * (p_b.y - p_a.y));
254         return res;
255     }
256 
257     Vector2 linearInterpolate(in Vector2 p_b, real_t p_t) const {
258         Vector2 res = this;
259         res.x += (p_t * (p_b.x - x));
260         res.y += (p_t * (p_b.y - y));
261         return res;
262 
263     }
264 
265     alias lerp = linearInterpolate;
266 
267     Vector2 cubicInterpolate(in Vector2 p_b, in Vector2 p_pre_a, in Vector2 p_post_b, real_t p_t) const {
268         Vector2 p0 = p_pre_a;
269         Vector2 p1 = this;
270         Vector2 p2 = p_b;
271         Vector2 p3 = p_post_b;
272 
273         real_t t = p_t;
274         real_t t2 = t * t;
275         real_t t3 = t2 * t;
276 
277         Vector2 ret;
278         ret = ((p1 * 2.0) +
279                 (-p0 + p2) * t +
280                 (p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3) * t2 +
281                 (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * 0.5;
282 
283         return ret;
284     }
285 
286     Vector2 slide(in Vector2 p_vec) const {
287         return p_vec - this * this.dot(p_vec);
288     }
289 
290     Vector2 reflect(in Vector2 p_vec) const {
291         return p_vec - this * this.dot(p_vec) * 2.0;
292     }
293 
294     real_t angle() const {
295         return atan2(y, x);
296     }
297 
298     void setRotation(real_t p_radians) {
299         x = cos(p_radians);
300         y = sin(p_radians);
301     }
302 
303     Vector2 abs() const {
304         return Vector2(fabs(x), fabs(y));
305     }
306 
307     Vector2 rotated(real_t p_by) const {
308         Vector2 v = void;
309         v.setRotation(angle() + p_by);
310         v *= length();
311         return v;
312     }
313 
314     Vector2 tangent() const {
315         return Vector2(y, -x);
316     }
317 
318     Vector2 floor() const {
319         return Vector2(.floor(x), .floor(y));
320     }
321 
322     Vector2 snapped(in Vector2 p_by) const {
323         return Vector2(
324             p_by.x != 0 ? .floor(x / p_by.x + 0.5) * p_by.x : x,
325             p_by.y != 0 ? .floor(y / p_by.y + 0.5) * p_by.y : y
326         );
327     }
328 }
329 
330 // TODO: replace this stub
331 struct Vector2i {
332 @nogc nothrow:
333 
334     union {
335         struct {
336             union {
337                 godot_int x = 0; /// 
338                 godot_int width; /// 
339             }
340 
341             union {
342                 godot_int y = 0; /// 
343                 godot_int height; /// 
344             }
345         }
346 
347         godot_int[2] coord;
348     }
349 
350     this(godot_int x, godot_int y) {
351         this.x = x;
352         this.y = y;
353     }
354 
355     this(int[2] coord) {
356         this.coord = cast(godot_int[]) coord;
357     }
358 
359     this(in Vector2i b) {
360         this.x = b.x;
361         this.y = b.y;
362     }
363 
364     this(in godot_vector2i b) {
365         this.x = b._opaque[0];
366         this.y = b._opaque[1];
367     }
368 
369     void opAssign(in Vector2i b) {
370         this.x = b.x;
371         this.y = b.y;
372     }
373 
374     void opAssign(in godot_vector2i b) {
375         this.x = b._opaque[0];
376         this.y = b._opaque[1];
377     }
378 
379     ref godot_int opIndex(int axis) return {
380         return axis ? y : x;
381     }
382 
383     const(godot_int) opIndex(int axis) const {
384         return axis ? y : x;
385     }
386 
387     Vector2i opBinary(string op)(in Vector2i other) const
388     if (op == "+" || op == "-" || op == "*" || op == "/") {
389         Vector2i ret;
390         ret.x = mixin("x " ~ op ~ "other.x");
391         ret.y = mixin("y " ~ op ~ "other.y");
392         return ret;
393     }
394 
395     void opOpAssign(string op)(in Vector2i other)
396             if (op == "+" || op == "-" || op == "*" || op == "/") {
397         x = mixin("x " ~ op ~ "other.x");
398         y = mixin("y " ~ op ~ "other.y");
399     }
400 
401     Vector2i opUnary(string op : "-")() {
402         return Vector2i(-x, -y);
403     }
404 
405     Vector2i opBinary(string op)(in godot_int scalar) const
406     if (op == "*" || op == "/") {
407         Vector2 ret;
408         ret.x = mixin("x " ~ op ~ " scalar");
409         ret.y = mixin("y " ~ op ~ " scalar");
410         return ret;
411     }
412 
413     Vector2i opBinaryRight(string op)(in godot_int scalar) const
414     if (op == "*") {
415         Vector2i ret;
416         ret.x = mixin("x " ~ op ~ " scalar");
417         ret.y = mixin("y " ~ op ~ " scalar");
418         return ret;
419     }
420 
421     void opOpAssign(string op)(in godot_int scalar) if (op == "*" || op == "/") {
422         x = mixin("x " ~ op ~ " scalar");
423         y = mixin("y " ~ op ~ " scalar");
424     }
425 
426     int opCmp(in Vector2 other) const {
427         import std.algorithm.comparison;
428         import std.range;
429 
430         return cmp(only(x, y), only(other.x, other.y));
431     }
432 
433     Vector2 opCast(Vector2)() const {
434         return Vector2(x, y);
435     }
436 
437     real_t aspect() const {
438         return width / cast(real_t) height;
439     }
440 
441     real_t length() const {
442         return cast(real_t) sqrt(cast(double) lengthSquared());
443     }
444 
445     int64_t lengthSquared() const {
446         return (x * cast(int64_t) x) + (y * cast(int64_t) y);
447     }
448 
449     Vector2i abs() const {
450         import std.math : abs;
451 
452         return Vector2i(.abs(x), .abs(y));
453     }
454 
455     Vector2i clamp(in Vector2i p_min, in Vector2i p_max) const {
456         import std.algorithm.comparison : _clamp = clamp; // template looses priority to local symbol
457         return Vector2i(_clamp(x, p_min.x, p_max.x), _clamp(y, p_min.y, p_max.y));
458     }
459 }