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 
28 import std.traits : isNumeric;
29 static import std.math;
30 
31 public:
32 
33 /**
34  * Vector3 is a structure that defines a three-dimensional point.
35  *
36  * Author: Randy Schuett (rswhite4@googlemail.com)
37  */
38 struct Vector3(T) if (isNumeric!(T)) {
39     T x = 0; /// The x coordinate
40     T y = 0; /// The y coordinate
41     T z = 0; /// The z coordinate
42 
43     /**
44      * CTor
45      */
46     @nogc
47     this(T x, T y, T z = 0) pure nothrow {
48         this.x = x;
49         this.y = y;
50         this.z = z;
51     }
52     
53     /**
54      * CTor
55      */
56     @nogc
57     this(U)(U x, U y, U z = 0) pure nothrow if (isNumeric!(U) && !is(U == T)) {
58         this(cast(T) x, cast(T) y, cast(T) z);
59     }
60     
61     /**
62      * CTor
63      */
64     @nogc
65     this(U)(const Vector3!(U) vec) pure nothrow if (!is(U == T)) {
66         this(vec.x, vec.y, vec.z);
67     }
68 
69     /**
70      * Compares two vectors by checking whether the coordinates are equals.
71      */
72     @nogc
73     bool opEquals(const Vector3!(T) vec) const pure nothrow {
74         return vec.x == this.x && vec.y == this.y && vec.z == this.z;
75     }
76 
77     /**
78      * Checks if this vector is empty. This means that his coordinates are 0.
79      */
80     @nogc
81     bool isEmpty() const pure nothrow {
82         return this.x == 0 && this.y == 0 && this.z == 0;
83     }
84     
85     /**
86      * Calculate the scalar product.
87      */
88     @nogc
89     float scalar(const Vector3!(T) vec) const pure nothrow {
90         return this.x * vec.x + this.y * vec.y + this.z * vec.z;
91     }
92     
93     /**
94      * alias for scalar
95      */
96     alias dot = scalar;
97     
98     /**
99      * Calculate the length.
100      */
101     @nogc
102     @property
103     float length() const pure nothrow {
104         if (this.isEmpty())
105             return 0f;
106         return std.math.sqrt(std.math.pow(this.x, 2f) + std.math.pow(this.y, 2f) + std.math.pow(this.z, 2f));
107     }
108     
109     /**
110      * Calculate the diff between two vectors.
111      */
112     @nogc
113     float diff(const Vector3!(T) vec) const pure nothrow {
114         return std.math.sqrt(std.math.pow(this.x - vec.x, 2f) + std.math.pow(this.y - vec.y, 2f) + std.math.pow(this.z - vec.z, 2f));
115     }
116 
117     /**
118      * Supported operation: +=, -=, *=, /= and %=
119      */
120     @nogc
121     ref Vector3!(T) opOpAssign(string op)(const Vector3!(T) vec) pure nothrow {
122         switch (op) {
123             case "+":
124             case "-":
125             case "*":
126             case "/":
127             case "%":
128                 mixin("this.x " ~ op ~ "= vec.x;");
129                 mixin("this.y " ~ op ~ "= vec.y;");
130                 mixin("this.z " ~ op ~ "= vec.z;");
131             break;
132             default:
133                 assert(0, "Unsupported operator " ~ op);
134         }
135         
136         return this;
137     }
138     
139     /**
140      * Supported operation: +=, -=, *=, /= and %=
141      */
142     @nogc
143     ref Vector3!(T) opOpAssign(string op)(float num) pure nothrow {
144         switch (op) {
145             case "+":
146             case "-":
147             case "*":
148             case "/":
149             case "%":
150                 mixin("this.x = cast(T)(this.x " ~ op ~ " num);");
151                 mixin("this.y = cast(T)(this.y " ~ op ~ " num);");
152                 mixin("this.z = cast(T)(this.z " ~ op ~ " num);");
153             break;
154             default:
155                 assert(0, "Unsupported operator " ~ op);
156         }
157         
158         return this;
159     }
160 
161     /**
162      * Supported operation: +, -, *, / and %
163      */
164     @nogc
165     Vector3!(T) opBinary(string op)(const Vector3!(T) vec) const pure nothrow {
166         switch (op) {
167             case "+":
168             case "-":
169             case "*":
170             case "/":
171             case "%":
172                 mixin("return Vector3!(T)(this.x " ~ op ~ " vec.x, this.y " ~ op ~ " vec.y, this.z " ~ op ~ " vec.z);");
173             default:
174                 assert(0, "Unsupported operator " ~ op);
175         }
176     }
177     
178     /**
179      * Supported operation: +, -, *, / and %
180      */
181     @nogc
182     Vector3!(T) opBinary(string op)(float num) const pure {
183         switch (op) {
184             case "+":
185             case "-":
186             case "*":
187             case "/":
188             case "%":
189                 mixin("return Vector3!(T)(cast(T)(this.x " ~ op ~ " num), cast(T)(this.y " ~ op ~ " num), cast(T)(this.z " ~ op ~ " num));");
190             default:
191                 assert(0, "Unsupported operator " ~ op);
192         }
193     }
194 
195     /**
196      * Returns the cross product of this and another Vector.
197      */
198     @nogc
199     Vector3!(T) cross(const Vector3!(T) vec) const pure nothrow {
200         return Vector3!(T)(this.y * vec.z - this.z * vec.y, this.z * vec.x - this.x * vec.z, this.x * vec.y - this.y * vec.x);
201     }
202 
203     /**
204      * Normalize the vector in which the coordinates are divided by the length.
205      */
206     Vector3!(T) normalize() pure nothrow {
207         immutable float len = this.length;
208         if (len > 0)
209             return this / len;
210         return this;
211     }
212 
213     /**
214      * Rotate the current Vector by an angle and rotation Vector and returns the result
215      */
216     Vector3!(T) rotate(float angle, const Vector3!(T) rot) const pure nothrow {
217         immutable float len1 = this.length;
218         immutable float len2 = rot.length;
219 
220         assert(len1 > 0 && len2 > 0);
221 
222         const Vector3!(T) norm1 = this / len1;
223         const Vector3!(T) norm2 = rot / len2;
224 
225         immutable float rho_rad = angle / 180 * std.math.PI;
226         immutable float c = std.math.cos(rho_rad);
227         immutable float s = std.math.sin(rho_rad);
228         immutable float t = 1 - c;
229 
230         immutable float norm_final_x = norm1.x * (t * norm2.x * norm2.x + c) + norm1.y * (t * norm2.x * norm2.y - s * norm2.z) + norm1.z * (t * norm2.x * norm2.z + s * norm2.y);
231         immutable float norm_final_y = norm1.x * (t * norm2.x * norm2.y + s * norm2.z) + norm1.y * (t * norm2.y * norm2.y + c) + norm1.z * (t * norm2.y * norm2.z - s * norm2.x);
232         immutable float norm_final_z = norm1.x * (t * norm2.x * norm2.z - s * norm2.y) + norm1.y * (t * norm2.y * norm2.z + s * norm2.x) + norm1.z * (t * norm2.z * norm2.z + c);
233 
234         Vector3!(T) final_norm = Vector3!(T)(norm_final_x, norm_final_y, norm_final_z);
235         final_norm *= this.length;
236 
237         return final_norm;
238     }
239 }
240 
241 alias Vector3f = Vector3!(float); /// A float representation
242 alias Vector3i = Vector3!(int); /// An int representation