1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module atelier.core.vec3;
7 
8 import std.math;
9 import atelier.core.vec2;
10 
11 /// Represent a mathematical 3-dimensional vector.
12 struct Vec3(T) {
13     static assert(__traits(isArithmetic, T));
14 
15     static if (__traits(isUnsigned, T)) {
16         /// {1, 1, 1} vector. Its length is not one !
17         enum one = Vec3!T(1u, 1u, 1u);
18         /// Null vector.
19         enum zero = Vec3!T(0u, 0u, 0u);
20     }
21     else {
22         static if (__traits(isFloating, T)) {
23             /// {1, 1, 1} vector. Its length is not one !
24             enum one = Vec3!T(1f, 1f, 1f);
25             /// {0.5, 0.5, 0.5} vector. Its length is not 0.5 !
26             enum half = Vec3!T(.5f, .5f, .5f);
27             /// Null vector.
28             enum zero = Vec3!T(0f, 0f, 0f);
29             /// {-1, 0, 0} vector.
30             enum left = Vec3!T(-1f, 0, 0);
31             /// {1, 0, 0} vector.
32             enum right = Vec3!T(1f, 0, 0);
33             /// {0, -1, 0} vector.
34             enum down = Vec3!T(0, -1f, 0);
35             /// {0, 1, 0} vector.
36             enum up = Vec3!T(0, 1f, 0);
37             /// {0, 0, -1} vector.
38             enum bottom = Vec3!T(0, 0, -1f);
39             /// {0, 0, 1} vector.
40             enum top = Vec3!T(0, 0, 1f);
41         }
42         else {
43             /// {1, 1, 1} vector. Its length is not one !
44             enum one = Vec3!T(1, 1, 1);
45             /// Null vector.
46             enum zero = Vec3!T(0, 0, 0);
47             /// {-1, 0, 0} vector.
48             enum left = Vec3!T(-1, 0, 0);
49             /// {1, 0, 0} vector.
50             enum right = Vec3!T(1, 0, 0);
51             /// {0, -1, 0} vector.
52             enum down = Vec3!T(0, -1, 0);
53             /// {0, 1, 0} vector.
54             enum up = Vec3!T(0, 1, 0);
55             /// {0, 0, -1} vector.
56             enum bottom = Vec3!T(0, 0, -1);
57             /// {0, 0, 1} vector.
58             enum top = Vec3!T(0, 0, 1);
59         }
60     }
61 
62     /// x-axis coordinate.
63     T x;
64     /// y-axis coordinate.
65     T y;
66     /// z-axis coordinate.
67     T z;
68 
69     @property {
70         /// Return a 2-dimensional vector with {x, y}
71         Vec2!T xy() const {
72             return Vec2!T(x, y);
73         }
74         /// Ditto
75         Vec2!T xy(Vec2!T v) {
76             x = v.x;
77             y = v.y;
78             return v;
79         }
80 
81         /// Return a 2-dimensional vector with {y, z}
82         Vec2!T yz() const {
83             return Vec2!T(y, z);
84         }
85         /// Ditto
86         Vec2!T yz(Vec2!T v) {
87             y = v.x;
88             z = v.y;
89             return v;
90         }
91 
92         /// Return a 2-dimensional vector with {x, z}
93         Vec2!T xz() const {
94             return Vec2!T(x, z);
95         }
96         /// Ditto
97         Vec2!T xz(Vec2!T v) {
98             x = v.x;
99             z = v.y;
100             return v;
101         }
102     }
103 
104     /// Build a new 3-dimensional vector.
105     this(T x_, T y_, T z_) {
106         x = x_;
107         y = y_;
108         z = z_;
109     }
110 
111     /// Ditto
112     this(Vec2!T xy_, T z_) {
113         x = xy_.x;
114         y = xy_.y;
115         z = z_;
116     }
117 
118     /// Ditto
119     this(T x_, Vec2!T yz_) {
120         x = x_;
121         y = yz_.x;
122         z = yz_.y;
123     }
124 
125     /// Changes {x, y, z}
126     void set(T x_, T y_, T z_) {
127         x = x_;
128         y = y_;
129         z = z_;
130     }
131 
132     /// Ditto
133     void set(Vec2!T xy_, T z_) {
134         x = xy_.x;
135         y = xy_.y;
136         z = z_;
137     }
138 
139     /// Ditto
140     void set(T x_, Vec2!T yz_) {
141         x = x_;
142         y = yz_.x;
143         z = yz_.y;
144     }
145 
146     /// The distance between this point and the other one.
147     T distance(Vec3!T v) const {
148         static if (__traits(isUnsigned, T))
149             alias V = int;
150         else
151             alias V = T;
152 
153         V px = x - v.x, py = y - v.y, pz = z - v.z;
154 
155         static if (__traits(isFloating, T))
156             return std.math.sqrt(px * px + py * py + pz * pz);
157         else
158             return cast(T) std.math.sqrt(cast(float)(px * px + py * py + pz * pz));
159     }
160 
161     /// The distance between this point and the other one squared.
162     /// Not square rooted, so no crash and more efficient.
163     T distanceSquared(Vec3!T v) const {
164         static if (__traits(isUnsigned, T))
165             alias V = int;
166         else
167             alias V = T;
168 
169         V px = x - v.x, py = y - v.y, pz = z - v.z;
170 
171         return px * px + py * py + pz * pz;
172     }
173 
174     /// The smaller vector possible between the two.
175     Vec3!T min(const Vec3!T v) const {
176         return Vec3!T(x < v.x ? x : v.x, y < v.y ? y : v.y, z < v.z ? z : v.z);
177     }
178 
179     /// The larger vector possible between the two.
180     Vec3!T max(const Vec3!T v) const {
181         return Vec3!T(x > v.x ? x : v.x, y > v.y ? y : v.y, z > v.z ? z : v.z);
182     }
183 
184     /// Remove negative components.
185     Vec3!T abs() const {
186         static if (__traits(isFloating, T))
187             return Vec3!T(x < .0 ? -x : x, y < .0 ? -y : y, z < .0 ? -z : z);
188         else static if (__traits(isUnsigned, T))
189             return Vec3!T(x < 0U ? -x : x, y < 0U ? -y : y, z < 0U ? -z : z);
190         else
191             return Vec3!T(x < 0 ? -x : x, y < 0 ? -y : y, z < 0 ? -z : z);
192     }
193 
194     /// Truncate the values.
195     Vec3!T floor() const {
196         static if (__traits(isFloating, T))
197             return Vec3!T(std.math.floor(x), std.math.floor(y), std.math.floor(z));
198         else
199             return this;
200     }
201 
202     /// Round up the values.
203     Vec3!T ceil() const {
204         static if (__traits(isFloating, T))
205             return Vec3!T(std.math.ceil(x), std.math.ceil(y), std.math.ceil(z));
206         else
207             return this;
208     }
209 
210     /// Round the values to the nearest integer.
211     Vec3!T round() const {
212         static if (__traits(isFloating, T))
213             return Vec3!T(std.math.round(x), std.math.round(y), std.math.round(z));
214         else
215             return this;
216     }
217 
218     /// Adds x, y and z.
219     T sum() const {
220         return x + y + z;
221     }
222 
223     /// The total length of this vector.
224     T length() const {
225         static if (__traits(isFloating, T))
226             return std.math.sqrt(x * x + y * y + z * z);
227         else
228             return cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z));
229     }
230 
231     /// The squared length of this vector.
232     /// Can be null, and more efficiant than length.
233     T lengthSquared() const {
234         return x * x + y * y + z * z;
235     }
236 
237     /// Transform this vector in a unit vector.
238     void normalize() {
239         if (this == Vec3!T.zero)
240             return;
241         static if (__traits(isFloating, T))
242             const T len = std.math.sqrt(x * x + y * y + z * z);
243         else
244             const T len = cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z));
245 
246         x /= len;
247         y /= len;
248         z /= len;
249     }
250 
251     /// Returns a unit vector from this one without modifying this one.
252     Vec3!T normalized() const {
253         if (this == Vec3!T.zero)
254             return this;
255         static if (__traits(isFloating, T))
256             const T len = std.math.sqrt(x * x + y * y + z * z);
257         else
258             const T len = cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z));
259 
260         return Vec3!T(x / len, y / len, z / len);
261     }
262 
263     /// Bounds the vector between those two.
264     Vec3!T clamp(const Vec3!T min, const Vec3!T max) const {
265         Vec3!T v = this;
266         if (v.x < min.x)
267             v.x = min.x;
268         else if (v.x > max.x)
269             v.x = max.x;
270         if (v.y < min.y)
271             v.y = min.y;
272         else if (v.y > max.y)
273             v.y = max.y;
274         if (v.z < min.z)
275             v.z = min.z;
276         else if (v.z > max.z)
277             v.z = max.z;
278         return v;
279     }
280 
281     /// Is this vector between those boundaries ?
282     bool isBetween(const Vec3!T min, const Vec3!T max) const {
283         if (x < min.x)
284             return false;
285         else if (x > max.x)
286             return false;
287         if (y < min.y)
288             return false;
289         else if (y > max.y)
290             return false;
291         if (z < min.z)
292             return false;
293         else if (z > max.z)
294             return false;
295         return true;
296     }
297 
298     static if (__traits(isFloating, T)) {
299         /// Returns an interpolated vector from this vector to the end vector by a factor. \
300         /// Does not modify this vector.
301         Vec3!T lerp(Vec3!T end, float t) const {
302             return (this * (1.0 - t)) + (end * t);
303         }
304     }
305 
306     /// Operators.
307     bool opEquals(const Vec3!T v) const {
308         return (x == v.x) && (y == v.y) && (z == v.z);
309     }
310 
311     /// Ditto
312     Vec3!T opUnary(string op)() const {
313         return mixin("Vec3!T(" ~ op ~ " x, " ~ op ~ " y, " ~ op ~ " z)");
314     }
315 
316     /// Ditto
317     Vec3!T opBinary(string op)(const Vec3!T v) const {
318         return mixin("Vec3!T(x " ~ op ~ " v.x, y " ~ op ~ " v.y, z " ~ op ~ " v.z)");
319     }
320 
321     /// Ditto
322     Vec3!T opBinary(string op)(T s) const {
323         return mixin("Vec3!T(x " ~ op ~ " s, y " ~ op ~ " s, z " ~ op ~ " s)");
324     }
325 
326     /// Ditto
327     Vec3!T opBinaryRight(string op)(T s) const {
328         return mixin("Vec3!T(s " ~ op ~ " x, s " ~ op ~ " y, s " ~ op ~ " z)");
329     }
330 
331     /// Ditto
332     Vec3!T opOpAssign(string op)(Vec3!T v) {
333         mixin("x = x" ~ op ~ "v.x;y = y" ~ op ~ "v.y;z = z" ~ op ~ "v.z;");
334         return this;
335     }
336 
337     /// Ditto
338     Vec3!T opOpAssign(string op)(T s) {
339         mixin("x = x" ~ op ~ "s;y = y" ~ op ~ "s;z = z" ~ op ~ "s;");
340         return this;
341     }
342 
343     /// Ditto
344     Vec3!U opCast(V : Vec3!U, U)() const {
345         return V(cast(U) x, cast(U) y, cast(U) z);
346     }
347 }
348 
349 alias Vec3f = Vec3!(float);
350 alias Vec3i = Vec3!(int);
351 alias Vec3u = Vec3!(uint);