Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #ifndef EZ_TOOLS_VEC2
4 : : #define EZ_TOOLS_VEC2
5 : : #endif // EZ_TOOLS_VEC2
6 : :
7 : : /*
8 : : MIT License
9 : :
10 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
11 : :
12 : : Permission is hereby granted, free of charge, to any person obtaining a copy
13 : : of this software and associated documentation files (the "Software"), to deal
14 : : in the Software without restriction, including without limitation the rights
15 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 : : copies of the Software, and to permit persons to whom the Software is
17 : : furnished to do so, subject to the following conditions:
18 : :
19 : : The above copyright notice and this permission notice shall be included in all
20 : : copies or substantial portions of the Software.
21 : :
22 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 : : SOFTWARE.
29 : : */
30 : :
31 : : // ezVec2 is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
32 : :
33 : : #include <cmath>
34 : : #include <type_traits>
35 : : #include <string>
36 : : #include <vector>
37 : :
38 : : #ifdef min
39 : : #undef min
40 : : #endif // min
41 : :
42 : : #ifdef max
43 : : #undef max
44 : : #endif // max
45 : :
46 : : // Namespace ez
47 : : namespace ez {
48 : :
49 : : // Disable specific types for template functions
50 : : template <typename T>
51 : : struct is_valid_type : std::integral_constant<bool, (std::is_floating_point<T>::value || (std::is_integral<T>::value && sizeof(T) > 1))> {};
52 : :
53 : : // Vector 2D template class
54 : : template <typename T>
55 : : struct vec2 {
56 : : // Disable this template if T is not an integral or floating point type
57 : : //static_assert(is_valid_type<T>::value, "Invalid type for vec2. Only integral types larger than (u)int8_t and floating-point types are allowed.");
58 : :
59 : : T x = static_cast<T>(0), y = static_cast<T>(0);
60 : :
61 : : // Constructors
62 : 9 : vec2() = default;
63 : :
64 : : // Constructor with type conversion
65 : : template <typename U>
66 : : vec2(const vec2<U>& a) : x(static_cast<T>(a.x)), y(static_cast<T>(a.y)) {}
67 : :
68 : : // Constructor with type conversion
69 : : template <typename U>
70 : : vec2(const U& a) : x(static_cast<T>(a.x)), y(static_cast<T>(a.y)) {}
71 : :
72 : : vec2(T a) : x(a), y(a) {}
73 : :
74 : 809 : vec2(T a, T b) : x(a), y(b) {}
75 : :
76 : : #ifdef EZ_STR
77 : : vec2(const std::vector<std::string>& vArray) {
78 : : const size_t s = vArray.size();
79 : : if (s > 0) {
80 : : str::stringToNumber(vArray.at(0), x);
81 : : }
82 : : if (s > 1) {
83 : : str::stringToNumber(vArray.at(1), y);
84 : : }
85 : : };
86 : :
87 : : vec2(const std::string& vec, char c = ';', const vec2<T>* def = nullptr) {
88 : : if (def) {
89 : : x = def->x;
90 : : y = def->y;
91 : : }
92 : : std::vector<T> result = str::stringToNumberVector<T>(vec, c);
93 : : const size_t s = result.size();
94 : : if (s > 0) {
95 : : x = result.at(0);
96 : : }
97 : : if (s > 1) {
98 : : x = result.at(1);
99 : : }
100 : : }
101 : : #endif
102 : :
103 : : // Element access operator
104 : : T& operator[](size_t i) { return (&x)[i]; }
105 : :
106 : : // Offset function
107 : 6 : vec2 Offset(T vX, T vY) const { return vec2(x + vX, y + vY); }
108 : :
109 : : // Set function
110 : : void Set(T vX, T vY) {
111 : : x = vX;
112 : : y = vY;
113 : : }
114 : :
115 : : vec2 lerp(const vec2<T>& vPos, T vLerpValue) {
116 : : static_assert(std::is_floating_point<T>::value, "lerp is only valid for floating point types");
117 : : return vec2( //
118 : : ez::lerp(x, vPos.x, vLerpValue),
119 : : ez::lerp(y, vPos.y, vLerpValue));
120 : :
121 : : }
122 : :
123 : : vec2 lerp(const vec2<T>& vPos, const vec2<T>& vLerpValue) {
124 : : static_assert(std::is_floating_point<T>::value, "lerp is only valid for floating point types");
125 : : return vec2( //
126 : : ez::lerp(x, vPos.x, vLerpValue.x),
127 : : ez::lerp(y, vPos.y, vLerpValue.y));
128 : : }
129 : :
130 : : // Negation operator
131 : 4 : vec2 operator-() const {
132 : 4 : static_assert(std::is_signed<T>::value, "Negation operator is only valid for signed types");
133 : 4 : return vec2(-x, -y);
134 : 4 : }
135 : :
136 : : // Logical NOT operator
137 : : vec2 operator!() const {
138 : : static_assert(std::is_integral<T>::value, "Logical NOT is only valid for integral types");
139 : : return vec2(!x, !y);
140 : : }
141 : :
142 : : // Increment and decrement operators
143 : : vec2& operator++() {
144 : : ++x;
145 : : ++y;
146 : : return *this;
147 : : }
148 : :
149 : : vec2& operator--() {
150 : : --x;
151 : : --y;
152 : : return *this;
153 : : }
154 : :
155 : : vec2 operator++(int) {
156 : : vec2 tmp = *this;
157 : : ++*this;
158 : : return tmp;
159 : : }
160 : :
161 : : vec2 operator--(int) {
162 : : vec2 tmp = *this;
163 : : --*this;
164 : : return tmp;
165 : : }
166 : :
167 : : // Compound assignment operators
168 : : void operator+=(T a) {
169 : : x += a;
170 : : y += a;
171 : : }
172 : :
173 : : void operator+=(const vec2<T>& v) {
174 : : x += v.x;
175 : : y += v.y;
176 : : }
177 : :
178 : : void operator-=(T a) {
179 : : x -= a;
180 : : y -= a;
181 : : }
182 : :
183 : : void operator-=(const vec2<T>& v) {
184 : : x -= v.x;
185 : : y -= v.y;
186 : : }
187 : :
188 : : void operator*=(T a) {
189 : : x *= a;
190 : : y *= a;
191 : : }
192 : :
193 : 0 : void operator*=(const vec2<T>& v) {
194 : 0 : x *= v.x;
195 : 0 : y *= v.y;
196 : 0 : }
197 : :
198 : : void operator/=(T a) {
199 : : x /= a;
200 : : y /= a;
201 : : }
202 : :
203 : : void operator/=(const vec2<T>& v) {
204 : : x /= v.x;
205 : : y /= v.y;
206 : : }
207 : :
208 : : // Comparison operators
209 : : bool operator==(T a) const { return (x == a) && (y == a); }
210 : :
211 : : bool operator==(const vec2<T>& v) const { return (x == v.x) && (y == v.y); }
212 : :
213 : : bool operator!=(T a) const { return (x != a) || (y != a); }
214 : :
215 : : bool operator!=(const vec2<T>& v) const { return (x != v.x) || (y != v.y); }
216 : :
217 : : // Length and normalization
218 : 10 : T lengthSquared() const { return x * x + y * y; }
219 : :
220 : 10 : T length() const { return static_cast<T>(ez::sqrt(lengthSquared())); }
221 : :
222 : 4 : T normalize() {
223 : 4 : T len = length();
224 [ - + ][ - + ]: 4 : if (len < static_cast<T>(1e-5))
225 : 0 : return static_cast<T>(0.0);
226 : 4 : T invLen = static_cast<T>(1.0) / len;
227 : 4 : x *= invLen;
228 : 4 : y *= invLen;
229 : 4 : return len;
230 : 4 : }
231 : :
232 : 2 : vec2 GetNormalized() const {
233 : 2 : vec2 n(x, y);
234 : 2 : n.normalize();
235 : 2 : return n;
236 : 2 : }
237 : :
238 : : // Sum functions
239 : 6 : T sum() const { return x + y; }
240 : :
241 : : T sumAbs() const { return ez::abs(x) + ez::abs(y); }
242 : :
243 : : // Empty checks
244 [ + + ][ + + ]: 24 : bool emptyAND() const { return x == static_cast<T>(0) && y == static_cast<T>(0); }
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
245 : :
246 [ + + ][ + + ]: 24 : bool emptyOR() const { return x == static_cast<T>(0) || y == static_cast<T>(0); }
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
247 : :
248 : : #ifdef EZ_STR
249 : : // Convert to string
250 : : std::string string(char c = ';') const { return str::toStr(x) + c + str::toStr(y); }
251 : : std::vector<std::string> array(char c = ';') const { return {str::toStr(x), str::toStr(y)}; }
252 : : #endif
253 : :
254 : : // Ratio functions
255 : : template <typename U>
256 : : U ratioXY() const {
257 : : if (y != static_cast<T>(0))
258 : : return static_cast<U>(x) / static_cast<U>(y);
259 : : return static_cast<U>(0);
260 : : }
261 : :
262 : : template <typename U>
263 : : U ratioYX() const {
264 : : if (x != static_cast<T>(0))
265 : : return static_cast<U>(y) / static_cast<U>(x);
266 : : return static_cast<U>(0);
267 : : }
268 : :
269 : : // Min and max
270 [ + - ][ + - ]: 6 : T min() const { return x < y ? x : y; }
[ + - ][ + - ]
[ + - ][ + - ]
271 : :
272 [ - + ][ - + ]: 6 : T max() const { return x > y ? x : y; }
[ - + ][ - + ]
[ - + ][ - + ]
273 : : };
274 : :
275 : : // Operators
276 : : template <typename T>
277 : 6 : inline vec2<T> operator+(const vec2<T>& v, T f) {
278 : 6 : return vec2<T>(v.x + f, v.y + f);
279 : 6 : }
280 : :
281 : : template <typename T>
282 : 6 : inline vec2<T> operator+(T f, const vec2<T>& v) {
283 : 6 : return vec2<T>(v.x + f, v.y + f);
284 : 6 : }
285 : :
286 : : template <typename T>
287 : 6 : inline vec2<T> operator+(const vec2<T>& v, const vec2<T>& f) {
288 : 6 : return vec2<T>(v.x + f.x, v.y + f.y);
289 : 6 : }
290 : :
291 : : template <typename T>
292 : 6 : inline vec2<T> operator-(const vec2<T>& v, T f) {
293 : 6 : return vec2<T>(v.x - f, v.y - f);
294 : 6 : }
295 : :
296 : : template <typename T>
297 : 6 : inline vec2<T> operator-(T f, const vec2<T>& v) {
298 : 6 : return vec2<T>(f - v.x, f - v.y);
299 : 6 : }
300 : :
301 : : template <typename T>
302 : 8 : inline vec2<T> operator-(const vec2<T>& v, const vec2<T>& f) {
303 : 8 : return vec2<T>(v.x - f.x, v.y - f.y);
304 : 8 : }
305 : :
306 : : template <typename T>
307 : 6 : inline vec2<T> operator*(const vec2<T>& v, T f) {
308 : 6 : return vec2<T>(v.x * f, v.y * f);
309 : 6 : }
310 : :
311 : : template <typename T>
312 : 8 : inline vec2<T> operator*(T f, const vec2<T>& v) {
313 : 8 : return vec2<T>(v.x * f, v.y * f);
314 : 8 : }
315 : :
316 : : template <typename T>
317 : 6 : inline vec2<T> operator*(const vec2<T>& v, const vec2<T>& f) {
318 : 6 : return vec2<T>(v.x * f.x, v.y * f.y);
319 : 6 : }
320 : :
321 : : template <typename T>
322 : 4 : inline vec2<T> operator/(const vec2<T>& v, T f) {
323 : 4 : return vec2<T>(v.x / f, v.y / f);
324 : 4 : }
325 : :
326 : : template <typename T>
327 : 2 : inline vec2<T> operator/(T f, const vec2<T>& v) {
328 : 2 : return vec2<T>(f / v.x, f / v.y);
329 : 2 : }
330 : :
331 : : template <typename T>
332 : 4 : inline vec2<T> operator/(const vec2<T>& v, const vec2<T>& f) {
333 : 4 : return vec2<T>(v.x / f.x, v.y / f.y);
334 : 4 : }
335 : :
336 : : // Comparison operators
337 : : template <typename T>
338 : : inline bool operator<(const vec2<T>& v, const vec2<T>& f) {
339 : : return v.x < f.x && v.y < f.y;
340 : : }
341 : :
342 : : template <typename T>
343 : : inline bool operator<(const vec2<T>& v, T f) {
344 : : return v.x < f && v.y < f;
345 : : }
346 : :
347 : : template <typename T>
348 : : inline bool operator>(const vec2<T>& v, const vec2<T>& f) {
349 : : return v.x > f.x && v.y > f.y;
350 : : }
351 : :
352 : : template <typename T>
353 : : inline bool operator>(const vec2<T>& v, T f) {
354 : : return v.x > f && v.y > f;
355 : : }
356 : :
357 : : template <typename T>
358 : : inline bool operator<=(const vec2<T>& v, const vec2<T>& f) {
359 : : return v.x <= f.x && v.y <= f.y;
360 : : }
361 : :
362 : : template <typename T>
363 : : inline bool operator<=(const vec2<T>& v, T f) {
364 : : return v.x <= f && v.y <= f;
365 : : }
366 : :
367 : : template <typename T>
368 : : inline bool operator>=(const vec2<T>& v, const vec2<T>& f) {
369 : : return v.x >= f.x && v.y >= f.y;
370 : : }
371 : :
372 : : template <typename T>
373 : : inline bool operator>=(const vec2<T>& v, T f) {
374 : : return v.x >= f && v.y >= f;
375 : : }
376 : :
377 : : template <typename T>
378 : : inline bool operator!=(const vec2<T>& v, const vec2<T>& f) {
379 : : return f.x != v.x || f.y != v.y;
380 : : }
381 : :
382 : : template <typename T>
383 : : inline bool operator==(const vec2<T>& v, const vec2<T>& f) {
384 : : return f.x == v.x && f.y == v.y;
385 : : }
386 : :
387 : : // Additional vector operations
388 : : template <typename T>
389 : 2 : inline vec2<T> floor(const vec2<T>& a) {
390 : 2 : return vec2<T>(ez::floor(a.x), ez::floor(a.y));
391 : 2 : }
392 : :
393 : : template <typename T>
394 : 2 : inline vec2<T> fract(const vec2<T>& a) {
395 : 2 : static_assert(std::is_floating_point<T>::value, "fract is only valid for theses types : float, double, long double");
396 : 2 : return vec2<T>(fract(a.x), fract(a.y));
397 : 2 : }
398 : :
399 : : template <typename T>
400 : 2 : inline vec2<T> ceil(const vec2<T>& a) {
401 : 2 : return vec2<T>(ez::ceil(a.x), ez::ceil(a.y));
402 : 2 : }
403 : :
404 : : template <typename T>
405 : 6 : inline vec2<T> mini(const vec2<T>& a, const vec2<T>& b) {
406 : 6 : return vec2<T>(ez::mini(a.x, b.x), ez::mini(a.y, b.y));
407 : 6 : }
408 : :
409 : : template <typename T>
410 : 6 : inline vec2<T> maxi(const vec2<T>& a, const vec2<T>& b) {
411 : 6 : return vec2<T>(ez::maxi(a.x, b.x), ez::maxi(a.y, b.y));
412 : 6 : }
413 : :
414 : : // scalar prodcut not possible with (u)int8
415 : : template <typename T>
416 : 8 : inline T dot(const vec2<T>& a, const vec2<T>& b) {
417 : 8 : return a.x * b.x + a.y * b.y;
418 : 8 : }
419 : :
420 : : template <typename T>
421 : 6 : inline T det(const vec2<T>& a, const vec2<T>& b) {
422 : 6 : return a.x * b.y - a.y * b.x;
423 : 6 : }
424 : :
425 : : template <typename T>
426 : : inline T length(const vec2<T>& v) {
427 : : return sqrt(dot(v,v));
428 : : }
429 : :
430 : : template <typename T>
431 : : inline vec2<T> cross(const vec2<T>& a, const vec2<T>& b) {
432 : : return vec2<T>(a.x * b.y - a.y * b.x, a.y * b.x - a.x * b.y);
433 : : }
434 : :
435 : : template <typename T>
436 : 2 : inline vec2<T> reflect(const vec2<T>& I, const vec2<T>& N) {
437 : 2 : static_assert(std::is_floating_point<T>::value, "fract is only valid for floating point types");
438 : 2 : return I - static_cast<T>(2) * ez::dot(N, I) * N;
439 : 2 : }
440 : :
441 : : template <typename T>
442 : 8 : inline vec2<T> sign(const vec2<T>& a) {
443 : 8 : return vec2<T>(ez::sign(a.x), ez::sign(a.y));
444 : 8 : }
445 : :
446 : : template <typename T>
447 : 2 : inline vec2<T> sin(const vec2<T>& a) {
448 : 2 : return vec2<T>(ez::sin(a.x), ez::sin(a.y));
449 : 2 : }
450 : :
451 : : template <typename T>
452 : 2 : inline vec2<T> cos(const vec2<T>& a) {
453 : 2 : return vec2<T>(ez::cos(a.x), ez::cos(a.y));
454 : 2 : }
455 : :
456 : : template <typename T>
457 : 2 : inline vec2<T> tan(const vec2<T>& a) {
458 : 2 : return vec2<T>(ez::tan(a.x), ez::tan(a.y));
459 : 2 : }
460 : :
461 : : template <typename T>
462 : 2 : inline vec2<T> atan(const vec2<T>& a) {
463 : 2 : return vec2<T>(ez::atan(a.x), ez::atan(a.y));
464 : 2 : }
465 : :
466 : : // Clamps a value between 0 and 1.
467 : : // Works with both integral and floating-point types.
468 : : template <typename T>
469 : : inline vec2<T> clamp(vec2<T> n) {
470 : : vec2<T> ret;
471 : : ret.x = ez::clamp(n.x);
472 : : ret.y = ez::clamp(n.y);
473 : : return ret;
474 : : }
475 : :
476 : : // Clamps a value between 0 and b.
477 : : // Works with both integral and floating-point types.
478 : : template <typename T>
479 : : inline vec2<T> clamp(vec2<T> n, T b) {
480 : : vec2<T> ret;
481 : : ret.x = ez::clamp(n.x);
482 : : ret.y = ez::clamp(n.y);
483 : : return ret;
484 : : }
485 : :
486 : : // Clamps a value between a and b.
487 : : // Works with both integral and floating-point types.
488 : : template <typename T>
489 : : inline vec2<T> clamp(vec2<T> n, T a, T b) {
490 : : vec2<T> ret;
491 : : ret.x = ez::clamp(n.x, a, b);
492 : : ret.y = ez::clamp(n.y, a, b);
493 : : return ret;
494 : : }
495 : :
496 : : // Using statements for different types of vec2
497 : : using dvec2 = vec2<double>;
498 : : using fvec2 = vec2<float>;
499 : : using f32vec2 = vec2<float>;
500 : : using f64vec2 = vec2<double>;
501 : : using i8vec2 = vec2<int8_t>;
502 : : using i16vec2 = vec2<int16_t>;
503 : : using ivec2 = vec2<int32_t>;
504 : : using i32vec2 = vec2<int32_t>;
505 : : using i64vec2 = vec2<int64_t>;
506 : : using u8vec2 = vec2<uint8_t>;
507 : : using u16vec2 = vec2<uint16_t>;
508 : : using uvec2 = vec2<uint32_t>;
509 : : using u32vec2 = vec2<uint32_t>;
510 : : using u64vec2 = vec2<uint64_t>;
511 : :
512 : : // Conversion functions
513 : 0 : inline fvec2 convert(const ivec2& v) {
514 : 0 : return fvec2(static_cast<float>(v.x), static_cast<float>(v.y));
515 : 0 : }
516 : :
517 : 0 : inline ivec2 convert(const fvec2& v) {
518 : 0 : return ivec2(static_cast<int>(v.x), static_cast<int>(v.y));
519 : 0 : }
520 : :
521 : : #ifdef floatIsValid
522 : : // Float validation
523 : : inline bool valid(const fvec2& a) {
524 : : return floatIsValid(a.x) && floatIsValid(a.y);
525 : : }
526 : : #endif
527 : :
528 : : #ifdef isEqual
529 : : // Float comparison operators
530 : : inline bool operator==(const fvec2& v, const fvec2& f) {
531 : : return isEqual(f.x, v.x) && isEqual(f.y, v.y);
532 : : }
533 : : #endif
534 : :
535 : : #ifdef isDifferent
536 : : inline bool operator!=(const fvec2& v, const fvec2& f) {
537 : : return isDifferent(f.x, v.x) || isDifferent(f.y, v.y);
538 : : }
539 : : #endif
540 : :
541 : : // Function to compute the angle in radians from a vec2, only for floating-point types
542 : : // Allowed types: float, double, long double
543 : : template <typename T>
544 : : inline typename std::enable_if<std::is_floating_point<T>::value, T>::type radAngleFromVec2(const vec2<T>& vec) {
545 : : T angle = static_cast<T>(0);
546 : : if (vec.lengthSquared() > static_cast<T>(0) && vec.x != static_cast<T>(0)) {
547 : : angle = ez::atan(vec.y / vec.x);
548 : : }
549 : : return angle;
550 : : }
551 : :
552 : : // Function to compute the continuous angle in radians from a vec2 with an offset, only for floating-point types
553 : : // Allowed types: float, double, long double
554 : : template <typename T>
555 : : inline typename std::enable_if<std::is_floating_point<T>::value, T>::type radAngleContinuousFromVec2(const vec2<T>& vec, T angleOffset) {
556 : : T angle = static_cast<T>(0);
557 : : if (vec.x > static_cast<T>(0)) {
558 : : angle = ez::atan(vec.y / vec.x);
559 : : } else if (vec.x < static_cast<T>(0)) {
560 : : angle = static_cast<T>(M_PI) - ez::atan(-vec.y / vec.x);
561 : : }
562 : : return angle - angleOffset;
563 : : }
564 : :
565 : : template <typename T>
566 : : inline std::istream& operator>>(std::istream& vIn, vec2<T>& vType) {
567 : : char separator;
568 : : if (vIn >> vType.x >> separator >> vType.y) {
569 : : if (separator != ';') {
570 : : vIn.setstate(std::ios::failbit);
571 : : }
572 : : }
573 : : return vIn;
574 : : }
575 : :
576 : : template <typename T>
577 : : inline std::ostream& operator<<(std::ostream& vOut, const vec2<T>& vType) {
578 : : vOut << vType.x << ";" << vType.y;
579 : : return vOut;
580 : : }
581 : :
582 : : } // namespace ez
|