1 /*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef TNT_MATH_TVECHELPERS_H
18 #define TNT_MATH_TVECHELPERS_H
19
20 #include "compiler.h"
21
22 #include <cmath> // for std:: namespace
23 #include <cstdint>
24
25 #include <sys/types.h>
26
27 namespace filament {
28 namespace math {
29 namespace details {
30 // -------------------------------------------------------------------------------------
31
32 template<typename U>
min(U a,U b)33 inline constexpr U min(U a, U b) noexcept {
34 return a < b ? a : b;
35 }
36
37 template<typename U>
max(U a,U b)38 inline constexpr U max(U a, U b) noexcept {
39 return a > b ? a : b;
40 }
41
42 template<typename T, typename U>
43 struct arithmetic_result {
44 using type = decltype(std::declval<T>() + std::declval<U>());
45 };
46
47 template<typename T, typename U>
48 using arithmetic_result_t = typename arithmetic_result<T, U>::type;
49
50 template<typename A, typename B = int, typename C = int, typename D = int>
51 using enable_if_arithmetic_t = std::enable_if_t<
52 is_arithmetic<A>::value &&
53 is_arithmetic<B>::value &&
54 is_arithmetic<C>::value &&
55 is_arithmetic<D>::value>;
56
57 /*
58 * No user serviceable parts here.
59 *
60 * Don't use this file directly, instead include math/vec{2|3|4}.h
61 */
62
63 /*
64 * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
65 * operators on a vector of type BASE<T>.
66 *
67 * BASE only needs to implement operator[] and size().
68 * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
69 * get all the functionality here.
70 */
71
72 template<template<typename T> class VECTOR, typename T>
73 class TVecAddOperators {
74 public:
75 /* compound assignment from a another vector of the same size but different
76 * element type.
77 */
78 template<typename U>
79 constexpr VECTOR<T>& operator+=(const VECTOR<U>& v) {
80 VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
81 for (size_t i = 0; i < lhs.size(); i++) {
82 lhs[i] += v[i];
83 }
84 return lhs;
85 }
86
87 template<typename U, typename = enable_if_arithmetic_t<U>>
88 constexpr VECTOR<T>& operator+=(U v) {
89 return operator+=(VECTOR<U>(v));
90 }
91
92 template<typename U>
93 constexpr VECTOR<T>& operator-=(const VECTOR<U>& v) {
94 VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
95 for (size_t i = 0; i < lhs.size(); i++) {
96 lhs[i] -= v[i];
97 }
98 return lhs;
99 }
100
101 template<typename U, typename = enable_if_arithmetic_t<U>>
102 constexpr VECTOR<T>& operator-=(U v) {
103 return operator-=(VECTOR<U>(v));
104 }
105
106 private:
107 /*
108 * NOTE: the functions below ARE NOT member methods. They are friend functions
109 * with they definition inlined with their declaration. This makes these
110 * template functions available to the compiler when (and only when) this class
111 * is instantiated, at which point they're only templated on the 2nd parameter
112 * (the first one, BASE<T> being known).
113 */
114
115 template<typename U>
116 friend inline constexpr
117 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, const VECTOR<U>& rv)
118 {
119 VECTOR<arithmetic_result_t<T, U>> res(lv);
120 res += rv;
121 return res;
122 }
123
124 template<typename U, typename = enable_if_arithmetic_t<U>>
125 friend inline constexpr
126 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, U rv) {
127 return lv + VECTOR<U>(rv);
128 }
129
130 template<typename U, typename = enable_if_arithmetic_t<U>>
131 friend inline constexpr
132 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(U lv, const VECTOR<T>& rv) {
133 return VECTOR<U>(lv) + rv;
134 }
135
136 template<typename U>
137 friend inline constexpr
138 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, const VECTOR<U>& rv)
139 {
140 VECTOR<arithmetic_result_t<T, U>> res(lv);
141 res -= rv;
142 return res;
143 }
144
145 template<typename U, typename = enable_if_arithmetic_t<U>>
146 friend inline constexpr
147 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, U rv) {
148 return lv - VECTOR<U>(rv);
149 }
150
151 template<typename U, typename = enable_if_arithmetic_t<U>>
152 friend inline constexpr
153 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(U lv, const VECTOR<T>& rv) {
154 return VECTOR<U>(lv) - rv;
155 }
156 };
157
158 template<template<typename T> class VECTOR, typename T>
159 class TVecProductOperators {
160 public:
161 /* compound assignment from a another vector of the same size but different
162 * element type.
163 */
164 template<typename U>
165 constexpr VECTOR<T>& operator*=(const VECTOR<U>& v) {
166 VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
167 for (size_t i = 0; i < lhs.size(); i++) {
168 lhs[i] *= v[i];
169 }
170 return lhs;
171 }
172
173 template<typename U, typename = enable_if_arithmetic_t<U>>
174 constexpr VECTOR<T>& operator*=(U v) {
175 return operator*=(VECTOR<U>(v));
176 }
177
178 template<typename U>
179 constexpr VECTOR<T>& operator/=(const VECTOR<U>& v) {
180 VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
181 for (size_t i = 0; i < lhs.size(); i++) {
182 lhs[i] /= v[i];
183 }
184 return lhs;
185 }
186
187 template<typename U, typename = enable_if_arithmetic_t<U>>
188 constexpr VECTOR<T>& operator/=(U v) {
189 return operator/=(VECTOR<U>(v));
190 }
191
192 private:
193 /*
194 * NOTE: the functions below ARE NOT member methods. They are friend functions
195 * with they definition inlined with their declaration. This makes these
196 * template functions available to the compiler when (and only when) this class
197 * is instantiated, at which point they're only templated on the 2nd parameter
198 * (the first one, BASE<T> being known).
199 */
200
201 template<typename U>
202 friend inline constexpr
203 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, const VECTOR<U>& rv)
204 {
205 VECTOR<arithmetic_result_t<T, U>> res(lv);
206 res *= rv;
207 return res;
208 }
209
210 template<typename U, typename = enable_if_arithmetic_t<U>>
211 friend inline constexpr
212 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, U rv) {
213 return lv * VECTOR<U>(rv);
214 }
215
216 template<typename U, typename = enable_if_arithmetic_t<U>>
217 friend inline constexpr
218 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(U lv, const VECTOR<T>& rv) {
219 return VECTOR<U>(lv) * rv;
220 }
221
222 template<typename U>
223 friend inline constexpr
224 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, const VECTOR<U>& rv)
225 {
226 VECTOR<arithmetic_result_t<T, U>> res(lv);
227 res /= rv;
228 return res;
229 }
230
231 template<typename U, typename = enable_if_arithmetic_t<U>>
232 friend inline constexpr
233 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, U rv) {
234 return lv / VECTOR<U>(rv);
235 }
236
237 template<typename U, typename = enable_if_arithmetic_t<U>>
238 friend inline constexpr
239 VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(U lv, const VECTOR<T>& rv) {
240 return VECTOR<U>(lv) / rv;
241 }
242 };
243
244 /*
245 * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
246 *
247 * BASE only needs to implement operator[] and size().
248 * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
249 * get all the functionality here.
250 *
251 * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
252 */
253 template<template<typename T> class VECTOR, typename T>
254 class TVecUnaryOperators {
255 public:
256 constexpr VECTOR<T> operator-() const {
257 VECTOR<T> r{};
258 VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
259 for (size_t i = 0; i < r.size(); i++) {
260 r[i] = -rv[i];
261 }
262 return r;
263 }
264 };
265
266 /*
267 * TVecComparisonOperators implements relational/comparison operators
268 * on a vector of type BASE<T>.
269 *
270 * BASE only needs to implement operator[] and size().
271 * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
272 * get all the functionality here.
273 */
274 template<template<typename T> class VECTOR, typename T>
275 class TVecComparisonOperators {
276 private:
277 /*
278 * NOTE: the functions below ARE NOT member methods. They are friend functions
279 * with they definition inlined with their declaration. This makes these
280 * template functions available to the compiler when (and only when) this class
281 * is instantiated, at which point they're only templated on the 2nd parameter
282 * (the first one, BASE<T> being known).
283 */
284 template<typename U>
285 friend inline constexpr
286 bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<U>& rv) {
287 // w/ inlining we end-up with many branches that will pollute the BPU cache
288 MATH_NOUNROLL
289 for (size_t i = 0; i < lv.size(); i++) {
290 if (lv[i] != rv[i]) {
291 return false;
292 }
293 }
294 return true;
295 }
296
297 template<typename U>
298 friend inline constexpr
299 bool MATH_PURE operator!=(const VECTOR<T>& lv, const VECTOR<U>& rv) {
300 return !operator==(lv, rv);
301 }
302
303 template<typename U>
304 friend inline constexpr
equal(const VECTOR<T> & lv,const VECTOR<U> & rv)305 VECTOR<bool> MATH_PURE equal(const VECTOR<T>& lv, const VECTOR<U>& rv) {
306 VECTOR<bool> r{};
307 for (size_t i = 0; i < lv.size(); i++) {
308 r[i] = lv[i] == rv[i];
309 }
310 return r;
311 }
312
313 template<typename U>
314 friend inline constexpr
notEqual(const VECTOR<T> & lv,const VECTOR<U> & rv)315 VECTOR<bool> MATH_PURE notEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
316 VECTOR<bool> r{};
317 for (size_t i = 0; i < lv.size(); i++) {
318 r[i] = lv[i] != rv[i];
319 }
320 return r;
321 }
322
323 template<typename U>
324 friend inline constexpr
lessThan(const VECTOR<T> & lv,const VECTOR<U> & rv)325 VECTOR<bool> MATH_PURE lessThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
326 VECTOR<bool> r{};
327 for (size_t i = 0; i < lv.size(); i++) {
328 r[i] = lv[i] < rv[i];
329 }
330 return r;
331 }
332
333 template<typename U>
334 friend inline constexpr
lessThanEqual(const VECTOR<T> & lv,const VECTOR<U> & rv)335 VECTOR<bool> MATH_PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
336 VECTOR<bool> r{};
337 for (size_t i = 0; i < lv.size(); i++) {
338 r[i] = lv[i] <= rv[i];
339 }
340 return r;
341 }
342
343 template<typename U>
344 friend inline constexpr
greaterThan(const VECTOR<T> & lv,const VECTOR<U> & rv)345 VECTOR<bool> MATH_PURE greaterThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
346 VECTOR<bool> r;
347 for (size_t i = 0; i < lv.size(); i++) {
348 r[i] = lv[i] > rv[i];
349 }
350 return r;
351 }
352
353 template<typename U>
354 friend inline
greaterThanEqual(const VECTOR<T> & lv,const VECTOR<U> & rv)355 VECTOR<bool> MATH_PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
356 VECTOR<bool> r{};
357 for (size_t i = 0; i < lv.size(); i++) {
358 r[i] = lv[i] >= rv[i];
359 }
360 return r;
361 }
362 };
363
364 /*
365 * TVecFunctions implements functions on a vector of type BASE<T>.
366 *
367 * BASE only needs to implement operator[] and size().
368 * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
369 * get all the functionality here.
370 */
371 template<template<typename T> class VECTOR, typename T>
372 class TVecFunctions {
373 private:
374 /*
375 * NOTE: the functions below ARE NOT member methods. They are friend functions
376 * with they definition inlined with their declaration. This makes these
377 * template functions available to the compiler when (and only when) this class
378 * is instantiated, at which point they're only templated on the 2nd parameter
379 * (the first one, BASE<T> being known).
380 */
381 template<typename U>
382 friend constexpr inline
dot(const VECTOR<T> & lv,const VECTOR<U> & rv)383 arithmetic_result_t<T, U> MATH_PURE dot(const VECTOR<T>& lv, const VECTOR<U>& rv) {
384 arithmetic_result_t<T, U> r{};
385 for (size_t i = 0; i < lv.size(); i++) {
386 r += lv[i] * rv[i];
387 }
388 return r;
389 }
390
norm(const VECTOR<T> & lv)391 friend inline T MATH_PURE norm(const VECTOR<T>& lv) {
392 return std::sqrt(dot(lv, lv));
393 }
394
length(const VECTOR<T> & lv)395 friend inline T MATH_PURE length(const VECTOR<T>& lv) {
396 return norm(lv);
397 }
398
norm2(const VECTOR<T> & lv)399 friend inline constexpr T MATH_PURE norm2(const VECTOR<T>& lv) {
400 return dot(lv, lv);
401 }
402
length2(const VECTOR<T> & lv)403 friend inline constexpr T MATH_PURE length2(const VECTOR<T>& lv) {
404 return norm2(lv);
405 }
406
407 template<typename U>
408 friend inline constexpr
distance(const VECTOR<T> & lv,const VECTOR<U> & rv)409 arithmetic_result_t<T, U> MATH_PURE distance(const VECTOR<T>& lv, const VECTOR<U>& rv) {
410 return length(rv - lv);
411 }
412
413 template<typename U>
414 friend inline constexpr
distance2(const VECTOR<T> & lv,const VECTOR<U> & rv)415 arithmetic_result_t<T, U> MATH_PURE distance2(const VECTOR<T>& lv, const VECTOR<U>& rv) {
416 return length2(rv - lv);
417 }
418
normalize(const VECTOR<T> & lv)419 friend inline VECTOR<T> MATH_PURE normalize(const VECTOR<T>& lv) {
420 return lv * (T(1) / length(lv));
421 }
422
rcp(VECTOR<T> v)423 friend inline VECTOR<T> MATH_PURE rcp(VECTOR<T> v) {
424 return T(1) / v;
425 }
426
abs(VECTOR<T> v)427 friend inline constexpr VECTOR<T> MATH_PURE abs(VECTOR<T> v) {
428 for (size_t i = 0; i < v.size(); i++) {
429 v[i] = v[i] < 0 ? -v[i] : v[i];
430 }
431 return v;
432 }
433
floor(VECTOR<T> v)434 friend inline VECTOR<T> MATH_PURE floor(VECTOR<T> v) {
435 for (size_t i = 0; i < v.size(); i++) {
436 v[i] = std::floor(v[i]);
437 }
438 return v;
439 }
440
ceil(VECTOR<T> v)441 friend inline VECTOR<T> MATH_PURE ceil(VECTOR<T> v) {
442 for (size_t i = 0; i < v.size(); i++) {
443 v[i] = std::ceil(v[i]);
444 }
445 return v;
446 }
447
round(VECTOR<T> v)448 friend inline VECTOR<T> MATH_PURE round(VECTOR<T> v) {
449 for (size_t i = 0; i < v.size(); i++) {
450 v[i] = std::round(v[i]);
451 }
452 return v;
453 }
454
inversesqrt(VECTOR<T> v)455 friend inline VECTOR<T> MATH_PURE inversesqrt(VECTOR<T> v) {
456 for (size_t i = 0; i < v.size(); i++) {
457 v[i] = T(1) / std::sqrt(v[i]);
458 }
459 return v;
460 }
461
sqrt(VECTOR<T> v)462 friend inline VECTOR<T> MATH_PURE sqrt(VECTOR<T> v) {
463 for (size_t i = 0; i < v.size(); i++) {
464 v[i] = std::sqrt(v[i]);
465 }
466 return v;
467 }
468
cbrt(VECTOR<T> v)469 friend inline VECTOR<T> MATH_PURE cbrt(VECTOR<T> v) {
470 for (size_t i = 0; i < v.size(); i++) {
471 v[i] = std::cbrt(v[i]);
472 }
473 return v;
474 }
475
exp(VECTOR<T> v)476 friend inline VECTOR<T> MATH_PURE exp(VECTOR<T> v) {
477 for (size_t i = 0; i < v.size(); i++) {
478 v[i] = std::exp(v[i]);
479 }
480 return v;
481 }
482
pow(VECTOR<T> v,T p)483 friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, T p) {
484 for (size_t i = 0; i < v.size(); i++) {
485 v[i] = std::pow(v[i], p);
486 }
487 return v;
488 }
489
pow(T v,VECTOR<T> p)490 friend inline VECTOR<T> MATH_PURE pow(T v, VECTOR<T> p) {
491 for (size_t i = 0; i < p.size(); i++) {
492 p[i] = std::pow(v, p[i]);
493 }
494 return p;
495 }
496
pow(VECTOR<T> v,VECTOR<T> p)497 friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, VECTOR<T> p) {
498 for (size_t i = 0; i < v.size(); i++) {
499 v[i] = std::pow(v[i], p[i]);
500 }
501 return v;
502 }
503
log(VECTOR<T> v)504 friend inline VECTOR<T> MATH_PURE log(VECTOR<T> v) {
505 for (size_t i = 0; i < v.size(); i++) {
506 v[i] = std::log(v[i]);
507 }
508 return v;
509 }
510
log10(VECTOR<T> v)511 friend inline VECTOR<T> MATH_PURE log10(VECTOR<T> v) {
512 for (size_t i = 0; i < v.size(); i++) {
513 v[i] = std::log10(v[i]);
514 }
515 return v;
516 }
517
log2(VECTOR<T> v)518 friend inline VECTOR<T> MATH_PURE log2(VECTOR<T> v) {
519 for (size_t i = 0; i < v.size(); i++) {
520 v[i] = std::log2(v[i]);
521 }
522 return v;
523 }
524
saturate(const VECTOR<T> & lv)525 friend inline constexpr VECTOR<T> MATH_PURE saturate(const VECTOR<T>& lv) {
526 return clamp(lv, T(0), T(1));
527 }
528
clamp(VECTOR<T> v,T min,T max)529 friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, T min, T max) {
530 for (size_t i = 0; i < v.size(); i++) {
531 v[i] = details::min(max, details::max(min, v[i]));
532 }
533 return v;
534 }
535
clamp(VECTOR<T> v,VECTOR<T> min,VECTOR<T> max)536 friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, VECTOR<T> min, VECTOR<T> max) {
537 for (size_t i = 0; i < v.size(); i++) {
538 v[i] = details::min(max[i], details::max(min[i], v[i]));
539 }
540 return v;
541 }
542
fma(const VECTOR<T> & lv,const VECTOR<T> & rv,VECTOR<T> a)543 friend inline constexpr VECTOR<T> MATH_PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv,
544 VECTOR<T> a) {
545 for (size_t i = 0; i < lv.size(); i++) {
546 a[i] += (lv[i] * rv[i]);
547 }
548 return a;
549 }
550
min(const VECTOR<T> & u,VECTOR<T> v)551 friend inline constexpr VECTOR<T> MATH_PURE min(const VECTOR<T>& u, VECTOR<T> v) {
552 for (size_t i = 0; i < v.size(); i++) {
553 v[i] = details::min(u[i], v[i]);
554 }
555 return v;
556 }
557
max(const VECTOR<T> & u,VECTOR<T> v)558 friend inline constexpr VECTOR<T> MATH_PURE max(const VECTOR<T>& u, VECTOR<T> v) {
559 for (size_t i = 0; i < v.size(); i++) {
560 v[i] = details::max(u[i], v[i]);
561 }
562 return v;
563 }
564
max(const VECTOR<T> & v)565 friend inline constexpr T MATH_PURE max(const VECTOR<T>& v) {
566 T r(v[0]);
567 for (size_t i = 1; i < v.size(); i++) {
568 r = max(r, v[i]);
569 }
570 return r;
571 }
572
min(const VECTOR<T> & v)573 friend inline constexpr T MATH_PURE min(const VECTOR<T>& v) {
574 T r(v[0]);
575 for (size_t i = 1; i < v.size(); i++) {
576 r = min(r, v[i]);
577 }
578 return r;
579 }
580
mix(const VECTOR<T> & u,VECTOR<T> v,T a)581 friend inline constexpr VECTOR<T> MATH_PURE mix(const VECTOR<T>& u, VECTOR<T> v, T a) {
582 for (size_t i = 0; i < v.size(); i++) {
583 v[i] = u[i] * (T(1) - a) + v[i] * a;
584 }
585 return v;
586 }
587
smoothstep(T edge0,T edge1,VECTOR<T> v)588 friend inline constexpr VECTOR<T> MATH_PURE smoothstep(T edge0, T edge1, VECTOR<T> v) {
589 VECTOR<T> t = saturate((v - edge0) / (edge1 - edge0));
590 return t * t * (T(3) - T(2) * t);
591 }
592
step(T edge,VECTOR<T> v)593 friend inline constexpr VECTOR<T> MATH_PURE step(T edge, VECTOR<T> v) {
594 for (size_t i = 0; i < v.size(); i++) {
595 v[i] = v[i] < edge ? T(0) : T(1);
596 }
597 return v;
598 }
599
step(VECTOR<T> edge,VECTOR<T> v)600 friend inline constexpr VECTOR<T> MATH_PURE step(VECTOR<T> edge, VECTOR<T> v) {
601 for (size_t i = 0; i < v.size(); i++) {
602 v[i] = v[i] < edge[i] ? T(0) : T(1);
603 }
604 return v;
605 }
606
any(const VECTOR<T> & v)607 friend inline constexpr bool MATH_PURE any(const VECTOR<T>& v) {
608 for (size_t i = 0; i < v.size(); i++) {
609 if (v[i] != T(0)) return true;
610 }
611 return false;
612 }
613
all(const VECTOR<T> & v)614 friend inline constexpr bool MATH_PURE all(const VECTOR<T>& v) {
615 bool result = true;
616 for (size_t i = 0; i < v.size(); i++) {
617 result &= (v[i] != T(0));
618 }
619 return result;
620 }
621 };
622
623 // -------------------------------------------------------------------------------------
624 } // namespace details
625 } // namespace math
626 } // namespace filament
627
628 #endif // TNT_MATH_TVECHELPERS_H
629