| TGX 1.1.3 A tiny 2D/3D graphics library optimized for 32 bits microcontrollers.

Loading...
Searching...
No Matches
Misc.h
Go to the documentation of this file.
1
5//
6// Copyright 2020 Arvind Singh
7// Fast approximations copyright 2025 Ken Cooke
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12//version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; If not, see <http://www.gnu.org/licenses/>.
21
22#ifndef _TGX_MISC_H_
23#define _TGX_MISC_H_
24
25#include "tgx_config.h"
26
27#include <stdint.h>
28#include <math.h>
29#include <string.h>
30
31
32
33// c++, no plain c
34#ifdef __cplusplus
35
36
37// macro to cast indexes as 32bit when doing pointer arithmetic
38#define TGX_CAST32(a) ((int32_t)(a))
39
40#define DEPRECATED(X) [[deprecated(" " X " ")]]
41
49#ifndef TGX_FORCED_INLINE
50 #if defined(_MSC_VER)
51 #define TGX_FORCED_INLINE __forceinline
52 #elif defined(__GNUC__) || defined(__clang__)
53 #define TGX_FORCED_INLINE inline __attribute__((always_inline))
54 #else
55 #define TGX_FORCED_INLINE inline
56 #endif
57#endif
58
59
60// check that int is at least 4 bytes.
61 static_assert(sizeof(int) >= 4, "The TGX library only works on 32 bits or 64 bits architecture. Sorry!");
62
63
64
65#if defined(ARDUINO_TEENSY41)
66
67 // check existence of external ram (EXTMEM).
68 extern "C" uint8_t external_psram_size;
69
70 // check is an address is in flash
71 #define TGX_IS_PROGMEM(X) ((((uint32_t)(X)) >= 0x60000000)&&(((uint32_t)(X)) < 0x70000000))
72
73 // check if an address is in external ram
74 #define TGX_IS_EXTMEM(X) ((((uint32_t)(X)) >= 0x70000000)&&(((uint32_t)(X)) < 0x80000000))
75
76#endif
77
78#ifndef M_PI
79#define M_PI 3.14159265358979323846
80#endif
81
82
83 namespace tgx
84{
85
86
92 template<int N> struct DummyType
93 {
94 // nothing here :-)
95 };
96
97
103 template<bool BB1, bool BB2> struct DummyTypeBB
104 {
105 // nothing here :-)
106 };
107
108
110 template<typename T = int> struct DefaultFPType
111 {
112#if TGX_SINGLE_PRECISION_COMPUTATIONS
113 typedef float fptype;
114#else
115 typedef double fptype;
116#endif
117 };
118
119#if TGX_SINGLE_PRECISION_COMPUTATIONS
121 template<> struct DefaultFPType<double>
122 {
123 typedef double fptype;
124 };
125#endif
126
127
129 template <typename, typename> struct is_same { static const bool value = false; };
130 template <typename T> struct is_same<T, T> { static const bool value = true; };
131
132
134 TGX_INLINE inline uint16_t BigEndian16(uint16_t v)
135 {
136#ifdef __GNUC__
137 return __builtin_bswap16(v);
138#else
139 return ((v >> 8) | (v << 8));
140#endif
141 }
142
143
145 template<typename T> TGX_INLINE inline void swap(T& a, T& b) { T c(a); a = b; b = c; }
146
147
149 template<typename T> TGX_INLINE inline T min(T a, T b) { return((a < b) ? a : b); }
150
151
153 template<typename T> TGX_INLINE inline T max(T a, T b) { return((a > b) ? a : b); }
154
155
157 template<typename T> TGX_INLINE inline T clamp(T v, T vmin, T vmax)
158 {
159 return max(vmin, min(vmax, v));
160 }
161
162
164 TGX_INLINE inline float roundfp(const float f) { return roundf(f); }
165
166
168 TGX_INLINE inline double roundfp(const double f) { return round(f); }
169
170
172 TGX_INLINE inline uint32_t float_as_uint32(float f) { uint32_t u; memcpy(&u, &f, sizeof(uint32_t)); return u; }
173
174
176 TGX_INLINE inline float uint32_as_float(uint32_t u) { float f; memcpy(&f, &u, sizeof(float)); return f; }
177
178
182 TGX_INLINE inline int32_t safeMultB(int32_t A, int32_t B)
183 {
184 if ((A == 0) || (B == 0)) return B;
185 const int32_t max32 = 2147483647;
186 const int32_t nB = max32 / ((A > 0) ? A : (-A));
187 return ((B <= nB) ? B : nB);
188 }
189
190
191
197 TGX_INLINE inline float fast_inv(float x)
198 {
199#if defined(__XTENSA__) && !defined(__XTENSA_SOFT_FLOAT__)
200 // 2 NR iterations, error < 1 ULP
201 float t, result;
202 asm volatile (
203 "recip0.s %0, %2\n\t"
204 "const.s %1, 1\n\t"
205 "msub.s %1, %2, %0\n\t"
206 "madd.s %0, %0, %1\n\t"
207 "const.s %1, 1\n\t"
208 "msub.s %1, %2, %0\n\t"
209 "maddn.s %0, %0, %1"
210 : "=&f" (result),
211 "=&f" (t)
212 : "f" (x)
213 );
214 return result;
215#elif TGX_USE_FAST_INV_TRICK
216 // error < 14.3 ULP (8.91e-7)
217 // float y = uint32_as_float(0x7ef33409 - float_as_uint32(x));
218 //
219 // y = fmaf(y, fmaf(-x, y, 1.00127125f), y);
220 // y = fmaf(y, fmaf(-x, y, 1.00000083f), y);
221
222 // error < 16.5 ULP (1.03e-6)
223 float y = uint32_as_float(0x7ef335a7 - float_as_uint32(x));
224
225#if TGX_USE_FMA_MATH_MISC
226 y *= fmaf(-x, y, 2.00128722f);
227 y *= fmaf(-x, y, 2.00000072f);
228#else
229 y *= 2.00128722f - x * y;
230 y *= 2.00000072f - x * y;
231#endif
232 return y;
233#else
234 return ((x == 0) ? 1.0f : (1.0f / x));
235#endif
236 }
237
238
243 TGX_INLINE inline float fast_inv_approx(float x)
244 {
245#if defined(__XTENSA__) && !defined(__XTENSA_SOFT_FLOAT__)
246 // One Newton-Raphson iteration instead of two.
247 // Less accurate than fast_inv(), but cheaper.
248 float t, result;
249 asm volatile (
250 "recip0.s %0, %2\n\t"
251 "const.s %1, 1\n\t"
252 "msub.s %1, %2, %0\n\t"
253 "madd.s %0, %0, %1"
254 : "=&f" (result),
255 "=&f" (t)
256 : "f" (x)
257 );
258 return result;
259#elif TGX_USE_FAST_INV_TRICK
260 // One correction step: faster than fast_inv(), less accurate by design.
261 float y = uint32_as_float(0x7ef335a7 - float_as_uint32(x));
262#if TGX_USE_FMA_MATH_MISC
263 y *= fmaf(-x, y, 2.00128722f);
264#else
265 y *= 2.00128722f - x * y;
266#endif
267 return y;
268#else
269 return fast_inv(x);
270#endif
271 }
272
273
274
275
276
280 TGX_INLINE inline double fast_inv(double x)
281 {
282 // do not use fast approximation for double type.
283 return ((x == 0) ? 1.0 : (1.0 / x));
284 }
285
286
287
291 TGX_INLINE inline float precise_sqrt(float x)
292 {
293 return sqrtf(x);
294 }
295
296
300 TGX_INLINE inline double precise_sqrt(double x)
301 {
302 return sqrt(x);
303 }
304
305
311 TGX_INLINE inline float fast_sqrt(float x)
312 {
313#if TGX_USE_FAST_SQRT_TRICK
314 // error < 11321 ULP (8.81e-4)
315 float y = uint32_as_float(0x5f0b3892 - (float_as_uint32(x) >> 1));
316
317#if TGX_USE_FMA_MATH_MISC
318 return x * y * fmaf(-x, y * y, 1.89099002f);
319#else
320 return x * y * (1.89099002f - x * y * y);
321#endif
322#else
323 return precise_sqrt(x);
324#endif
325 }
326
327
331 TGX_INLINE inline double fast_sqrt(double x)
332 {
333 // do not use fast approximation for double type.
334 return precise_sqrt(x);
335 }
336
337
343 TGX_INLINE inline float precise_invsqrt(float x)
344 {
345#if defined(__XTENSA__) && !defined(__XTENSA_SOFT_FLOAT__)
346 // 2 NR iterations, error < 2 ULP
347 float t0, t1, t2, t3, result;
348 asm volatile (
349 "rsqrt0.s %0, %5\n\t"
350 "mul.s %1, %5, %0\n\t"
351 "const.s %2, 3\n\t"
352 "mul.s %3, %2, %0\n\t"
353 "const.s %4, 1\n\t"
354 "msub.s %4, %1, %0\n\t"
355 "madd.s %0, %3, %4\n\t"
356 "mul.s %1, %5, %0\n\t"
357 "mul.s %3, %2, %0\n\t"
358 "const.s %4, 1\n\t"
359 "msub.s %4, %1, %0\n\t"
360 "maddn.s %0, %3, %4"
361 : "=&f" (result),
362 "=&f" (t0),
363 "=&f" (t1),
364 "=&f" (t2),
365 "=&f" (t3)
366 : "f" (x)
367 );
368 return result;
369#else
370 const float s = sqrtf(x);
371 return (s == 0) ? 1.0f : (1.0f / s);
372#endif
373 }
374
375
379 TGX_INLINE inline double precise_invsqrt(double x)
380 {
381 const double s = sqrt(x);
382 return (s == 0) ? 1.0 : (1.0 / s);
383 }
384
390 TGX_INLINE inline float fast_invsqrt(float x)
391 {
392#if defined(__XTENSA__) && !defined(__XTENSA_SOFT_FLOAT__)
393 // 1 NR iteration, error < 728 ULP
394 float t0, t1, t2, t3, result;
395 asm volatile (
396 "rsqrt0.s %0, %5\n\t"
397 "mul.s %1, %5, %0\n\t"
398 "const.s %2, 3\n\t"
399 "mul.s %3, %2, %0\n\t"
400 "const.s %4, 1\n\t"
401 "msub.s %4, %1, %0\n\t"
402 "maddn.s %0, %3, %4"
403 : "=&f" (result),
404 "=&f" (t0),
405 "=&f" (t1),
406 "=&f" (t2),
407 "=&f" (t3)
408 : "f" (x)
409 );
410 return result;
411#elif TGX_USE_FAST_INV_SQRT_TRICK
412 // error < 12536 ULP (8.81e-4)
413 float y = uint32_as_float(0x5f0b3892 - (float_as_uint32(x) >> 1));
414
415#if TGX_USE_FMA_MATH_MISC
416 return y * fmaf(-x, y * y, 1.89099002f);
417#else
418 return y * (1.89099002f - x * y * y);
419#endif
420#else
421 return precise_invsqrt(x);
422#endif
423 }
424
425
429 TGX_INLINE inline double fast_invsqrt(double x)
430 {
431 // do not use fast approximation for double type.
432 return precise_invsqrt(x);
433 }
434
435
441 TGX_INLINE inline int32_t lfloorf(float x)
442 {
443#if defined(__XTENSA__) && !defined(__XTENSA_SOFT_FLOAT__)
444 uint32_t result;
445 asm volatile (
446 "floor.s %0, %1, 0"
447 : "=a" (result)
448 : "f" (x)
449 );
450 return result;
451#else
452 #if 0 // defined (TGX_RUN_ON_ESP32S2) || defined (TGX_RUN_ON_RP2040) || defined (TGX_RUN_ON_RP2350)
453 const int32_t i = (int32_t)x;
454 return i - ((float)i > x);
455 #else
456 return (int32_t)floorf(x);
457 #endif
458#endif
459 }
460
461
467 TGX_INLINE inline float tgx_fast_cos_deg_clamped(float deg)
468 {
469 if (deg < 0.0f) deg = -deg;
470 if (deg >= 180.0f) return -1.0f;
471 constexpr float INV_90 = 1.0f / 90.0f;
472 const float u = (deg - 90.0f) * INV_90;
473 const float au = (u < 0.0f) ? -u : u;
474 const float c = -u * (2.0f - au);
475 const float ac = (c < 0.0f) ? -c : c;
476 return c + 0.225f * (c * ac - c);
477 }
478
479
485 TGX_INLINE inline float tgx_fast_sin_deg_clamped(float deg)
486 {
487 if (deg > 180.0f) return 0.0f;
488 if (deg < -180.0f) return 0.0f;
489 constexpr float INV_180 = 1.0f / 180.0f;
490 const float x = deg * INV_180;
491 const float ax = (x < 0.0f) ? -x : x;
492 const float s = 4.0f * x * (1.0f - ax);
493 const float as = (s < 0.0f) ? -s : s;
494 return s + 0.225f * (s * as - s);
495 }
496
497}
498
499
500
501
502
503#endif
504
505#endif
506
507
tgx::lfloorf
TGX_INLINE int32_t lfloorf(float x)
Compute (int32_t)floorf(x).
Definition: Misc.h:441
tgx::max
TGX_INLINE T max(T a, T b)
Don't know why but much faster than fmaxf() for floats.
Definition: Misc.h:153
tgx::swap
TGX_INLINE void swap(T &a, T &b)
Baby let me swap you one more time...
Definition: Misc.h:145
tgx::safeMultB
TGX_INLINE int32_t safeMultB(int32_t A, int32_t B)
Return a value smaller or equal to B such that the multiplication by A is safe (no overflow with int3...
Definition: Misc.h:182
tgx::float_as_uint32
TGX_INLINE uint32_t float_as_uint32(float f)
Reinterpret the bits of a float as a uint32_t.
Definition: Misc.h:172
tgx::tgx_fast_sin_deg_clamped
TGX_INLINE float tgx_fast_sin_deg_clamped(float deg)
Fast sine approximation for an angle expressed in degrees.
Definition: Misc.h:485
tgx::fast_invsqrt
TGX_INLINE float fast_invsqrt(float x)
Compute a fast approximation of the inverse square root of a float.
Definition: Misc.h:390
tgx::precise_sqrt
TGX_INLINE float precise_sqrt(float x)
Compute the square root of a float (exact computation).
Definition: Misc.h:291
tgx::clamp
TGX_INLINE T clamp(T v, T vmin, T vmax)
Template clamp version.
Definition: Misc.h:157
tgx::roundfp
TGX_INLINE float roundfp(const float f)
Rounding for floats.
Definition: Misc.h:164
tgx::min
TGX_INLINE T min(T a, T b)
Don't know why but faster than fminf() for floats.
Definition: Misc.h:149
tgx::tgx_fast_cos_deg_clamped
TGX_INLINE float tgx_fast_cos_deg_clamped(float deg)
Fast cosine approximation for an angle expressed in degrees.
Definition: Misc.h:467
tgx::precise_invsqrt
TGX_INLINE float precise_invsqrt(float x)
Compute the inverse square root of a float (exact computation).
Definition: Misc.h:343
tgx::fast_sqrt
TGX_INLINE float fast_sqrt(float x)
Compute a fast approximation of the square root of a float.
Definition: Misc.h:311
tgx::uint32_as_float
TGX_INLINE float uint32_as_float(uint32_t u)
Reinterpret the bits of a uint32_t as a float.
Definition: Misc.h:176
tgx::fast_inv
TGX_INLINE float fast_inv(float x)
Fast (approximate) computation of 1/x.
Definition: Misc.h:197
tgx::fast_inv_approx
TGX_INLINE float fast_inv_approx(float x)
Fast (very approximate) computation of 1/x.
Definition: Misc.h:243
tgx::BigEndian16
TGX_INLINE uint16_t BigEndian16(uint16_t v)
little endian / big endian conversion
Definition: Misc.h:134
tgx_config.h
Configuration file depending on the architecture.