1 module prova.math.vector3;
2 
3 import prova.math;
4 import std.math;
5 
6 ///
7 struct Vector3
8 {
9   ///
10   static immutable auto left = Vector3(-1, 0, 0);
11   ///
12   static immutable auto right = Vector3(1, 0, 0);
13   ///
14   static immutable auto up = Vector3(0, 1, 0);
15   ///
16   static immutable auto down = Vector3(0, -1, 0);
17   ///
18   static immutable auto forward = Vector3(0, 0, -1);
19   ///
20   static immutable auto back = Vector3(0, 0, 1);
21 
22   ///
23   float x = 0;
24   ///
25   float y = 0;
26   ///
27   float z = 0;
28 
29   ///
30   this(float x, float y, float z)
31   {
32     set(x, y, z);
33   }
34 
35   ///
36   this(Vector2 vector)
37   {
38     set(vector.x, vector.y, 0);
39   }
40 
41   /// Sets the values of x, y, and z in a single statement
42   void set(float x, float y, float z)
43   {
44     this.x = x;
45     this.y = y;
46     this.z = z;
47   }
48 
49   /// Creates a normalized vector with a random direction
50   static Vector3 random()
51   {
52     Vector3 vector = Vector3(
53       randomF(-1, 1),
54       randomF(-1, 1),
55       randomF(-1, 1)
56     );
57 
58     vector.normalize();
59 
60     return vector;
61   }
62 
63   ///
64   @property Vector2 xy() const
65   {
66     return Vector2(x, y);
67   }
68 
69   /// Returns a normalized copy of this vector
70   Vector3 getNormalized() const
71   {
72     const float magnitude = getMagnitude();
73 
74     Vector3 result;
75 
76     if(magnitude != 0) {
77       result.x = x / magnitude;
78       result.y = y / magnitude;
79       result.z = y / magnitude;
80     }
81 
82     return result;
83   }
84 
85   /// Normalizes the vector
86   void normalize()
87   {
88     const float magnitude = getMagnitude();
89 
90     if(magnitude == 0)
91       return;
92 
93     x = x / magnitude;
94     y = y / magnitude;
95     z = z / magnitude;
96   }
97 
98   /// Returns the magnitude of the vector
99   float getMagnitude() const
100   {
101     return sqrt(x * x + y * y + z * z);
102   }
103 
104   /**
105    * Sets the magnitude of this vector
106    *
107    * If the previous magnitude is zero, the x value
108    * of the vector will be set to the magnitude
109    */
110   void setMagnitude(float magnitude)
111   {
112     if(getMagnitude() == 0) {
113       x = magnitude;
114       return;
115     }
116 
117     normalize();
118 
119     x *= magnitude;
120     y *= magnitude;
121     z *= magnitude;
122   }
123 
124   ///
125   Quaternion getDirection() const
126   {
127     Vector3 normalizedThis = getNormalized();
128     Vector3 perpendicular = forward.cross(normalizedThis);
129 
130     auto result = Quaternion.fromAxisAngle(perpendicular, 90);
131 
132     // flip if the this vector is > 180deg away from forward
133     if(z > 0) {
134       result *= Quaternion(0, 1, 0, 0);
135       result.y *= -1;
136     }
137 
138     return result;
139   }
140 
141   /// Returns the distance between the vectors
142   float distanceTo(Vector3 vector) const
143   {
144     const float a = vector.x - x;
145     const float b = vector.y - y;
146     const float c = vector.z - z;
147 
148     return sqrt(a * a + b * b + c * c);
149   }
150 
151   /// Returns the squared distance between the vectors
152   float distanceToSquared(Vector3 vector) const
153   {
154     const float a = vector.x - x;
155     const float b = vector.y - y;
156     const float c = vector.z - z;
157 
158     return a * a + b * b + c * c;
159   }
160 
161   /// Returns the dot product of the two vectors
162   float dot(Vector3 vector) const
163   {
164     return x * vector.x + y * vector.y + z * vector.z;
165   }
166 
167   /// Returns the cross product of the two vectors
168   Vector3 cross(Vector3 vector) const
169   {
170     Vector3 result;
171     result.x = y * vector.z - vector.y * z;
172     result.y = -(x * vector.z - vector.x * z);
173     result.z = x * vector.y - vector.x * y;
174 
175     return result;
176   }
177 
178 
179   // assignment overloading
180   ///
181   Vector3 opAddAssign(Vector3 vector)
182   {
183     x += vector.x;
184     y += vector.y;
185     z += vector.z;
186 
187     return this;
188   }
189 
190   ///
191   Vector3 opAddAssign(Vector2 vector)
192   {
193     x += vector.x;
194     y += vector.y;
195 
196     return this;
197   }
198 
199   ///
200   Vector3 opSubAssign(Vector3 vector)
201   {
202     x -= vector.x;
203     y -= vector.y;
204     z -= vector.z;
205 
206     return this;
207   }
208 
209   ///
210   Vector3 opSubAssign(Vector2 vector)
211   {
212     x -= vector.x;
213     y -= vector.y;
214 
215     return this;
216   }
217 
218   ///
219   Vector3 opMulAssign(Vector3 vector)
220   {
221     x *= vector.x;
222     y *= vector.y;
223     z *= vector.z;
224 
225     return this; 
226   }
227 
228   ///
229   Vector3 opMulAssign(float a)
230   {
231     x *= a;
232     y *= a;
233     z *= a;
234 
235     return this;
236   }
237 
238   ///
239   Vector3 opDivAssign(Vector3 vector)
240   {
241     x /= vector.x;
242     y /= vector.y;
243     z /= vector.z;
244 
245     return this;
246   }
247 
248   ///
249   Vector3 opDivAssign(float a)
250   {
251     x /= a;
252     y /= a;
253     z /= a;
254 
255     return this;
256   }
257 
258 
259   // arithmetic overloading
260   ///
261   Vector3 opAdd(Vector3 vector) const
262   {
263     Vector3 result;
264     result.x = x + vector.x;
265     result.y = y + vector.y;
266     result.z = z + vector.z;
267 
268     return result;
269   }
270 
271   ///
272   Vector3 opAdd(Vector2 vector) const
273   {
274     Vector3 result;
275     result.x = x + vector.x;
276     result.y = y + vector.y;
277     result.z = z;
278 
279     return result; 
280   }
281 
282   ///
283   Vector3 opSub(Vector3 vector) const
284   {
285     Vector3 result;
286     result.x = x - vector.x;
287     result.y = y - vector.y;
288     result.z = z - vector.z;
289 
290     return result; 
291   }
292 
293   ///
294   Vector3 opSub(Vector2 vector) const
295   {
296     Vector3 result;
297     result.x = x - vector.x;
298     result.y = y - vector.y;
299     result.z = z;
300 
301     return result; 
302   }
303 
304   ///
305   Vector3 opUnary(string s)() const if (s == "-")
306   {
307     Vector3 result;
308     result.x = -x;
309     result.y = -y;
310     result.z = -z;
311 
312     return result;
313   }
314 
315   ///
316   Vector3 opMul(Vector3 vector) const
317   {
318     Vector3 result;
319     result.x = x * vector.x;
320     result.y = y * vector.y;
321     result.z = z * vector.z;
322 
323     return result; 
324   }
325 
326   ///
327   Vector3 opMul(float a) const
328   {
329     Vector3 result;
330     result.x = x * a;
331     result.y = y * a;
332     result.z = z * a;
333 
334     return result; 
335   }
336 
337   ///
338   Vector3 opDiv(Vector3 vector)
339   {
340     Vector3 result;
341     result.x = x / vector.x;
342     result.y = y / vector.y;
343     result.z = z / vector.z;
344 
345     return result;
346   }
347 
348   ///
349   Vector3 opDiv(float a) const
350   {
351     Vector3 result;
352     result.x = x / a;
353     result.y = y / a;
354     result.z = z / a;
355 
356     return result; 
357   }
358 
359   ///
360   void opAssign(Vector2 vector)
361   {
362     x = vector.x;
363     y = vector.y;
364     z = 0;
365   }
366 }