1 /**
2 Vector used for 2D Math.
3 
4 Copyright:
5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
7 Copyright (c) 2017-2018 Godot-D contributors  
8 
9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
10 
11 
12 */
13 module godot.core.vector2;
14 
15 import godot.core.defs;
16 
17 import std.math;
18 
19 private bool isValidSwizzle(dstring s)
20 {
21 	import std.algorithm : canFind;
22 	if(s.length != 2 && s.length != 3) return false;
23 	foreach(dchar c; s)
24 	{
25 		if(!"xyn".canFind(c)) return false;
26 	}
27 	return true;
28 }
29 
30 /**
31 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values.
32 */
33 struct Vector2
34 {
35 	@nogc nothrow:
36 	
37 	union
38 	{
39 		struct
40 		{
41 			union
42 			{
43 				real_t x = 0.0; /// 
44 				real_t width; /// 
45 			}
46 			union
47 			{
48 				real_t y = 0.0; /// 
49 				real_t height; /// 
50 			}
51 		}
52 
53 		real_t[2] coord;
54 	}
55 	
56 	import std.algorithm : count;
57 	/++
58 	Swizzle the vector with x, y, or n. Pass floats as args for any n's; if
59 	there are more n's than args, the last arg is used for the rest. If no args
60 	are passed at all, 0.0 is used for each n.
61 	
62 	The swizzle must be 2 or 3 characters, as Godot only has Vector2/3.
63 	+/
64 	auto opDispatch(string swizzle, size_t nArgCount)(float[nArgCount] nArgs...) const
65 		if(swizzle.isValidSwizzle && nArgCount <= swizzle.count('n'))
66 	{
67 		import godot.core.vector3;
68 		import std.algorithm : min, count;
69 		static if(swizzle.length == 2) Vector2 ret = void;
70 		else Vector3 ret = void;
71 		/// how many n's already appeared before ci, which equals the index into nArgs for the n at ci
72 		enum ni(size_t ci) = min(nArgCount-1, swizzle[0..ci].count('n'));
73 		static foreach(ci, c; swizzle)
74 		{
75 			static if(c == 'n')
76 			{
77 				static if(nArgCount == 0) ret.coord[ci] = 0f;
78 				else static if(ni!ci >= nArgCount) ret.coord[ci] = nArgs[nArgCount-1];
79 				else ret.coord[ci] = nArgs[ni!ci];
80 			}
81 			else ret.coord[ci] = mixin([c]);
82 		}
83 		return ret;
84 	}
85 	
86 	this(real_t x, real_t y)
87 	{
88 		this.x = x;
89 		this.y = y;
90 	}
91 	
92 	this(real_t[2] coord)
93 	{
94 		this.coord = coord;
95 	}
96 	
97 	this(in Vector2 b)
98 	{
99 		this.x = b.x;
100 		this.y = b.y;
101 	}
102 	
103 	void opAssign(in Vector2 b)
104 	{
105 		this.x = b.x;
106 		this.y = b.y;
107 	}
108 	
109 	ref real_t opIndex(int axis) return
110 	{
111 		return axis?y:x;
112 	}
113 	const(real_t) opIndex(int axis) const
114 	{
115 		return axis?y:x;
116 	}
117 	
118 	Vector2 opBinary(string op)(in Vector2 other) const
119 		if(op=="+" || op=="-" || op=="*" || op=="/")
120 	{
121 		Vector2 ret;
122 		ret.x = mixin("x "~op~"other.x");
123 		ret.y = mixin("y "~op~"other.y");
124 		return ret;
125 	}
126 	void opOpAssign(string op)(in Vector2 other)
127 		if(op=="+" || op=="-" || op=="*" || op=="/")
128 	{
129 		x = mixin("x "~op~"other.x");
130 		y = mixin("y "~op~"other.y");
131 	}
132 	
133 	Vector2 opUnary(string op : "-")()
134 	{
135 		return Vector2(-x, -y);
136 	}
137 	
138 	Vector2 opBinary(string op)(in real_t scalar) const
139 		if(op=="*" || op=="/")
140 	{
141 		Vector2 ret;
142 		ret.x = mixin("x "~op~" scalar");
143 		ret.y = mixin("y "~op~" scalar");
144 		return ret;
145 	}
146 	Vector2 opBinaryRight(string op)(in real_t scalar) const
147 		if(op=="*")
148 	{
149 		Vector2 ret;
150 		ret.x = mixin("x "~op~" scalar");
151 		ret.y = mixin("y "~op~" scalar");
152 		return ret;
153 	}
154 	void opOpAssign(string op)(in real_t scalar)
155 		if(op=="*" || op=="/")
156 	{
157 		x = mixin("x "~op~" scalar");
158 		y = mixin("y "~op~" scalar");
159 	}
160 	
161 	int opCmp(in Vector2 other) const
162 	{
163 		import std.algorithm.comparison;
164 		import std.range;
165 		return cmp(only(x,y), only(other.x, other.y));
166 	}
167 	
168 	real_t aspect() const
169 	{
170 		return width/height;
171 	}
172 	
173 	void normalize()
174 	{
175 		real_t l = x*x + y*y;
176 		if (l != 0)
177 		{
178 			l = sqrt(l);
179 			x /= l;
180 			y /= l;
181 		}
182 	}
183 	
184 	Vector2 normalized() const
185 	{
186 		Vector2 v = this;
187 		v.normalize();
188 		return v;
189 	}
190 	
191 	real_t length() const
192 	{
193 		return sqrt(x*x + y*y);
194 	}
195 	real_t lengthSquared() const
196 	{
197 		return x*x + y*y;
198 	}
199 	
200 	real_t distanceTo(in Vector2 p_vector2) const
201 	{
202 		return sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y));
203 	}
204 	
205 	real_t distanceSquaredTo(in Vector2 p_vector2) const
206 	{
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 	{
212 		return atan2(cross(p_vector2), dot(p_vector2));
213 	}
214 	
215 	real_t angleToPoint(in Vector2 p_vector2) const
216 	{
217 		return atan2(y - p_vector2.y, x-p_vector2.x);
218 	}
219 	
220 	real_t dot(in Vector2 p_other) const
221 	{
222 		return x * p_other.x + y * p_other.y;
223 	}
224 	
225 	real_t cross(in Vector2 p_other) const
226 	{
227 		return x * p_other.y - y * p_other.x;
228 	}
229 	
230 	Vector2 cross(real_t p_other) const
231 	{
232 		return Vector2(p_other * y, -p_other * x);
233 	}
234 	
235 	Vector2 project(in Vector2 p_vec) const
236 	{
237 		Vector2 v1 = p_vec;
238 		Vector2 v2 = this;
239 		return v2 * (v1.dot(v2) / v2.dot(v2));
240 	}
241 	
242 	Vector2 planeProject(real_t p_d, in Vector2 p_vec) const
243 	{
244 		return  p_vec - this * (dot(p_vec) -p_d);
245 	}
246 	
247 	Vector2 clamped(real_t p_len) const
248 	{
249 		real_t l = length();
250 		Vector2 v = this;
251 		if (l > 0 && p_len < l)
252 		{
253 			v /= l;
254 			v *= p_len;
255 		}
256 		return v;
257 	}
258 	
259 	static Vector2 linearInterpolate(in Vector2 p_a, in Vector2 p_b, real_t p_t)
260 	{
261 		Vector2 res=p_a;
262 		res.x+= (p_t * (p_b.x-p_a.x));
263 		res.y+= (p_t * (p_b.y-p_a.y));
264 		return res;
265 	}
266 	
267 	Vector2 linearInterpolate(in Vector2 p_b,real_t p_t) const
268 	{
269 		Vector2 res=this;
270 		res.x+= (p_t * (p_b.x-x));
271 		res.y+= (p_t * (p_b.y-y));
272 		return res;
273 	
274 	}
275 	alias lerp = linearInterpolate;
276 	
277 	Vector2 cubicInterpolate(in Vector2 p_b,in Vector2 p_pre_a, in Vector2 p_post_b, real_t p_t) const
278 	{
279 		Vector2 p0=p_pre_a;
280 		Vector2 p1=this;
281 		Vector2 p2=p_b;
282 		Vector2 p3=p_post_b;
283 	
284 		real_t t = p_t;
285 		real_t t2 = t * t;
286 		real_t t3 = t2 * t;
287 	
288 		Vector2 ret;
289 		ret = ( ( p1 * 2.0) +
290 		( -p0 + p2 ) * t +
291 		( p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3 ) * t2 +
292 		( -p0 + p1 * 3.0 - p2 * 3.0 + p3 ) * t3 ) * 0.5;
293 	
294 		return ret;
295 	}
296 	
297 	
298 	Vector2 slide(in Vector2 p_vec) const
299 	{
300 		return p_vec - this * this.dot(p_vec);
301 	}
302 	
303 	Vector2 reflect(in Vector2 p_vec) const
304 	{
305 		return p_vec - this * this.dot(p_vec) * 2.0;
306 	}
307 	
308 	real_t angle() const
309 	{
310 		return atan2(y, x);
311 	}
312 	
313 	void setRotation(real_t p_radians)
314 	{
315 		x = cos(p_radians);
316 		y = sin(p_radians);
317 	}
318 	
319 	Vector2 abs() const
320 	{
321 		return Vector2( fabs(x), fabs(y) );
322 	}
323 	
324 	Vector2 rotated(real_t p_by) const
325 	{
326 		Vector2 v = void;
327 		v.setRotation(angle() + p_by);
328 		v *= length();
329 		return v;
330 	}
331 	
332 	Vector2 tangent() const
333 	{
334 		return Vector2(y,-x);
335 	}
336 	
337 	Vector2 floor() const
338 	{
339 		return Vector2(.floor(x), .floor(y));
340 	}
341 	
342 	Vector2 snapped(in Vector2 p_by) const
343 	{
344 		return Vector2(
345 			p_by.x != 0 ? .floor(x / p_by.x + 0.5) * p_by.x : x,
346 			p_by.y != 0 ? .floor(y / p_by.y + 0.5) * p_by.y : y
347 		);
348 	}
349 }
350 
351 
352 
353