1 /**
2 Grimoire
3 Copyright (c) 2017 Enalye
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
7 from 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
11 it freely, subject to the following restrictions:
12 
13 	1. The origin of this software must not be misrepresented;
14 	   you must not claim that you wrote the original software.
15 	   If you use this software in a product, an acknowledgment
16 	   in the product documentation would be appreciated but
17 	   is not required.
18 
19 	2. Altered source versions must be plainly marked as such,
20 	   and must not be misrepresented as being the original software.
21 
22 	3. This notice may not be removed or altered from any source distribution.
23 */
24 
25 module core.vec2;
26 
27 import std.math;
28 
29 import core.vec3;
30 import core.vec4;
31 
32 enum double degToRad = std.math.PI / 180.0;
33 enum double radToDeg = 180.0 / std.math.PI;
34 
35 struct Vec2(T) {
36 	static assert(__traits(isArithmetic, T));
37 
38 	static if(__traits(isUnsigned, T)) {
39 		enum one = Vec2!T(1u, 1u);
40 		enum zero = Vec2!T(0u, 0u);
41 	}
42 	else {
43 		static if(__traits(isFloating, T)) {
44 			enum one = Vec2!T(1f, 1f);
45 			enum half = Vec2!T(.5f, .5f);
46 			enum zero = Vec2!T(0f, 0f);
47 		}
48 		else {
49 			enum one = Vec2!T(1, 1);
50 			enum zero = Vec2!T(0, 0);
51 		}
52 	}
53 
54 	T x, y;
55 
56 	void set(T nx, T ny) {
57 		x = nx;
58 		y = ny;
59 	}
60 
61 	T distance(Vec2!T v) {
62 		static if(__traits(isUnsigned, T))
63 			alias V = int;
64 		else
65 			alias V = T;
66 
67 		V px = x - v.x,
68 		  py = y - v.y;
69 
70 		static if(__traits(isFloating, T))
71 			return std.math.sqrt(px * px + py * py);
72 		else
73 			return cast(T)std.math.sqrt(cast(float)(px * px + py * py));
74 	}
75 
76 	T distanceSquared(Vec2!T v) const {
77 		static if(__traits(isUnsigned, T))
78 			alias V = int;
79 		else
80 			alias V = T;
81 
82 		V px = x - v.x,
83 		  py = y - v.y;
84 
85 		return px * px + py * py;
86 	}
87 
88 	T dot(const Vec2!T v) const {
89 		return x * v.x + y * v.y;
90 	}
91 
92 	T cross(const Vec2!T v) const {
93 		static if(__traits(isUnsigned, T))
94 			return cast(int)(x * v.y) - cast(int)(y * v.x);
95 		else
96 			return (x * v.y) - (y * v.x);
97 	}
98 
99 	Vec2!T normal() const {
100 		return Vec2!T(-y, x);
101 	}
102 
103 	Vec2!T reflect(const Vec2!T v) const {
104 		static if(__traits(isFloating, T)) {
105 			T dotNI2 = 2.0 * x * v.x + y * v.y;
106 			return Vec2!T(cast(T)(x - dotNI2 * v.x), cast(T)(y - dotNI2 * v.y));
107 		}
108 		else {
109 			T dotNI2 = 2 * x * v.x + y * v.y;
110 			static if(__traits(isUnsigned, T))
111 				return Vec2!T(cast(int)(x) - cast(int)(dotNI2 * v.x), cast(int)(y) - cast(int)(dotNI2 * v.y));
112 			else
113 				return Vec2!T(x - dotNI2 * v.x, y - dotNI2 * v.y);
114 		}
115 	}
116 
117 	Vec2!T refract(const Vec2!T v, float eta) const {
118 		static if(__traits(isFloating, T)) {
119 			T dotNI = (x * v.x + y * v.y);
120 			T k = 1.0 - eta * eta * (1.0 - dotNI * dotNI);
121 			if (k < .0)
122 				return Vec2!T(T.init, T.init);
123 			else {
124 				double s = (eta * dotNI + sqrt(k));
125 				return Vec2!T(eta * x - s * v.x, eta * y - s * v.y);
126 			}
127 		}
128 		else {
129 			float dotNI = cast(float)(x * v.x + y * v.y);
130 			float k = 1.0f - eta * eta * (1.0f - dotNI * dotNI);
131 			if (k < 0f)
132 				return Vec2!T(T.init, T.init);
133 			else {
134 				float s = (eta * dotNI + sqrt(k));
135 				return Vec2!T(cast(T)(eta * x - s * v.x), cast(T)(eta * y - s * v.y));
136 			}
137 		}
138 	}
139 
140 	Vec2!T abs() const {
141 		static if(__traits(isFloating, T))
142 			return Vec2!T(x < .0 ? -x : x, y < .0 ? -y : y);
143 		else static if(__traits(isUnsigned, T))
144 			return Vec2!T(x < 0U ? -x : x, y < 0U ? -y : y);
145 		else
146 			return Vec2!T(x, y);
147 	}
148 
149 	Vec2!T floor() const {
150 		static if(__traits(isFloating, T))
151 			return Vec2!T(std.math.floor(x), std.math.floor(y));
152 		else
153 			return this;
154 	}
155 
156 	Vec2!T ceil() const {
157 		static if(__traits(isFloating, T))
158 			return Vec2!T(std.math.ceil(x), std.math.ceil(y));
159 		else
160 			return this;
161 	}
162 
163 	Vec2!T round() const {
164 		static if(__traits(isFloating, T))
165 			return Vec2!T(std.math.round(x), std.math.round(y));
166 		else
167 			return this;
168 	}
169 
170 	static if(__traits(isFloating, T)) {
171 		T angle() const {
172 			return std.math.atan2(y, x) * radToDeg;
173 		}
174 	
175 		Vec2!T rotate(T angle) {
176 			T radians = angle * degToRad;
177 			T px = x, py = y;
178 			T c = std.math.cos(radians);
179 			T s = std.math.sin(radians);
180 			x = px * c - py * s;
181 			y = px * s + py * c;
182 			return this;
183 		}
184 
185 		Vec2!T rotated(T angle) const {
186 			T radians = angle * degToRad;
187 			T c = std.math.cos(radians);
188 			T s = std.math.sin(radians);
189 			return Vec2f(x * c - y * s, x * s + y * c);
190 		}
191 
192 		static Vec2!T angled(T angle) {
193 			T radians = angle * degToRad;
194 			return Vec2f(std.math.cos(radians), std.math.sin(radians));
195 		}
196 	}
197 
198 	T sum() const {
199 		return x + y;
200 	}
201 
202 	T length() const {
203 		static if(__traits(isFloating, T))
204 			return std.math.sqrt(x * x + y * y);
205 		else
206 			return cast(T)std.math.sqrt(cast(float)(x * x + y * y));
207 	}
208 
209 	T lengthSquared() const  {
210 		return x * x + y * y;
211 	}
212 
213 	void normalize() {
214 		static if(__traits(isFloating, T))
215 			T len = std.math.sqrt(x * x + y * y);
216 		else
217 			T len = cast(T)std.math.sqrt(cast(float)(x * x + y * y));
218 
219 		x /= len;
220 		y /= len;
221 	}
222 
223 	Vec2!T normalized() const  {
224 		static if(__traits(isFloating, T))
225 			T len = std.math.sqrt(x * x + y * y);
226 		else
227 			T len = cast(T)std.math.sqrt(cast(float)(x * x + y * y));
228 
229 		return Vec2!T(x / len, y / len);
230 	}
231 
232 	Vec2!T clamp(const Vec2!T min, const Vec2!T max) const {
233 		Vec2!T v = {x, y};
234 		if (v.x < min.x) v.x = min.x;
235 		else if(v.x > max.x) v.x = max.x;
236 		if (v.y < min.y) v.y = min.y;
237 		else if (v.y > max.y) v.y = max.y;
238 		return v;
239 	}
240 
241 	Vec2!T clamp(const Vec4!T clip) const {
242 		Vec2!T v = {x, y};
243 		if (v.x < clip.x) v.x = clip.x;
244 		else if(v.x > clip.z) v.x = clip.z;
245 		if (v.y < clip.y) v.y = clip.y;
246 		else if (v.y > clip.w) v.y = clip.w;
247 		return v;
248 	}
249 
250 	bool isBetween(const Vec2!T min, const Vec2!T max) const {
251 		if (x < min.x) return false;
252 		else if (x > max.x) return false;
253 		if (y < min.y) return false;
254 		else if (y > max.y) return false;
255 		return true;
256 	}
257 
258 	static if(__traits(isFloating, T)) {
259 		Vec2!T lerp(Vec2!T end, float t) const {
260 			return (this * (1.0 - t)) + (end * t);
261 		}
262 
263 		Vec2!T fit(const Vec2!T v) const {
264 			Vec2!T u = {v.x / x, v.y / y};
265 			if(u.x < u.y)
266 				return Vec2!T(v.x, v.x);
267 			else
268 				return Vec2!T(v.y, v.y);
269 		}
270 	}
271 
272 	bool opEquals(const Vec2!T v) const {
273 		return (x == v.x) && (y == v.y);
274 	}
275 
276 	Vec2!T opUnary(string op)() const {
277 		return mixin("Vec2!T(" ~ op ~ " x, " ~ op ~ " y)");
278 	}
279 
280 	Vec2!T opBinary(string op)(const Vec2!T v) const {
281 		return mixin("Vec2!T(x " ~ op ~ " v.x, y " ~ op ~ " v.y)");
282 	}
283 
284 	Vec2!T opBinary(string op)(T s) const {
285 		return mixin("Vec2!T(x " ~ op ~ " s, y " ~ op ~ " s)");
286 	}
287 
288 	Vec2!T opBinaryRight(string op)(T s) const {
289 		return mixin("Vec2!T(s " ~ op ~ " x, s " ~ op ~ " y)");
290 	}
291 
292 	Vec2!T opOpAssign(string op)(Vec2!T v) {
293 		mixin("x = x" ~ op ~ "v.x;y = y" ~ op ~ "v.y;");
294 		return this;
295 	}
296 
297 	Vec2!T opOpAssign(string op)(T s) {
298 		mixin("x = x" ~ op ~ "s;y = y" ~ op ~ "s;");
299 		return this;
300 	}
301 
302 	Vec2!U opCast(V: Vec2!U, U)() const {
303 		return V(cast(U)x, cast(U)y);
304 	}
305 }
306 
307 alias Vec2f = Vec2!(float);
308 alias Vec2i = Vec2!(int);
309 alias Vec2u = Vec2!(uint);