Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #ifndef EZ_TOOLS_VEC3
4 : : #define EZ_TOOLS_VEC3
5 : : #endif // EZ_TOOLS_VEC3
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 : : // ezVec3 is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
32 : :
33 : : #include <type_traits>
34 : : #include <cmath>
35 : : #include <string>
36 : : #include <vector>
37 : :
38 : : namespace ez {
39 : :
40 : : // Restrict the template to relevant types only (e.g., disable bool)
41 : : template <typename T>
42 : : struct vec3 {
43 : : static_assert(!std::is_same<T, bool>::value, "vec3 cannot be instantiated with bool type");
44 : :
45 : : T x = static_cast<T>(0), y = static_cast<T>(0), z = static_cast<T>(0);
46 : :
47 : : // Default constructor
48 : 11 : vec3() : x(static_cast<T>(0)), y(static_cast<T>(0)), z(static_cast<T>(0)) {}
49 : :
50 : : // Constructor with type conversion
51 : : template <typename U>
52 : : vec3(const vec3<U>& a) : x(static_cast<T>(a.x)), y(static_cast<T>(a.y)), z(static_cast<T>(a.z)) {}
53 : :
54 : : // Constructor with type conversion
55 : : template <typename U>
56 : : vec3(const U& a) : x(static_cast<T>(a.x)), y(static_cast<T>(a.y)), z(static_cast<T>(a.z)) {}
57 : :
58 : : // Constructor for uniform initialization
59 : 42 : vec3(T xyz) : x(xyz), y(xyz), z(xyz) {}
60 : :
61 : : // Constructor with specific values
62 : 6458030 : vec3(T x, T y, T z) : x(x), y(y), z(z) {}
63 : :
64 : : // Constructor using a vec2 and a scalar
65 : : // Assumption: vec2<T> is defined elsewhere, as it is referenced but not included here.
66 : : vec3(const vec2<T>& xy, T z) : x(xy.x), y(xy.y), z(z) {}
67 : :
68 : : // Constructor from string
69 : : vec3(const std::string& vec, char c = ';', vec3<T>* def = nullptr) {
70 : : if (def) {
71 : : x = def->x;
72 : : y = def->y;
73 : : z = def->z;
74 : : }
75 : : std::vector<T> result = str::stringToNumberVector<T>(vec, c);
76 : : const size_t s = result.size();
77 : : if (s > 0)
78 : : x = result[0];
79 : : if (s > 1)
80 : : y = result[1];
81 : : if (s > 2)
82 : : z = result[2];
83 : : }
84 : :
85 : : // Indexing operator
86 : : T& operator[](size_t i) { return (&x)[i]; }
87 : :
88 : : // Offset the vector
89 : 6 : vec3 Offset(T vX, T vY, T vZ) const { return vec3(x + vX, y + vY, z + vZ); }
90 : :
91 : : // Set the vector's components
92 : 6 : void Set(T vX, T vY, T vZ) {
93 : 6 : x = vX;
94 : 6 : y = vY;
95 : 6 : z = vZ;
96 : 6 : }
97 : :
98 : : operator vec2<T>() const { return vec2<T>(x, y); }
99 : :
100 : : // Negation operator
101 : 4 : vec3 operator-() const {
102 : 4 : static_assert(std::is_signed<T>::value, "Negate is only valid for signed types");
103 : 4 : return vec3(-x, -y, -z);
104 : 4 : }
105 : :
106 : : // Logical NOT operator, only for integral types
107 : 2 : vec3 operator!() const {
108 : 2 : static_assert(std::is_integral<T>::value, "Logical NOT is only valid for integral types");
109 : 2 : return vec3(!x, !y, !z);
110 : 2 : }
111 : :
112 : : // Extract 2D vectors from 3D vector
113 : 6 : vec2<T> xy() const { return vec2<T>(x, y); }
114 : :
115 : 6 : vec2<T> xz() const { return vec2<T>(x, z); }
116 : :
117 : 6 : vec2<T> yz() const { return vec2<T>(y, z); }
118 : :
119 : : // Cyclic permutation
120 : 6 : vec3 yzx() const { return vec3(y, z, x); }
121 : :
122 : : // Pre-increment and pre-decrement operators
123 : 6 : vec3& operator++() {
124 : 6 : ++x;
125 : 6 : ++y;
126 : 6 : ++z;
127 : 6 : return *this;
128 : 6 : }
129 : :
130 : 6 : vec3& operator--() {
131 : 6 : --x;
132 : 6 : --y;
133 : 6 : --z;
134 : 6 : return *this;
135 : 6 : }
136 : :
137 : : // Post-increment and post-decrement operators
138 : : vec3 operator++(int) {
139 : : vec3 tmp = *this;
140 : : ++*this;
141 : : return tmp;
142 : : }
143 : :
144 : : vec3 operator--(int) {
145 : : vec3 tmp = *this;
146 : : --*this;
147 : : return tmp;
148 : : }
149 : :
150 : : // Compound assignment operators
151 : : void operator+=(T a) {
152 : : x += a;
153 : : y += a;
154 : : z += a;
155 : : }
156 : :
157 : : void operator+=(const vec3& v) {
158 : : x += v.x;
159 : : y += v.y;
160 : : z += v.z;
161 : : }
162 : :
163 : : void operator-=(T a) {
164 : : x -= a;
165 : : y -= a;
166 : : z -= a;
167 : : }
168 : :
169 : : void operator-=(const vec3& v) {
170 : : x -= v.x;
171 : : y -= v.y;
172 : : z -= v.z;
173 : : }
174 : :
175 : : void operator*=(T a) {
176 : : x *= a;
177 : : y *= a;
178 : : z *= a;
179 : : }
180 : :
181 : : void operator*=(const vec3& v) {
182 : : x *= v.x;
183 : : y *= v.y;
184 : : z *= v.z;
185 : : }
186 : :
187 : : void operator/=(T a) {
188 : : x /= a;
189 : : y /= a;
190 : : z /= a;
191 : : }
192 : :
193 : : void operator/=(const vec3& v) {
194 : : x /= v.x;
195 : : y /= v.y;
196 : : z /= v.z;
197 : : }
198 : :
199 : : // Length of the vector
200 : 6 : T length() const { return static_cast<T>(ez::sqrt(lengthSquared())); }
201 : :
202 : : // Squared length of the vector
203 : 6 : T lengthSquared() const { return x * x + y * y + z * z; }
204 : :
205 : : // Normalize the vector
206 : 2 : T normalize() {
207 : 2 : T _length = length();
208 [ - + ][ - + ]: 2 : if (_length < std::numeric_limits<T>::epsilon())
209 : 0 : return static_cast<T>(0);
210 : 2 : T _invLength = static_cast<T>(1) / _length;
211 : 2 : x *= _invLength;
212 : 2 : y *= _invLength;
213 : 2 : z *= _invLength;
214 : 2 : return _length;
215 : 2 : }
216 : :
217 : : // Get a normalized copy of the vector
218 : : vec3 GetNormalized() const {
219 : : vec3 n(x, y, z);
220 : : n.normalize();
221 : : return n;
222 : : }
223 : :
224 : : // Sum of components
225 : 6 : T sum() const { return x + y + z; }
226 : :
227 : : // Sum of absolute values of components
228 : 4 : T sumAbs() const { return ez::abs(x) + ez::abs(y) + ez::abs(z); }
229 : :
230 : : // Check if all components are zero (AND)
231 [ + + ][ + + ]: 12 : bool emptyAND() const { return x == static_cast<T>(0) && y == static_cast<T>(0) && z == static_cast<T>(0); }
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
232 : :
233 : : // Check if any component is zero (OR)
234 [ + + ][ + + ]: 12 : bool emptyOR() const { return x == static_cast<T>(0) || y == static_cast<T>(0) || z == static_cast<T>(0); }
[ + + ][ + + ]
[ + + ][ + + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
235 : :
236 : : // Convert to string
237 : 6 : std::string string(char c = ';') const { return ez::str::toStr(x) + c + ez::str::toStr(y) + c + ez::str::toStr(z); }
238 : :
239 : : // Minimum component
240 : 6 : T mini() const { return ez::mini(x, ez::mini(y, z)); }
241 : :
242 : : // Maximum component
243 : 6 : T maxi() const { return ez::maxi(x, ez::maxi(y, z)); }
244 : : };
245 : :
246 : : // Operators for vec3
247 : : template <typename T>
248 : : inline vec3<T> operator+(const vec3<T>& v, T f) {
249 : : return vec3<T>(v.x + f, v.y + f, v.z + f);
250 : : }
251 : :
252 : : template <typename T>
253 : : inline vec3<T> operator+(T f, const vec3<T>& v) {
254 : : return vec3<T>(v.x + f, v.y + f, v.z + f);
255 : : }
256 : :
257 : : template <typename T>
258 : 47 : inline vec3<T> operator+(const vec3<T>& v, const vec3<T>& f) {
259 : 47 : return vec3<T>(v.x + f.x, v.y + f.y, v.z + f.z);
260 : 47 : }
261 : :
262 : : template <typename T>
263 : : inline vec3<T> operator-(const vec3<T>& v, T f) {
264 : : return vec3<T>(v.x - f, v.y - f, v.z - f);
265 : : }
266 : :
267 : : template <typename T>
268 : : inline vec3<T> operator-(T f, const vec3<T>& v) {
269 : : return vec3<T>(f - v.x, f - v.y, f - v.z);
270 : : }
271 : :
272 : : template <typename T>
273 : 49 : inline vec3<T> operator-(const vec3<T>& v, const vec3<T>& f) {
274 : 49 : return vec3<T>(v.x - f.x, v.y - f.y, v.z - f.z);
275 : 49 : }
276 : :
277 : : template <typename T>
278 : 6 : inline vec3<T> operator*(const vec3<T>& v, T f) {
279 : 6 : return vec3<T>(v.x * f, v.y * f, v.z * f);
280 : 6 : }
281 : :
282 : : template <typename T>
283 : : inline vec3<T> operator*(T f, const vec3<T>& v) {
284 : : return vec3<T>(v.x * f, v.y * f, v.z * f);
285 : : }
286 : :
287 : : template <typename T>
288 : : inline vec3<T> operator*(const vec3<T>& v, const vec3<T>& f) {
289 : : return vec3<T>(v.x * f.x, v.y * f.y, v.z * f.z);
290 : : }
291 : :
292 : : template <typename T>
293 : 47 : inline vec3<T> operator/(const vec3<T>& v, T f) {
294 : 47 : return vec3<T>(v.x / f, v.y / f, v.z / f);
295 : 47 : }
296 : :
297 : : template <typename T>
298 : : inline vec3<T> operator/(T f, const vec3<T>& v) {
299 : : return vec3<T>(f / v.x, f / v.y, f / v.z);
300 : : }
301 : :
302 : : template <typename T>
303 : : inline vec3<T> operator/(const vec3<T>& v, const vec3<T>& f) {
304 : : return vec3<T>(v.x / f.x, v.y / f.y, v.z / f.z);
305 : : }
306 : :
307 : : // Comparison operators
308 : : template <typename T>
309 : : inline bool operator<(const vec3<T>& v, const vec3<T>& f) {
310 : : return v.x < f.x && v.y < f.y && v.z < f.z;
311 : : }
312 : :
313 : : template <typename T>
314 : : inline bool operator<(const vec3<T>& v, T f) {
315 : : return v.x < f && v.y < f && v.z < f;
316 : : }
317 : :
318 : : template <typename T>
319 : : inline bool operator<(T f, const vec3<T>& v) {
320 : : return f < v.x && f < v.y && f < v.z;
321 : : }
322 : :
323 : : template <typename T>
324 : : inline bool operator>(const vec3<T>& v, const vec3<T>& f) {
325 : : return v.x > f.x && v.y > f.y && v.z > f.z;
326 : : }
327 : :
328 : : template <typename T>
329 : : inline bool operator>(const vec3<T>& v, T f) {
330 : : return v.x > f && v.y > f && v.z > f;
331 : : }
332 : :
333 : : template <typename T>
334 : : inline bool operator>(T f, const vec3<T>& v) {
335 : : return f > v.x && f > v.y && f > v.z;
336 : : }
337 : :
338 : : template <typename T>
339 : : inline bool operator<=(const vec3<T>& v, const vec3<T>& f) {
340 : : return v.x <= f.x && v.y <= f.y && v.z <= f.z;
341 : : }
342 : :
343 : : template <typename T>
344 : : inline bool operator<=(const vec3<T>& v, T f) {
345 : : return v.x <= f && v.y <= f && v.z <= f;
346 : : }
347 : :
348 : : template <typename T>
349 : : inline bool operator<=(T f, const vec3<T>& v) {
350 : : return f <= v.x && f <= v.y && f <= v.z;
351 : : }
352 : :
353 : : template <typename T>
354 : : inline bool operator>=(const vec3<T>& v, const vec3<T>& f) {
355 : : return v.x >= f.x && v.y >= f.y && v.z >= f.z;
356 : : }
357 : :
358 : : template <typename T>
359 : : inline bool operator>=(const vec3<T>& v, T f) {
360 : : return v.x >= f && v.y >= f && v.z >= f;
361 : : }
362 : :
363 : : template <typename T>
364 : : inline bool operator>=(T f, const vec3<T>& v) {
365 : : return f >= v.x && f >= v.y && f >= v.z;
366 : : }
367 : :
368 : : template <typename T>
369 : : inline bool operator!=(const vec3<T>& v, const vec3<T>& f) {
370 : : return v.x != f.x || v.y != f.y || v.z != f.z;
371 : : }
372 : :
373 : : template <>
374 : 0 : inline bool operator!=(const vec3<float>& v, const vec3<float>& f) {
375 : 0 : return ez::isDifferent(v.x, f.x) || ez::isDifferent(v.y, f.y) || ez::isDifferent(v.z, f.z);
376 : 0 : }
377 : :
378 : : template <>
379 : 0 : inline bool operator!=(const vec3<double>& v, const vec3<double>& f) {
380 : 0 : return ez::isDifferent(v.x, f.x) || ez::isDifferent(v.y, f.y) || ez::isDifferent(v.z, f.z);
381 : 0 : }
382 : :
383 : : template <typename T>
384 : : inline bool operator!=(const vec3<T>& v, T f) {
385 : : return v.x != f || v.y != f || v.z != f;
386 : : }
387 : :
388 : : template <typename T>
389 : : inline bool operator!=(T f, const vec3<T>& v) {
390 : : return f != v.x || f != v.y || f != v.z;
391 : : }
392 : :
393 : : template <typename T>
394 : 8 : inline bool operator==(const vec3<T>& v, const vec3<T>& f) {
395 [ + + ][ + + ]: 8 : return v.x == f.x && v.y == f.y && v.z == f.z;
[ + + ][ + + ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
396 : 8 : }
397 : :
398 : : template <>
399 : 2 : inline bool operator==(const vec3<float>& v, const vec3<float>& f) {
400 [ + + ][ + - ]: 2 : return ez::isEqual(v.x, f.x) && ez::isEqual(v.y, f.y) && ez::isEqual(v.z, f.z);
[ + - ]
401 : 2 : }
402 : :
403 : : template <>
404 : 2 : inline bool operator==(const vec3<double>& v, const vec3<double>& f) {
405 [ + + ][ + - ]: 2 : return ez::isEqual(v.x, f.x) && ez::isEqual(v.y, f.y) && ez::isEqual(v.z, f.z);
[ + - ]
406 : 2 : }
407 : :
408 : : template <typename T>
409 : : inline bool operator==(const vec3<T>& v, T f) {
410 : : return v.x == f && v.y == f && v.z == f;
411 : : }
412 : :
413 : : template <typename T>
414 : : inline bool operator==(T f, const vec3<T>& v) {
415 : : return f == v.x && f == v.y && f == v.z;
416 : : }
417 : :
418 : : // Utility functions
419 : : template <typename T>
420 : : inline vec3<T> mini(const vec3<T>& a, const vec3<T>& b) {
421 : : return vec3<T>(ez::mini(a.x, b.x), ez::mini(a.y, b.y), ez::mini(a.z, b.z));
422 : : }
423 : :
424 : : template <typename T>
425 : : inline vec3<T> maxi(const vec3<T>& a, const vec3<T>& b) {
426 : : return vec3<T>(ez::maxi(a.x, b.x), ez::maxi(a.y, b.y), ez::maxi(a.z, b.z));
427 : : }
428 : :
429 : : template <typename T>
430 : : inline vec3<T> floor(const vec3<T>& a) {
431 : : return vec3<T>(ez::floor(a.x), ez::floor(a.y), ez::floor(a.z));
432 : : }
433 : :
434 : : template <typename T>
435 : : inline vec3<T> ceil(const vec3<T>& a) {
436 : : return vec3<T>(ez::ceil(a.x), ez::ceil(a.y), ez::ceil(a.z));
437 : : }
438 : :
439 : : template <typename T>
440 : : inline vec3<T> abs(const vec3<T>& a) {
441 : : return vec3<T>(ez::abs(a.x), ez::abs(a.y), ez::abs(a.z));
442 : : }
443 : :
444 : : template <typename T>
445 : : inline T dotS(const vec3<T>& a, const vec3<T>& b) {
446 : : return a.x * b.x + a.y * b.y + a.z * b.z;
447 : : }
448 : :
449 : : template <typename T>
450 : : inline vec3<T> cCross(const vec3<T>& a, const vec3<T>& b) {
451 : : return vec3<T>(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
452 : : }
453 : :
454 : : template <typename T>
455 : : inline vec3<T> cReflect(const vec3<T>& I, const vec3<T>& N) {
456 : : return I - static_cast<T>(2) * dotS(N, I) * N;
457 : : }
458 : :
459 : : // Clamps a value between 0 and 1.
460 : : // Works with both integral and floating-point types.
461 : : template <typename T>
462 : : inline vec3<T> clamp(vec3<T> n) {
463 : : vec3<T> ret;
464 : : ret.x = ez::clamp(n.x);
465 : : ret.y = ez::clamp(n.y);
466 : : ret.z = ez::clamp(n.z);
467 : : return ret;
468 : : }
469 : :
470 : : // Clamps a value between 0 and b.
471 : : // Works with both integral and floating-point types.
472 : : template <typename T>
473 : : inline vec3<T> clamp(vec3<T> n, T b) {
474 : : vec3<T> ret;
475 : : ret.x = ez::clamp(n.x, b);
476 : : ret.y = ez::clamp(n.y, b);
477 : : ret.z = ez::clamp(n.z, b);
478 : : return ret;
479 : : }
480 : :
481 : : // Clamps a value between a and b.
482 : : // Works with both integral and floating-point types.
483 : : template <typename T>
484 : : inline vec3<T> clamp(vec3<T> n, T a, T b) {
485 : : vec3<T> ret;
486 : : ret.x = ez::clamp(n.x, a, b);
487 : : ret.y = ez::clamp(n.y, a, b);
488 : : ret.z = ez::clamp(n.z, a, b);
489 : : return ret;
490 : : }
491 : :
492 : : // Type aliases for common vector types
493 : : using dvec3 = vec3<double>;
494 : : using fvec3 = vec3<float>;
495 : : using f32vec3 = vec3<float>;
496 : : using f64vec3 = vec3<double>;
497 : : using ivec3 = vec3<int>;
498 : : using i8vec3 = vec3<int8_t>;
499 : : using i16vec3 = vec3<int16_t>;
500 : : using i32vec3 = vec3<int32_t>;
501 : : using i64vec3 = vec3<int64_t>;
502 : : using u8vec3 = vec3<uint8_t>;
503 : : using u16vec3 = vec3<uint16_t>;
504 : : using uvec3 = vec3<uint32_t>;
505 : : using u32vec3 = vec3<uint32_t>;
506 : : using u64vec3 = vec3<uint64_t>;
507 : :
508 : : // Specialization for float32 validation
509 : 0 : inline bool valid(const fvec3& a) {
510 : 0 : return floatIsValid(a.x) && floatIsValid(a.y) && floatIsValid(a.z);
511 : 0 : }
512 : :
513 : : template <typename T>
514 : : inline std::istream& operator>>(std::istream& vIn, vec3<T>& vType) {
515 : : char separator;
516 : : if (vIn >> vType.x >> separator >> vType.y >> separator >> vType.z) {
517 : : if (separator != ';') {
518 : : vIn.setstate(std::ios::failbit);
519 : : }
520 : : }
521 : : return vIn;
522 : : }
523 : :
524 : : template <typename T>
525 : : inline std::ostream& operator<<(std::ostream& vOut, const vec3<T>& vType) {
526 : : vOut << vType.x << ";" << vType.y << ";" << vType.z;
527 : : return vOut;
528 : : }
529 : :
530 : : } // namespace ez
|