1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Math.Vector3;
25 
26 private {
27 	debug import std.stdio : writeln;
28 	import std.math : pow, sqrt;
29 	import std.traits : isNumeric;
30 }
31 
32 /**
33  * Vector3 is a structure that defines a two-dimensional point.
34  *
35  * Author: rschuett
36  */
37 struct Vector3(T) if (isNumeric!T) {
38 	/**
39 	 * x coordinate
40 	 */
41 	T x = 0;
42 	/**
43 	 * y coordinate
44 	 */
45 	T y = 0;
46 	/**
47 	 * z coordinate
48 	 */
49 	T z = 0;
50 	
51 	/**
52 	 * CTor
53 	 */
54 	this(T x, T y, T z = 0) pure nothrow {
55 		this.x = x;
56 		this.y = y;
57 		this.z = z;
58 	}
59 	
60 	/**
61 	 * CTor
62 	 */
63 	this(U)(U x, U y, U z = 0) pure nothrow if (isNumeric!U && !is(U : T)) {
64 		this(cast(T) x, cast(T) y, cast(T) z);
65 	}
66 	
67 	/**
68 	 * CTor
69 	 */
70 	this(U)(U[3] pos) pure nothrow if (isNumeric!U) {
71 		this(pos[0], pos[1], pos[2]);
72 	}
73 	
74 	/**
75 	 * CTor
76 	 */
77 	this(U)(ref const Vector3!U vec) pure nothrow {
78 		this(vec.x, vec.y, vec.z);
79 	}
80 	
81 //	debug(Dgame)
82 //	this(this) {
83 //		writeln("Postblit Vector3");
84 //	}
85 	
86 	/**
87 	 * Supported operation: +=, -=, *=, /= and %=
88 	 */
89 	ref Vector3 opOpAssign(string op)(ref const Vector3 vec) pure {
90 		switch (op) {
91 			case "+":
92 				this.x += vec.x;
93 				this.y += vec.y;
94 				this.z += vec.z;
95 				break;
96 			case "-":
97 				this.x -= vec.x;
98 				this.y -= vec.y;
99 				this.z -= vec.z;
100 				break;
101 			case "*":
102 				this.x *= vec.x;
103 				this.y *= vec.y;
104 				this.z *= vec.z;
105 				break;
106 			case "/":
107 				this.x /= vec.x;
108 				this.y /= vec.y;
109 				this.z /= vec.z;
110 				break;
111 			case "%":
112 				this.x %= vec.x;
113 				this.y %= vec.y;
114 				this.z %= vec.z;
115 				break;
116 			default: throw new Exception("Unsupported operator " ~ op);
117 		}
118 		
119 		return this;
120 	}
121 	
122 	/**
123 	 * Supported operation: +=, -=, *=, /= and %=
124 	 */
125 	ref Vector3 opOpAssign(string op)(T number) pure {
126 		switch (op) {
127 			case "+":
128 				this.x += number;
129 				this.y += number;
130 				this.z += number;
131 				break;
132 			case "-":
133 				this.x -= number;
134 				this.y -= number;
135 				this.z -= number;
136 				break;
137 			case "*":
138 				this.x *= number;
139 				this.y *= number;
140 				this.z *= number;
141 				break;
142 			case "/":
143 				this.x /= number;
144 				this.y /= number;
145 				this.z /= number;
146 				break;
147 			case "%":
148 				this.x %= number;
149 				this.y %= number;
150 				this.z %= number;
151 				break;
152 			default: throw new Exception("Unsupported operator " ~ op);
153 		}
154 		
155 		return this;
156 	}
157 	
158 	/**
159 	 * Supported operation: +, -, *, / and %
160 	 */
161 	Vector3 opBinary(string op)(ref const Vector3 vec) pure {
162 		switch (op) {
163 			case "+": return Vector3(vec.x + this.x, vec.y + this.y, vec.z + this.z);
164 			case "-": return Vector3(vec.x - this.x, vec.y - this.y, vec.z - this.z);
165 			case "*": return Vector3(vec.x * this.x, vec.y * this.y, vec.z * this.z);
166 			case "/": return Vector3(vec.x / this.x, vec.y / this.y, vec.z / this.z);
167 			case "%": return Vector3(vec.x % this.x, vec.y % this.y, vec.z % this.z);
168 			default: throw new Exception("Unsupported operator " ~ op);
169 		}
170 	}
171 	
172 	/**
173 	 * Supported operation: +, -, *, / and %
174 	 */
175 	Vector3 opBinary(string op)(T number) pure {
176 		switch (op) {
177 			case "+": return Vector3(number + this.x, number + this.y, number + this.z);
178 			case "-": return Vector3(number - this.x, number - this.y, number - this.z);
179 			case "*": return Vector3(number * this.x, number * this.y, number * this.z);
180 			case "/": return Vector3(number / this.x, number / this.y, number / this.z);
181 			case "%": return Vector3(number % this.x, number % this.y, number % this.z);
182 			default: throw new Exception("Unsupported operator " ~ op);
183 		}
184 	}
185 	
186 	/**
187 	 * Returns a negated copy of this Vector.
188 	 */
189 	Vector3 opNeg() const pure nothrow {
190 		return Vector3(-this.x, -this.y, -this.z);
191 	}
192 	
193 	/**
194 	 * Negate this Vector
195 	 */
196 	void negate() pure nothrow {
197 		this.x = -this.x;
198 		this.y = -this.y;
199 		this.z = -this.z;
200 	}
201 	
202 	/**
203 	 * Compares two vectors by checking whether the coordinates are equals.
204 	 */
205 	bool opEquals(ref const Vector3 vec) const pure nothrow {
206 		return vec.x == this.x && vec.y == this.y && vec.z == this.z;
207 	}
208 
209 	/**
210 	 * Checks if this vector is empty. This means that his coordinates are 0.
211 	 */
212 	bool isEmpty() const pure nothrow {
213 		return this.x == 0 && this.y == 0 && this.z == 0;
214 	}
215 	
216 	/**
217 	 * Calculate the scalar product.
218 	 */
219 	float scalar(ref const Vector3 vec) const pure nothrow {
220 		return this.x * vec.x + this.y * vec.y + this.z * vec.z;
221 	}
222 	
223 	/**
224 	 * alias for scalar
225 	 */
226 	alias dot = scalar;
227 	
228 	/**
229 	 * Calculate the length.
230 	 */
231 	@property
232 	float length() const pure nothrow {
233 		if (this.isEmpty())
234 			return 0f;
235 		
236 		return sqrt(pow(this.x, 2f) + pow(this.y, 2f) + pow(this.z, 2f));
237 	}
238 	
239 	/**
240 	 * Calculate the diff between two vectors.
241 	 */
242 	float diff(ref const Vector3 vec) const pure nothrow {
243 		return sqrt(pow(this.x - vec.x, 2f) + pow(this.y - vec.y, 2f) + pow(this.z - vec.z, 2f));
244 	}
245 	
246 	/**
247 	 * Returns the cross product of this and another Vector.
248 	 */
249 	Vector3 cross(ref const Vector3 vec) const pure nothrow {
250 		return Vector3(this.y * vec.z - this.z * vec.y,
251 		               this.z * vec.x - this.x * vec.z,
252 		               this.x * vec.y - this.y * vec.x);
253 	}
254 	
255 	/**
256 	 * Set new coordinates.
257 	 */
258 	void set(T x, T y, T z) pure nothrow {
259 		this.x = x;
260 		this.y = y;
261 		this.z = z;
262 	}
263 	
264 	/**
265 	 * Move the current coordinates.
266 	 */
267 	void move(T x, T y, T z) pure nothrow {
268 		this.x += x;
269 		this.y += y;
270 		this.z += z;
271 	}
272 	
273 	/**
274 	 * Returns the Vector as static array.
275 	 */
276 	T[3] asArray() const pure nothrow {
277 		return [this.x, this.y, this.z];
278 	}
279 }
280 
281 /**
282  * Alias for short Vector
283  */
284 alias Vector3s = Vector3!(short);
285 /**
286  * Alias for float Vector
287  */
288 alias Vector3f = Vector3!(float);
289 /**
290  * Alias for byte Vector
291  */
292 alias Vector3b = Vector3!(byte);