Backported the improvements to the Math class from Godot4.

This commit is contained in:
Relintai 2022-08-13 16:49:47 +02:00
parent 730bce8587
commit 6523457c0f
7 changed files with 137 additions and 20 deletions

View File

@ -55,6 +55,10 @@ uint32_t Math::rand() {
return default_rand.rand(); return default_rand.rand();
} }
double Math::randfn(double mean, double deviation) {
return default_rand.randfn(mean, deviation);
}
int Math::step_decimals(double p_step) { int Math::step_decimals(double p_step) {
static const int maxn = 10; static const int maxn = 10;
static const double sd[maxn] = { static const double sd[maxn] = {
@ -184,3 +188,11 @@ double Math::random(double from, double to) {
float Math::random(float from, float to) { float Math::random(float from, float to) {
return default_rand.random(from, to); return default_rand.random(from, to);
} }
real_t Math::randomr(real_t from, real_t to) {
return default_rand.randomr(from, to);
}
int Math::random(int from, int to) {
return default_rand.random(from, to);
}

View File

@ -104,6 +104,12 @@ public:
static _ALWAYS_INLINE_ double log(double p_x) { return ::log(p_x); } static _ALWAYS_INLINE_ double log(double p_x) { return ::log(p_x); }
static _ALWAYS_INLINE_ float log(float p_x) { return ::logf(p_x); } static _ALWAYS_INLINE_ float log(float p_x) { return ::logf(p_x); }
static _ALWAYS_INLINE_ double log1p(double p_x) { return ::log1p(p_x); }
static _ALWAYS_INLINE_ float log1p(float p_x) { return ::log1pf(p_x); }
static _ALWAYS_INLINE_ double log2(double p_x) { return ::log2(p_x); }
static _ALWAYS_INLINE_ float log2(float p_x) { return ::log2f(p_x); }
static _ALWAYS_INLINE_ double exp(double p_x) { return ::exp(p_x); } static _ALWAYS_INLINE_ double exp(double p_x) { return ::exp(p_x); }
static _ALWAYS_INLINE_ float exp(float p_x) { return ::expf(p_x); } static _ALWAYS_INLINE_ float exp(float p_x) { return ::expf(p_x); }
@ -211,6 +217,24 @@ public:
value += 0.0f; value += 0.0f;
return value; return value;
} }
static _ALWAYS_INLINE_ float fposmodp(float p_x, float p_y) {
float value = Math::fmod(p_x, p_y);
if (value < 0) {
value += p_y;
}
value += 0.0f;
return value;
}
static _ALWAYS_INLINE_ double fposmodp(double p_x, double p_y) {
double value = Math::fmod(p_x, p_y);
if (value < 0) {
value += p_y;
}
value += 0.0;
return value;
}
static _ALWAYS_INLINE_ int64_t posmod(int64_t p_x, int64_t p_y) { static _ALWAYS_INLINE_ int64_t posmod(int64_t p_x, int64_t p_y) {
int64_t value = p_x % p_y; int64_t value = p_x % p_y;
if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) { if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) {
@ -265,6 +289,42 @@ public:
return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value));
} }
static _ALWAYS_INLINE_ double cubic_interpolate(double p_from, double p_to, double p_pre, double p_post, double p_weight) {
return 0.5 *
((p_from * 2.0) +
(-p_pre + p_to) * p_weight +
(2.0 * p_pre - 5.0 * p_from + 4.0 * p_to - p_post) * (p_weight * p_weight) +
(-p_pre + 3.0 * p_from - 3.0 * p_to + p_post) * (p_weight * p_weight * p_weight));
}
static _ALWAYS_INLINE_ float cubic_interpolate(float p_from, float p_to, float p_pre, float p_post, float p_weight) {
return 0.5f *
((p_from * 2.0f) +
(-p_pre + p_to) * p_weight +
(2.0f * p_pre - 5.0f * p_from + 4.0f * p_to - p_post) * (p_weight * p_weight) +
(-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
}
static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
/* Formula from Wikipedia article on Bezier curves. */
double omt = (1.0 - p_t);
double omt2 = omt * omt;
double omt3 = omt2 * omt;
double t2 = p_t * p_t;
double t3 = t2 * p_t;
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) {
/* Formula from Wikipedia article on Bezier curves. */
float omt = (1.0f - p_t);
float omt2 = omt * omt;
float omt3 = omt2 * omt;
float t2 = p_t * p_t;
float t3 = t2 * p_t;
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3;
}
static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) {
if (is_equal_approx(p_from, p_to)) { if (is_equal_approx(p_from, p_to)) {
return p_from; return p_from;
@ -328,6 +388,19 @@ public:
return result; return result;
} }
static _ALWAYS_INLINE_ float fract(float value) {
return value - floor(value);
}
static _ALWAYS_INLINE_ double fract(double value) {
return value - floor(value);
}
static _ALWAYS_INLINE_ float pingpong(float value, float length) {
return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f;
}
static _ALWAYS_INLINE_ double pingpong(double value, double length) {
return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0;
}
// double only, as these functions are mainly used by the editor and not performance-critical, // double only, as these functions are mainly used by the editor and not performance-critical,
static double ease(double p_x, double p_c); static double ease(double p_x, double p_c);
static int step_decimals(double p_step); static int step_decimals(double p_step);
@ -347,12 +420,12 @@ public:
static _ALWAYS_INLINE_ float randf() { static _ALWAYS_INLINE_ float randf() {
return (float)rand() / (float)Math::RANDOM_32BIT_MAX; return (float)rand() / (float)Math::RANDOM_32BIT_MAX;
} }
static double randfn(double mean, double deviation);
static double random(double from, double to); static double random(double from, double to);
static float random(float from, float to); static float random(float from, float to);
static real_t random(int from, int to) { static real_t randomr(real_t from, real_t to);
return (real_t)random((real_t)from, (real_t)to); static int random(int from, int to);
}
static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON, real_t min_epsilon = CMP_EPSILON) { static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON, real_t min_epsilon = CMP_EPSILON) {
// this is an approximate way to check that numbers are close, as a ratio of their average size // this is an approximate way to check that numbers are close, as a ratio of their average size

View File

@ -43,30 +43,20 @@ protected:
public: public:
_FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); } _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); }
_FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); }
_FORCE_INLINE_ void set_state(uint64_t p_state) { randbase.set_state(p_state); } _FORCE_INLINE_ void set_state(uint64_t p_state) { randbase.set_state(p_state); }
_FORCE_INLINE_ uint64_t get_state() const { return randbase.get_state(); } _FORCE_INLINE_ uint64_t get_state() const { return randbase.get_state(); }
_FORCE_INLINE_ void randomize() { randbase.randomize(); } _FORCE_INLINE_ void randomize() { randbase.randomize(); }
_FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); }
_FORCE_INLINE_ real_t randf() { return randbase.randf(); } _FORCE_INLINE_ real_t randf() { return randbase.randf(); }
_FORCE_INLINE_ real_t randf_range(real_t from, real_t to) { return randbase.random(from, to); } _FORCE_INLINE_ real_t randf_range(real_t from, real_t to) { return randbase.random(from, to); }
_FORCE_INLINE_ real_t randfn(real_t mean = 0.0, real_t deviation = 1.0) { return randbase.randfn(mean, deviation); } _FORCE_INLINE_ real_t randfn(real_t mean = 0.0, real_t deviation = 1.0) { return randbase.randfn(mean, deviation); }
_FORCE_INLINE_ int randi_range(int from, int to) { _FORCE_INLINE_ int randi_range(int from, int to) {
unsigned int ret = randbase.rand(); return randbase.random(from, to);
if (to < from) {
return ret % (from - to + 1) + to;
} else {
return ret % (to - from + 1) + from;
}
} }
RandomNumberGenerator(); RandomNumberGenerator();

View File

@ -49,3 +49,10 @@ double RandomPCG::random(double p_from, double p_to) {
float RandomPCG::random(float p_from, float p_to) { float RandomPCG::random(float p_from, float p_to) {
return randf() * (p_to - p_from) + p_from; return randf() * (p_to - p_from) + p_from;
} }
int RandomPCG::random(int p_from, int p_to) {
if (p_from == p_to) {
return p_from;
}
return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to);
}

View File

@ -80,9 +80,11 @@ public:
void randomize(); void randomize();
_FORCE_INLINE_ uint32_t rand() { _FORCE_INLINE_ uint32_t rand() {
current_seed = pcg.state;
return pcg32_random_r(&pcg); return pcg32_random_r(&pcg);
} }
_FORCE_INLINE_ uint32_t rand(uint32_t bounds) {
return pcg32_boundedrand_r(&pcg, bounds);
}
// Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity.
// These functions sample the output of rand() as the fraction part of an infinite binary number, // These functions sample the output of rand() as the fraction part of an infinite binary number,
@ -131,7 +133,8 @@ public:
double random(double p_from, double p_to); double random(double p_from, double p_to);
float random(float p_from, float p_to); float random(float p_from, float p_to);
real_t random(int p_from, int p_to) { return (real_t)random((real_t)p_from, (real_t)p_to); } real_t randomr(real_t p_from, real_t p_to) { return random(p_from, p_to); }
int random(int p_from, int p_to);
}; };
#endif // RANDOM_PCG_H #endif // RANDOM_PCG_H

View File

@ -23,3 +23,36 @@ void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
rng->state += initstate; rng->state += initstate;
pcg32_random_r(rng); pcg32_random_r(rng);
} }
// Source from https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c
// pcg32_boundedrand_r(rng, bound):
// Generate a uniformly distributed number, r, where 0 <= r < bound
uint32_t pcg32_boundedrand_r(pcg32_random_t *rng, uint32_t bound) {
// To avoid bias, we need to make the range of the RNG a multiple of
// bound, which we do by dropping output less than a threshold.
// A naive scheme to calculate the threshold would be to do
//
// uint32_t threshold = 0x100000000ull % bound;
//
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
// 32-bit platforms). In essence, we do
//
// uint32_t threshold = (0x100000000ull-bound) % bound;
//
// because this version will calculate the same modulus, but the LHS
// value is less than 2^32.
uint32_t threshold = -bound % bound;
// Uniformity guarantees that this loop will terminate. In practice, it
// should usually terminate quickly; on average (assuming all bounds are
// equally likely), 82.25% of the time, we can expect it to require just
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
// (i.e., 2147483649), which invalidates almost 50% of the range. In
// practice, bounds are typically small and only a tiny amount of the range
// is eliminated.
for (;;) {
uint32_t r = pcg32_random_r(rng);
if (r >= threshold)
return r % bound;
}
}

View File

@ -1,10 +1,8 @@
#ifndef RANDOM_H
#define RANDOM_H
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org // *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
#ifndef RANDOM_H
#define RANDOM_H
#include "core/typedefs.h" #include "core/typedefs.h"
@ -13,5 +11,6 @@
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t; typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
uint32_t pcg32_random_r(pcg32_random_t* rng); uint32_t pcg32_random_r(pcg32_random_t* rng);
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq); void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq);
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);
#endif // RANDOM_H #endif // RANDOM_H