1 /**
2 Vector struct, which performs basic 3D vector math operations.
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.vector3;
14 
15 import godot.core.defs;
16 import godot.core.basis;
17 import godot.core..string;
18 
19 import std.math;
20 
21 private bool isValidSwizzle(dstring s)
22 {
23 	import std.algorithm : canFind;
24 	if(s.length != 2 && s.length != 3) return false;
25 	foreach(dchar c; s)
26 	{
27 		if(!"xyzn".canFind(c)) return false;
28 	}
29 	return true;
30 }
31 
32 /**
33 Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations.
34 */
35 struct Vector3
36 {
37 	@nogc nothrow:
38 	
39 	enum Axis
40 	{
41 		x,
42 		y,
43 		z
44 	}
45 
46 	union
47 	{
48 		struct
49 		{
50 			real_t x = 0; /// 
51 			real_t y = 0; /// 
52 			real_t z = 0; /// 
53 		}
54 
55 		real_t[3] coord;
56 	}
57 
58 	import std.algorithm : count;
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 	{
69 		import godot.core.vector3;
70 		import std.algorithm : min, count;
71 		static if(swizzle.length == 2) Vector2 ret = void;
72 		else Vector3 ret = void;
73 		/// how many n's already appeared before ci, which equals the index into nArgs for the n at ci
74 		enum ni(size_t ci) = min(nArgCount-1, swizzle[0..ci].count('n'));
75 		static foreach(ci, c; swizzle)
76 		{
77 			static if(c == 'n')
78 			{
79 				static if(nArgCount == 0) ret.coord[ci] = 0f;
80 				else static if(ni!ci >= nArgCount) ret.coord[ci] = nArgs[nArgCount-1];
81 				else ret.coord[ci] = nArgs[ni!ci];
82 			}
83 			else ret.coord[ci] = mixin([c]);
84 		}
85 		return ret;
86 	}
87 
88 	this(real_t x, real_t y, real_t z)
89 	{
90 		this.x = x;
91 		this.y = y;
92 		this.z = z;
93 	}
94 	
95 	this(real_t[3] coord)
96 	{
97 		this.coord = coord;
98 	}
99 	
100 	this(in Vector3 b)
101 	{
102 		this.x = b.x;
103 		this.y = b.y;
104 		this.z = b.z;
105 	}
106 	
107 	void opAssign(in Vector3 b)
108 	{
109 		this.x = b.x;
110 		this.y = b.y;
111 		this.z = b.z;
112 	}
113 	
114 	const(real_t) opIndex(int p_axis) const
115 	{
116 		return coord[p_axis];
117 	}
118 	
119 	ref real_t opIndex(int p_axis) return
120 	{
121 		return coord[p_axis];
122 	}
123 	
124 	Vector3 opBinary(string op)(in Vector3 other) const
125 		if(op=="+" || op=="-" || op=="*" || op=="/")
126 	{
127 		Vector3 ret;
128 		ret.x = mixin("x "~op~"other.x");
129 		ret.y = mixin("y "~op~"other.y");
130 		ret.z = mixin("z "~op~"other.z");
131 		return ret;
132 	}
133 	void opOpAssign(string op)(in Vector3 other)
134 		if(op=="+" || op=="-" || op=="*" || op=="/")
135 	{
136 		x = mixin("x "~op~"other.x");
137 		y = mixin("y "~op~"other.y");
138 		z = mixin("z "~op~"other.z");
139 	}
140 	
141 	Vector3 opUnary(string op : "-")()
142 	{
143 		return Vector3(-x, -y, -z);
144 	}
145 	
146 	Vector3 opBinary(string op)(in real_t scalar) const
147 		if(op=="*" || op=="/")
148 	{
149 		Vector3 ret;
150 		ret.x = mixin("x "~op~" scalar");
151 		ret.y = mixin("y "~op~" scalar");
152 		ret.z = mixin("z "~op~" scalar");
153 		return ret;
154 	}
155 	Vector3 opBinaryRight(string op)(in real_t scalar) const
156 		if(op=="*")
157 	{
158 		Vector3 ret;
159 		ret.x = mixin("x "~op~" scalar");
160 		ret.y = mixin("y "~op~" scalar");
161 		ret.z = mixin("z "~op~" scalar");
162 		return ret;
163 	}
164 	void opOpAssign(string op)(in real_t scalar)
165 		if(op=="*" || op=="/")
166 	{
167 		x = mixin("x "~op~" scalar");
168 		y = mixin("y "~op~" scalar");
169 		z = mixin("z "~op~" scalar");
170 	}
171 	
172 	int opCmp(in Vector3 other) const
173 	{
174 		import std.algorithm.comparison;
175 		return cmp(this.coord[], other.coord[]);
176 	}
177 	
178 	Vector3 abs() const
179 	{
180 		return Vector3(fabs(x), fabs(y), fabs(z));
181 	}
182 	
183 	Vector3 ceil() const
184 	{
185 		return Vector3(.ceil(x), .ceil(y), .ceil(z));
186 	}
187 	
188 	Vector3 cross(in Vector3 b) const
189 	{
190 		return Vector3(
191 			(y * b.z) - (z * b.y),
192 			(z * b.x) - (x * b.z),
193 			(x * b.y) - (y * b.x)
194 		);
195 	}
196 	
197 	Vector3 linearInterpolate(in Vector3 p_b, real_t p_t) const
198 	{
199 		return Vector3(
200 			x+(p_t * (p_b.x-x)),
201 			y+(p_t * (p_b.y-y)),
202 			z+(p_t * (p_b.z-z))
203 		);
204 	}
205 	alias lerp = linearInterpolate;
206 	
207 	Vector3 cubicInterpolate(in Vector3 b, in Vector3 pre_a, in Vector3 post_b, in real_t t) const
208 	{
209 		Vector3 p0=pre_a;
210 		Vector3 p1=this;
211 		Vector3 p2=b;
212 		Vector3 p3=post_b;
213 	
214 		real_t t2 = t * t;
215 		real_t t3 = t2 * t;
216 	
217 		Vector3 ret;
218 		ret = ( ( p1 * 2.0) +
219 		( -p0 + p2 ) * t +
220 		( p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3 ) * t2 +
221 		( -p0 + p1 * 3.0 - p2 * 3.0 + p3 ) * t3 ) * 0.5;
222 		return ret;
223 	}
224 	
225 	real_t length() const
226 	{
227 		real_t x2=x*x;
228 		real_t y2=y*y;
229 		real_t z2=z*z;
230 	
231 		return sqrt(x2+y2+z2);
232 	}
233 	
234 	real_t lengthSquared() const
235 	{
236 		real_t x2=x*x;
237 		real_t y2=y*y;
238 		real_t z2=z*z;
239 	
240 		return x2+y2+z2;
241 	}
242 	
243 	real_t distanceSquaredTo(in Vector3 b) const
244 	{
245 		return (b-this).length();
246 	}
247 	
248 	real_t distanceTo(in Vector3 b) const
249 	{
250 		return (b-this).lengthSquared();
251 	}
252 	
253 	real_t dot(in Vector3 b) const
254 	{
255 		return x*b.x + y*b.y + z*b.z;
256 	}
257 	
258 	Vector3 floor() const
259 	{
260 		return Vector3(.floor(x), .floor(y), .floor(z));
261 	}
262 	
263 	Vector3 inverse() const
264 	{
265 		return Vector3( 1.0/x, 1.0/y, 1.0/z );
266 	}
267 	
268 	int maxAxis() const
269 	{
270 		return (x < y) ? (y < z ? 2 : 1) : (x < z ? 2 : 0);
271 	}
272 	
273 	int minAxis() const
274 	{
275 		return (x < y) ? (x < z ? 0 : 2) : (y < z ? 1 : 2);
276 	}
277 	
278 	void normalize()
279 	{
280 		real_t l=length();
281 		if (l==0) {
282 			x=y=z=0;
283 		} else {
284 			x/=l;
285 			y/=l;
286 			z/=l;
287 		}
288 	}
289 	
290 	Vector3 normalized() const
291 	{
292 		Vector3 v = this;
293 		v.normalize();
294 		return v;
295 	}
296 	
297 	Vector3 reflect(in Vector3 by) const
298 	{
299 		return by - this * this.dot(by) * 2.0;
300 	}
301 	
302 	Vector3 rotated(in Vector3 axis, in real_t phi) const
303 	{
304 		Vector3 v = this;
305 		v.rotate(axis, phi);
306 		return v;
307 	}
308 	
309 	void rotate(in Vector3 axis, in real_t phi)
310 	{
311 		this=Basis(axis,phi).xform(this);
312 	}
313 	
314 	Vector3 slide(in Vector3 by) const
315 	{
316 		return by - this * this.dot(by);
317 	}
318 	
319 	void snap(real_t step)
320 	{
321 		foreach(ref v; coord) v = (step != 0)?(.floor(v/step +0.5)*step):v;
322 	}
323 	
324 	Vector3 snapped(in real_t step) const
325 	{
326 		Vector3 v = this;
327 		v.snap(step);
328 		return v;
329 	}
330 }
331 
332 
333 
334