Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/math/math_funcs.h
9903 views
1
/**************************************************************************/
2
/* math_funcs.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/error/error_macros.h"
34
#include "core/math/math_defs.h"
35
#include "core/typedefs.h"
36
37
#include <cfloat>
38
#include <cmath>
39
40
namespace Math {
41
42
_ALWAYS_INLINE_ double sin(double p_x) {
43
return std::sin(p_x);
44
}
45
_ALWAYS_INLINE_ float sin(float p_x) {
46
return std::sin(p_x);
47
}
48
49
_ALWAYS_INLINE_ double cos(double p_x) {
50
return std::cos(p_x);
51
}
52
_ALWAYS_INLINE_ float cos(float p_x) {
53
return std::cos(p_x);
54
}
55
56
_ALWAYS_INLINE_ double tan(double p_x) {
57
return std::tan(p_x);
58
}
59
_ALWAYS_INLINE_ float tan(float p_x) {
60
return std::tan(p_x);
61
}
62
63
_ALWAYS_INLINE_ double sinh(double p_x) {
64
return std::sinh(p_x);
65
}
66
_ALWAYS_INLINE_ float sinh(float p_x) {
67
return std::sinh(p_x);
68
}
69
70
_ALWAYS_INLINE_ double sinc(double p_x) {
71
return p_x == 0 ? 1 : sin(p_x) / p_x;
72
}
73
_ALWAYS_INLINE_ float sinc(float p_x) {
74
return p_x == 0 ? 1 : sin(p_x) / p_x;
75
}
76
77
_ALWAYS_INLINE_ double sincn(double p_x) {
78
return sinc(PI * p_x);
79
}
80
_ALWAYS_INLINE_ float sincn(float p_x) {
81
return sinc((float)PI * p_x);
82
}
83
84
_ALWAYS_INLINE_ double cosh(double p_x) {
85
return std::cosh(p_x);
86
}
87
_ALWAYS_INLINE_ float cosh(float p_x) {
88
return std::cosh(p_x);
89
}
90
91
_ALWAYS_INLINE_ double tanh(double p_x) {
92
return std::tanh(p_x);
93
}
94
_ALWAYS_INLINE_ float tanh(float p_x) {
95
return std::tanh(p_x);
96
}
97
98
// Always does clamping so always safe to use.
99
_ALWAYS_INLINE_ double asin(double p_x) {
100
return p_x < -1 ? (-PI / 2) : (p_x > 1 ? (PI / 2) : std::asin(p_x));
101
}
102
_ALWAYS_INLINE_ float asin(float p_x) {
103
return p_x < -1 ? (-(float)PI / 2) : (p_x > 1 ? ((float)PI / 2) : std::asin(p_x));
104
}
105
106
// Always does clamping so always safe to use.
107
_ALWAYS_INLINE_ double acos(double p_x) {
108
return p_x < -1 ? PI : (p_x > 1 ? 0 : std::acos(p_x));
109
}
110
_ALWAYS_INLINE_ float acos(float p_x) {
111
return p_x < -1 ? (float)PI : (p_x > 1 ? 0 : std::acos(p_x));
112
}
113
114
_ALWAYS_INLINE_ double atan(double p_x) {
115
return std::atan(p_x);
116
}
117
_ALWAYS_INLINE_ float atan(float p_x) {
118
return std::atan(p_x);
119
}
120
121
_ALWAYS_INLINE_ double atan2(double p_y, double p_x) {
122
return std::atan2(p_y, p_x);
123
}
124
_ALWAYS_INLINE_ float atan2(float p_y, float p_x) {
125
return std::atan2(p_y, p_x);
126
}
127
128
_ALWAYS_INLINE_ double asinh(double p_x) {
129
return std::asinh(p_x);
130
}
131
_ALWAYS_INLINE_ float asinh(float p_x) {
132
return std::asinh(p_x);
133
}
134
135
// Always does clamping so always safe to use.
136
_ALWAYS_INLINE_ double acosh(double p_x) {
137
return p_x < 1 ? 0 : std::acosh(p_x);
138
}
139
_ALWAYS_INLINE_ float acosh(float p_x) {
140
return p_x < 1 ? 0 : std::acosh(p_x);
141
}
142
143
// Always does clamping so always safe to use.
144
_ALWAYS_INLINE_ double atanh(double p_x) {
145
return p_x <= -1 ? -INF : (p_x >= 1 ? INF : std::atanh(p_x));
146
}
147
_ALWAYS_INLINE_ float atanh(float p_x) {
148
return p_x <= -1 ? (float)-INF : (p_x >= 1 ? (float)INF : std::atanh(p_x));
149
}
150
151
_ALWAYS_INLINE_ double sqrt(double p_x) {
152
return std::sqrt(p_x);
153
}
154
_ALWAYS_INLINE_ float sqrt(float p_x) {
155
return std::sqrt(p_x);
156
}
157
158
_ALWAYS_INLINE_ double fmod(double p_x, double p_y) {
159
return std::fmod(p_x, p_y);
160
}
161
_ALWAYS_INLINE_ float fmod(float p_x, float p_y) {
162
return std::fmod(p_x, p_y);
163
}
164
165
_ALWAYS_INLINE_ double modf(double p_x, double *r_y) {
166
return std::modf(p_x, r_y);
167
}
168
_ALWAYS_INLINE_ float modf(float p_x, float *r_y) {
169
return std::modf(p_x, r_y);
170
}
171
172
_ALWAYS_INLINE_ double floor(double p_x) {
173
return std::floor(p_x);
174
}
175
_ALWAYS_INLINE_ float floor(float p_x) {
176
return std::floor(p_x);
177
}
178
179
_ALWAYS_INLINE_ double ceil(double p_x) {
180
return std::ceil(p_x);
181
}
182
_ALWAYS_INLINE_ float ceil(float p_x) {
183
return std::ceil(p_x);
184
}
185
186
_ALWAYS_INLINE_ double pow(double p_x, double p_y) {
187
return std::pow(p_x, p_y);
188
}
189
_ALWAYS_INLINE_ float pow(float p_x, float p_y) {
190
return std::pow(p_x, p_y);
191
}
192
193
_ALWAYS_INLINE_ double log(double p_x) {
194
return std::log(p_x);
195
}
196
_ALWAYS_INLINE_ float log(float p_x) {
197
return std::log(p_x);
198
}
199
200
_ALWAYS_INLINE_ double log1p(double p_x) {
201
return std::log1p(p_x);
202
}
203
_ALWAYS_INLINE_ float log1p(float p_x) {
204
return std::log1p(p_x);
205
}
206
207
_ALWAYS_INLINE_ double log2(double p_x) {
208
return std::log2(p_x);
209
}
210
_ALWAYS_INLINE_ float log2(float p_x) {
211
return std::log2(p_x);
212
}
213
214
_ALWAYS_INLINE_ double exp(double p_x) {
215
return std::exp(p_x);
216
}
217
_ALWAYS_INLINE_ float exp(float p_x) {
218
return std::exp(p_x);
219
}
220
221
_ALWAYS_INLINE_ bool is_nan(double p_val) {
222
return std::isnan(p_val);
223
}
224
225
_ALWAYS_INLINE_ bool is_nan(float p_val) {
226
return std::isnan(p_val);
227
}
228
229
_ALWAYS_INLINE_ bool is_inf(double p_val) {
230
return std::isinf(p_val);
231
}
232
233
_ALWAYS_INLINE_ bool is_inf(float p_val) {
234
return std::isinf(p_val);
235
}
236
237
// These methods assume (p_num + p_den) doesn't overflow.
238
_ALWAYS_INLINE_ int32_t division_round_up(int32_t p_num, int32_t p_den) {
239
int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1;
240
return (p_num + p_den + offset) / p_den;
241
}
242
_ALWAYS_INLINE_ uint32_t division_round_up(uint32_t p_num, uint32_t p_den) {
243
return (p_num + p_den - 1) / p_den;
244
}
245
_ALWAYS_INLINE_ int64_t division_round_up(int64_t p_num, int64_t p_den) {
246
int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1;
247
return (p_num + p_den + offset) / p_den;
248
}
249
_ALWAYS_INLINE_ uint64_t division_round_up(uint64_t p_num, uint64_t p_den) {
250
return (p_num + p_den - 1) / p_den;
251
}
252
253
_ALWAYS_INLINE_ bool is_finite(double p_val) {
254
return std::isfinite(p_val);
255
}
256
_ALWAYS_INLINE_ bool is_finite(float p_val) {
257
return std::isfinite(p_val);
258
}
259
260
_ALWAYS_INLINE_ double abs(double p_value) {
261
return std::abs(p_value);
262
}
263
_ALWAYS_INLINE_ float abs(float p_value) {
264
return std::abs(p_value);
265
}
266
_ALWAYS_INLINE_ int8_t abs(int8_t p_value) {
267
return p_value > 0 ? p_value : -p_value;
268
}
269
_ALWAYS_INLINE_ int16_t abs(int16_t p_value) {
270
return p_value > 0 ? p_value : -p_value;
271
}
272
_ALWAYS_INLINE_ int32_t abs(int32_t p_value) {
273
return std::abs(p_value);
274
}
275
_ALWAYS_INLINE_ int64_t abs(int64_t p_value) {
276
return std::abs(p_value);
277
}
278
279
_ALWAYS_INLINE_ double fposmod(double p_x, double p_y) {
280
double value = fmod(p_x, p_y);
281
if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) {
282
value += p_y;
283
}
284
value += 0.0;
285
return value;
286
}
287
_ALWAYS_INLINE_ float fposmod(float p_x, float p_y) {
288
float value = fmod(p_x, p_y);
289
if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) {
290
value += p_y;
291
}
292
value += 0.0f;
293
return value;
294
}
295
296
_ALWAYS_INLINE_ double fposmodp(double p_x, double p_y) {
297
double value = fmod(p_x, p_y);
298
if (value < 0) {
299
value += p_y;
300
}
301
value += 0.0;
302
return value;
303
}
304
_ALWAYS_INLINE_ float fposmodp(float p_x, float p_y) {
305
float value = fmod(p_x, p_y);
306
if (value < 0) {
307
value += p_y;
308
}
309
value += 0.0f;
310
return value;
311
}
312
313
_ALWAYS_INLINE_ int64_t posmod(int64_t p_x, int64_t p_y) {
314
ERR_FAIL_COND_V_MSG(p_y == 0, 0, "Division by zero in posmod is undefined. Returning 0 as fallback.");
315
int64_t value = p_x % p_y;
316
if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) {
317
value += p_y;
318
}
319
return value;
320
}
321
322
_ALWAYS_INLINE_ double deg_to_rad(double p_y) {
323
return p_y * (PI / 180.0);
324
}
325
_ALWAYS_INLINE_ float deg_to_rad(float p_y) {
326
return p_y * ((float)PI / 180.0f);
327
}
328
329
_ALWAYS_INLINE_ double rad_to_deg(double p_y) {
330
return p_y * (180.0 / PI);
331
}
332
_ALWAYS_INLINE_ float rad_to_deg(float p_y) {
333
return p_y * (180.0f / (float)PI);
334
}
335
336
_ALWAYS_INLINE_ double lerp(double p_from, double p_to, double p_weight) {
337
return p_from + (p_to - p_from) * p_weight;
338
}
339
_ALWAYS_INLINE_ float lerp(float p_from, float p_to, float p_weight) {
340
return p_from + (p_to - p_from) * p_weight;
341
}
342
343
_ALWAYS_INLINE_ double cubic_interpolate(double p_from, double p_to, double p_pre, double p_post, double p_weight) {
344
return 0.5 *
345
((p_from * 2.0) +
346
(-p_pre + p_to) * p_weight +
347
(2.0 * p_pre - 5.0 * p_from + 4.0 * p_to - p_post) * (p_weight * p_weight) +
348
(-p_pre + 3.0 * p_from - 3.0 * p_to + p_post) * (p_weight * p_weight * p_weight));
349
}
350
_ALWAYS_INLINE_ float cubic_interpolate(float p_from, float p_to, float p_pre, float p_post, float p_weight) {
351
return 0.5f *
352
((p_from * 2.0f) +
353
(-p_pre + p_to) * p_weight +
354
(2.0f * p_pre - 5.0f * p_from + 4.0f * p_to - p_post) * (p_weight * p_weight) +
355
(-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
356
}
357
358
_ALWAYS_INLINE_ double cubic_interpolate_angle(double p_from, double p_to, double p_pre, double p_post, double p_weight) {
359
double from_rot = fmod(p_from, TAU);
360
361
double pre_diff = fmod(p_pre - from_rot, TAU);
362
double pre_rot = from_rot + fmod(2.0 * pre_diff, TAU) - pre_diff;
363
364
double to_diff = fmod(p_to - from_rot, TAU);
365
double to_rot = from_rot + fmod(2.0 * to_diff, TAU) - to_diff;
366
367
double post_diff = fmod(p_post - to_rot, TAU);
368
double post_rot = to_rot + fmod(2.0 * post_diff, TAU) - post_diff;
369
370
return cubic_interpolate(from_rot, to_rot, pre_rot, post_rot, p_weight);
371
}
372
373
_ALWAYS_INLINE_ float cubic_interpolate_angle(float p_from, float p_to, float p_pre, float p_post, float p_weight) {
374
float from_rot = fmod(p_from, (float)TAU);
375
376
float pre_diff = fmod(p_pre - from_rot, (float)TAU);
377
float pre_rot = from_rot + fmod(2.0f * pre_diff, (float)TAU) - pre_diff;
378
379
float to_diff = fmod(p_to - from_rot, (float)TAU);
380
float to_rot = from_rot + fmod(2.0f * to_diff, (float)TAU) - to_diff;
381
382
float post_diff = fmod(p_post - to_rot, (float)TAU);
383
float post_rot = to_rot + fmod(2.0f * post_diff, (float)TAU) - post_diff;
384
385
return cubic_interpolate(from_rot, to_rot, pre_rot, post_rot, p_weight);
386
}
387
388
_ALWAYS_INLINE_ double cubic_interpolate_in_time(double p_from, double p_to, double p_pre, double p_post, double p_weight,
389
double p_to_t, double p_pre_t, double p_post_t) {
390
/* Barry-Goldman method */
391
double t = lerp(0.0, p_to_t, p_weight);
392
double a1 = lerp(p_pre, p_from, p_pre_t == 0 ? 0.0 : (t - p_pre_t) / -p_pre_t);
393
double a2 = lerp(p_from, p_to, p_to_t == 0 ? 0.5 : t / p_to_t);
394
double a3 = lerp(p_to, p_post, p_post_t - p_to_t == 0 ? 1.0 : (t - p_to_t) / (p_post_t - p_to_t));
395
double b1 = lerp(a1, a2, p_to_t - p_pre_t == 0 ? 0.0 : (t - p_pre_t) / (p_to_t - p_pre_t));
396
double b2 = lerp(a2, a3, p_post_t == 0 ? 1.0 : t / p_post_t);
397
return lerp(b1, b2, p_to_t == 0 ? 0.5 : t / p_to_t);
398
}
399
_ALWAYS_INLINE_ float cubic_interpolate_in_time(float p_from, float p_to, float p_pre, float p_post, float p_weight,
400
float p_to_t, float p_pre_t, float p_post_t) {
401
/* Barry-Goldman method */
402
float t = lerp(0.0f, p_to_t, p_weight);
403
float a1 = lerp(p_pre, p_from, p_pre_t == 0 ? 0.0f : (t - p_pre_t) / -p_pre_t);
404
float a2 = lerp(p_from, p_to, p_to_t == 0 ? 0.5f : t / p_to_t);
405
float a3 = lerp(p_to, p_post, p_post_t - p_to_t == 0 ? 1.0f : (t - p_to_t) / (p_post_t - p_to_t));
406
float b1 = lerp(a1, a2, p_to_t - p_pre_t == 0 ? 0.0f : (t - p_pre_t) / (p_to_t - p_pre_t));
407
float b2 = lerp(a2, a3, p_post_t == 0 ? 1.0f : t / p_post_t);
408
return lerp(b1, b2, p_to_t == 0 ? 0.5f : t / p_to_t);
409
}
410
411
_ALWAYS_INLINE_ double cubic_interpolate_angle_in_time(double p_from, double p_to, double p_pre, double p_post, double p_weight,
412
double p_to_t, double p_pre_t, double p_post_t) {
413
double from_rot = fmod(p_from, TAU);
414
415
double pre_diff = fmod(p_pre - from_rot, TAU);
416
double pre_rot = from_rot + fmod(2.0 * pre_diff, TAU) - pre_diff;
417
418
double to_diff = fmod(p_to - from_rot, TAU);
419
double to_rot = from_rot + fmod(2.0 * to_diff, TAU) - to_diff;
420
421
double post_diff = fmod(p_post - to_rot, TAU);
422
double post_rot = to_rot + fmod(2.0 * post_diff, TAU) - post_diff;
423
424
return cubic_interpolate_in_time(from_rot, to_rot, pre_rot, post_rot, p_weight, p_to_t, p_pre_t, p_post_t);
425
}
426
_ALWAYS_INLINE_ float cubic_interpolate_angle_in_time(float p_from, float p_to, float p_pre, float p_post, float p_weight,
427
float p_to_t, float p_pre_t, float p_post_t) {
428
float from_rot = fmod(p_from, (float)TAU);
429
430
float pre_diff = fmod(p_pre - from_rot, (float)TAU);
431
float pre_rot = from_rot + fmod(2.0f * pre_diff, (float)TAU) - pre_diff;
432
433
float to_diff = fmod(p_to - from_rot, (float)TAU);
434
float to_rot = from_rot + fmod(2.0f * to_diff, (float)TAU) - to_diff;
435
436
float post_diff = fmod(p_post - to_rot, (float)TAU);
437
float post_rot = to_rot + fmod(2.0f * post_diff, (float)TAU) - post_diff;
438
439
return cubic_interpolate_in_time(from_rot, to_rot, pre_rot, post_rot, p_weight, p_to_t, p_pre_t, p_post_t);
440
}
441
442
_ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
443
/* Formula from Wikipedia article on Bezier curves. */
444
double omt = (1.0 - p_t);
445
double omt2 = omt * omt;
446
double omt3 = omt2 * omt;
447
double t2 = p_t * p_t;
448
double t3 = t2 * p_t;
449
450
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
451
}
452
_ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) {
453
/* Formula from Wikipedia article on Bezier curves. */
454
float omt = (1.0f - p_t);
455
float omt2 = omt * omt;
456
float omt3 = omt2 * omt;
457
float t2 = p_t * p_t;
458
float t3 = t2 * p_t;
459
460
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3;
461
}
462
463
_ALWAYS_INLINE_ double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
464
/* Formula from Wikipedia article on Bezier curves. */
465
double omt = (1.0 - p_t);
466
double omt2 = omt * omt;
467
double t2 = p_t * p_t;
468
469
double d = (p_control_1 - p_start) * 3.0 * omt2 + (p_control_2 - p_control_1) * 6.0 * omt * p_t + (p_end - p_control_2) * 3.0 * t2;
470
return d;
471
}
472
_ALWAYS_INLINE_ float bezier_derivative(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) {
473
/* Formula from Wikipedia article on Bezier curves. */
474
float omt = (1.0f - p_t);
475
float omt2 = omt * omt;
476
float t2 = p_t * p_t;
477
478
float d = (p_control_1 - p_start) * 3.0f * omt2 + (p_control_2 - p_control_1) * 6.0f * omt * p_t + (p_end - p_control_2) * 3.0f * t2;
479
return d;
480
}
481
482
_ALWAYS_INLINE_ double angle_difference(double p_from, double p_to) {
483
double difference = fmod(p_to - p_from, TAU);
484
return fmod(2.0 * difference, TAU) - difference;
485
}
486
_ALWAYS_INLINE_ float angle_difference(float p_from, float p_to) {
487
float difference = fmod(p_to - p_from, (float)TAU);
488
return fmod(2.0f * difference, (float)TAU) - difference;
489
}
490
491
_ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
492
return p_from + angle_difference(p_from, p_to) * p_weight;
493
}
494
_ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) {
495
return p_from + angle_difference(p_from, p_to) * p_weight;
496
}
497
498
_ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) {
499
return (p_value - p_from) / (p_to - p_from);
500
}
501
_ALWAYS_INLINE_ float inverse_lerp(float p_from, float p_to, float p_value) {
502
return (p_value - p_from) / (p_to - p_from);
503
}
504
505
_ALWAYS_INLINE_ double remap(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) {
506
return lerp(p_ostart, p_ostop, inverse_lerp(p_istart, p_istop, p_value));
507
}
508
_ALWAYS_INLINE_ float remap(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) {
509
return lerp(p_ostart, p_ostop, inverse_lerp(p_istart, p_istop, p_value));
510
}
511
512
_ALWAYS_INLINE_ bool is_equal_approx(double p_left, double p_right, double p_tolerance) {
513
// Check for exact equality first, required to handle "infinity" values.
514
if (p_left == p_right) {
515
return true;
516
}
517
// Then check for approximate equality.
518
return abs(p_left - p_right) < p_tolerance;
519
}
520
_ALWAYS_INLINE_ bool is_equal_approx(float p_left, float p_right, float p_tolerance) {
521
// Check for exact equality first, required to handle "infinity" values.
522
if (p_left == p_right) {
523
return true;
524
}
525
// Then check for approximate equality.
526
return abs(p_left - p_right) < p_tolerance;
527
}
528
529
_ALWAYS_INLINE_ bool is_equal_approx(double p_left, double p_right) {
530
// Check for exact equality first, required to handle "infinity" values.
531
if (p_left == p_right) {
532
return true;
533
}
534
// Then check for approximate equality.
535
double tolerance = CMP_EPSILON * abs(p_left);
536
if (tolerance < CMP_EPSILON) {
537
tolerance = CMP_EPSILON;
538
}
539
return abs(p_left - p_right) < tolerance;
540
}
541
_ALWAYS_INLINE_ bool is_equal_approx(float p_left, float p_right) {
542
// Check for exact equality first, required to handle "infinity" values.
543
if (p_left == p_right) {
544
return true;
545
}
546
// Then check for approximate equality.
547
float tolerance = (float)CMP_EPSILON * abs(p_left);
548
if (tolerance < (float)CMP_EPSILON) {
549
tolerance = (float)CMP_EPSILON;
550
}
551
return abs(p_left - p_right) < tolerance;
552
}
553
554
_ALWAYS_INLINE_ bool is_zero_approx(double p_value) {
555
return abs(p_value) < CMP_EPSILON;
556
}
557
_ALWAYS_INLINE_ bool is_zero_approx(float p_value) {
558
return abs(p_value) < (float)CMP_EPSILON;
559
}
560
561
_ALWAYS_INLINE_ bool is_same(double p_left, double p_right) {
562
return (p_left == p_right) || (is_nan(p_left) && is_nan(p_right));
563
}
564
_ALWAYS_INLINE_ bool is_same(float p_left, float p_right) {
565
return (p_left == p_right) || (is_nan(p_left) && is_nan(p_right));
566
}
567
568
_ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) {
569
if (is_equal_approx(p_from, p_to)) {
570
if (likely(p_from <= p_to)) {
571
return p_s <= p_from ? 0.0 : 1.0;
572
} else {
573
return p_s <= p_to ? 1.0 : 0.0;
574
}
575
}
576
double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0);
577
return s * s * (3.0 - 2.0 * s);
578
}
579
_ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) {
580
if (is_equal_approx(p_from, p_to)) {
581
if (likely(p_from <= p_to)) {
582
return p_s <= p_from ? 0.0f : 1.0f;
583
} else {
584
return p_s <= p_to ? 1.0f : 0.0f;
585
}
586
}
587
float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f);
588
return s * s * (3.0f - 2.0f * s);
589
}
590
591
_ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) {
592
return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta;
593
}
594
_ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) {
595
return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta;
596
}
597
598
_ALWAYS_INLINE_ double rotate_toward(double p_from, double p_to, double p_delta) {
599
double difference = angle_difference(p_from, p_to);
600
double abs_difference = abs(difference);
601
// When `p_delta < 0` move no further than to PI radians away from `p_to` (as PI is the max possible angle distance).
602
return p_from + CLAMP(p_delta, abs_difference - PI, abs_difference) * (difference >= 0.0 ? 1.0 : -1.0);
603
}
604
_ALWAYS_INLINE_ float rotate_toward(float p_from, float p_to, float p_delta) {
605
float difference = angle_difference(p_from, p_to);
606
float abs_difference = abs(difference);
607
// When `p_delta < 0` move no further than to PI radians away from `p_to` (as PI is the max possible angle distance).
608
return p_from + CLAMP(p_delta, abs_difference - (float)PI, abs_difference) * (difference >= 0.0f ? 1.0f : -1.0f);
609
}
610
611
_ALWAYS_INLINE_ double linear_to_db(double p_linear) {
612
return log(p_linear) * 8.6858896380650365530225783783321;
613
}
614
_ALWAYS_INLINE_ float linear_to_db(float p_linear) {
615
return log(p_linear) * (float)8.6858896380650365530225783783321;
616
}
617
618
_ALWAYS_INLINE_ double db_to_linear(double p_db) {
619
return exp(p_db * 0.11512925464970228420089957273422);
620
}
621
_ALWAYS_INLINE_ float db_to_linear(float p_db) {
622
return exp(p_db * (float)0.11512925464970228420089957273422);
623
}
624
625
_ALWAYS_INLINE_ double round(double p_val) {
626
return std::round(p_val);
627
}
628
_ALWAYS_INLINE_ float round(float p_val) {
629
return std::round(p_val);
630
}
631
632
_ALWAYS_INLINE_ double wrapf(double p_value, double p_min, double p_max) {
633
double range = p_max - p_min;
634
if (is_zero_approx(range)) {
635
return p_min;
636
}
637
double result = p_value - (range * floor((p_value - p_min) / range));
638
if (is_equal_approx(result, p_max)) {
639
return p_min;
640
}
641
return result;
642
}
643
_ALWAYS_INLINE_ float wrapf(float p_value, float p_min, float p_max) {
644
float range = p_max - p_min;
645
if (is_zero_approx(range)) {
646
return p_min;
647
}
648
float result = p_value - (range * floor((p_value - p_min) / range));
649
if (is_equal_approx(result, p_max)) {
650
return p_min;
651
}
652
return result;
653
}
654
655
_ALWAYS_INLINE_ int64_t wrapi(int64_t p_value, int64_t p_min, int64_t p_max) {
656
int64_t range = p_max - p_min;
657
return range == 0 ? p_min : p_min + ((((p_value - p_min) % range) + range) % range);
658
}
659
660
_ALWAYS_INLINE_ double fract(double p_value) {
661
return p_value - floor(p_value);
662
}
663
_ALWAYS_INLINE_ float fract(float p_value) {
664
return p_value - floor(p_value);
665
}
666
667
_ALWAYS_INLINE_ double pingpong(double p_value, double p_length) {
668
return (p_length != 0.0) ? abs(fract((p_value - p_length) / (p_length * 2.0)) * p_length * 2.0 - p_length) : 0.0;
669
}
670
_ALWAYS_INLINE_ float pingpong(float p_value, float p_length) {
671
return (p_length != 0.0f) ? abs(fract((p_value - p_length) / (p_length * 2.0f)) * p_length * 2.0f - p_length) : 0.0f;
672
}
673
674
// double only, as these functions are mainly used by the editor and not performance-critical,
675
double ease(double p_x, double p_c);
676
int step_decimals(double p_step);
677
int range_step_decimals(double p_step); // For editor use only.
678
double snapped(double p_value, double p_step);
679
680
uint32_t larger_prime(uint32_t p_val);
681
682
void seed(uint64_t p_seed);
683
void randomize();
684
uint32_t rand_from_seed(uint64_t *p_seed);
685
uint32_t rand();
686
_ALWAYS_INLINE_ double randd() {
687
return (double)rand() / (double)UINT32_MAX;
688
}
689
_ALWAYS_INLINE_ float randf() {
690
return (float)rand() / (float)UINT32_MAX;
691
}
692
double randfn(double p_mean, double p_deviation);
693
694
double random(double p_from, double p_to);
695
float random(float p_from, float p_to);
696
int random(int p_from, int p_to);
697
698
// This function should be as fast as possible and rounding mode should not matter.
699
_ALWAYS_INLINE_ int fast_ftoi(float p_value) {
700
return std::rint(p_value);
701
}
702
703
_ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t p_half) {
704
uint16_t h_exp, h_sig;
705
uint32_t f_sgn, f_exp, f_sig;
706
707
h_exp = (p_half & 0x7c00u);
708
f_sgn = ((uint32_t)p_half & 0x8000u) << 16;
709
switch (h_exp) {
710
case 0x0000u: /* 0 or subnormal */
711
h_sig = (p_half & 0x03ffu);
712
/* Signed zero */
713
if (h_sig == 0) {
714
return f_sgn;
715
}
716
/* Subnormal */
717
h_sig <<= 1;
718
while ((h_sig & 0x0400u) == 0) {
719
h_sig <<= 1;
720
h_exp++;
721
}
722
f_exp = ((uint32_t)(127 - 15 - h_exp)) << 23;
723
f_sig = ((uint32_t)(h_sig & 0x03ffu)) << 13;
724
return f_sgn + f_exp + f_sig;
725
case 0x7c00u: /* inf or NaN */
726
/* All-ones exponent and a copy of the significand */
727
return f_sgn + 0x7f800000u + (((uint32_t)(p_half & 0x03ffu)) << 13);
728
default: /* normalized */
729
/* Just need to adjust the exponent and shift */
730
return f_sgn + (((uint32_t)(p_half & 0x7fffu) + 0x1c000u) << 13);
731
}
732
}
733
734
_ALWAYS_INLINE_ float halfptr_to_float(const uint16_t *p_half) {
735
union {
736
uint32_t u32;
737
float f32;
738
} u;
739
740
u.u32 = halfbits_to_floatbits(*p_half);
741
return u.f32;
742
}
743
744
_ALWAYS_INLINE_ float half_to_float(const uint16_t p_half) {
745
return halfptr_to_float(&p_half);
746
}
747
748
_ALWAYS_INLINE_ uint16_t make_half_float(float p_value) {
749
union {
750
float fv;
751
uint32_t ui;
752
} ci;
753
ci.fv = p_value;
754
755
uint32_t x = ci.ui;
756
uint32_t sign = (unsigned short)(x >> 31);
757
uint32_t mantissa;
758
uint32_t exponent;
759
uint16_t hf;
760
761
// get mantissa
762
mantissa = x & ((1 << 23) - 1);
763
// get exponent bits
764
exponent = x & (0xFF << 23);
765
if (exponent >= 0x47800000) {
766
// check if the original single precision float number is a NaN
767
if (mantissa && (exponent == (0xFF << 23))) {
768
// we have a single precision NaN
769
mantissa = (1 << 23) - 1;
770
} else {
771
// 16-bit half-float representation stores number as Inf
772
mantissa = 0;
773
}
774
hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) |
775
(uint16_t)(mantissa >> 13);
776
}
777
// check if exponent is <= -15
778
else if (exponent <= 0x38000000) {
779
/*
780
// store a denorm half-float value or zero
781
exponent = (0x38000000 - exponent) >> 23;
782
mantissa >>= (14 + exponent);
783
784
hf = (((uint16_t)sign) << 15) | (uint16_t)(mantissa);
785
*/
786
hf = 0; //denormals do not work for 3D, convert to zero
787
} else {
788
hf = (((uint16_t)sign) << 15) |
789
(uint16_t)((exponent - 0x38000000) >> 13) |
790
(uint16_t)(mantissa >> 13);
791
}
792
793
return hf;
794
}
795
796
_ALWAYS_INLINE_ float snap_scalar(float p_offset, float p_step, float p_target) {
797
return p_step != 0 ? snapped(p_target - p_offset, p_step) + p_offset : p_target;
798
}
799
800
_ALWAYS_INLINE_ float snap_scalar_separation(float p_offset, float p_step, float p_target, float p_separation) {
801
if (p_step != 0) {
802
float a = snapped(p_target - p_offset, p_step + p_separation) + p_offset;
803
float b = a;
804
if (p_target >= 0) {
805
b -= p_separation;
806
} else {
807
b += p_step;
808
}
809
return (abs(p_target - a) < abs(p_target - b)) ? a : b;
810
}
811
return p_target;
812
}
813
814
}; // namespace Math
815
816