diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarMathCommon.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarMathCommon.hpp')
-rw-r--r-- | source/core/StarMathCommon.hpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/source/core/StarMathCommon.hpp b/source/core/StarMathCommon.hpp new file mode 100644 index 0000000..27d2976 --- /dev/null +++ b/source/core/StarMathCommon.hpp @@ -0,0 +1,328 @@ +#ifndef STAR_MATH_COMMON_HPP +#define STAR_MATH_COMMON_HPP + +#include <type_traits> +#include <limits> + +#include "StarMaybe.hpp" + +namespace Star { + +STAR_EXCEPTION(MathException, StarException); + +namespace Constants { + double const pi = 3.14159265358979323846; + double const rad2deg = 57.2957795130823208768; + double const deg2rad = 1 / rad2deg; + double const sqrt2 = 1.41421356237309504880; + double const log2e = 1.44269504088896340736; +} + +// Really common std namespace includes, and replacements for std libraries +// that don't provide them + +using std::abs; +using std::fabs; +using std::sqrt; +using std::floor; +using std::ceil; +using std::round; +using std::fmod; +using std::sin; +using std::cos; +using std::tan; +using std::pow; +using std::atan2; +using std::log; +using std::log10; +using std::copysign; + +inline float log2(float f) { + return log(f) * (float)Constants::log2e; +} + +inline double log2(double d) { + return log(d) * Constants::log2e; +} + +// Count the number of '1' bits in the given unsigned integer +template <typename Int> +typename std::enable_if<std::is_integral<Int>::value && std::is_unsigned<Int>::value, unsigned>::type countSetBits(Int value) { + unsigned count = 0; + while (value != 0) { + value &= (value - 1); + ++count; + } + return count; +} + +template <typename T, typename T2> +typename std::enable_if<!std::numeric_limits<T>::is_integer && !std::numeric_limits<T2>::is_integer && sizeof(T) >= sizeof(T2), bool>::type +nearEqual(T x, T2 y, unsigned ulp) { + auto epsilon = std::numeric_limits<T>::epsilon(); + return abs(x - y) <= epsilon * max(abs(x), (T)abs(y)) * ulp; +} + +template <typename T, typename T2> +typename std::enable_if<!std::numeric_limits<T>::is_integer && !std::numeric_limits<T2>::is_integer && sizeof(T) < sizeof(T2), bool>::type +nearEqual(T x, T2 y, unsigned ulp) { + return nearEqual(y, x, ulp); +} + +template <typename T, typename T2> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T2>::is_integer, bool>::type +nearEqual(T x, T2 y, unsigned ulp) { + return nearEqual((double)x, y, ulp); +} + +template <typename T, typename T2> +typename std::enable_if<!std::numeric_limits<T>::is_integer && std::numeric_limits<T2>::is_integer, bool>::type +nearEqual(T x, T2 y, unsigned ulp) { + return nearEqual(x, (double)y, ulp); +} + +template <typename T, typename T2> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T2>::is_integer, bool>::type +nearEqual(T x, T2 y, unsigned) { + return x == y; +} + +template <typename T, typename T2> +bool nearEqual(T x, T2 y) { + return nearEqual(x, y, 1); +} + +template <typename T> +typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type nearZero(T x, unsigned ulp = 2) { + return abs(x) <= std::numeric_limits<T>::min() * ulp; +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type nearZero(T x) { + return x == 0; +} + +template <typename T> +constexpr T lowest() { + return std::numeric_limits<T>::lowest(); +} + +template <typename T> +constexpr T highest() { + return std::numeric_limits<T>::max(); +} + +template <typename T> +constexpr T square(T const& x) { + return x * x; +} + +template <typename T> +constexpr T cube(T const& x) { + return x * x * x; +} + +template <typename Float> +int ipart(Float f) { + return (int)floor(f); +} + +template <typename Float> +Float fpart(Float f) { + return f - ipart(f); +} + +template <typename Float> +Float rfpart(Float f) { + return 1.0 - fpart(f); +} + +template <typename T, typename T2> +T clampMagnitude(T const& v, T2 const& mag) { + if (v > mag) + return mag; + else if (v < -mag) + return -mag; + else + return v; +} + +template <typename T> +T clamp(T const val, T const min, T const max) { + return std::min(std::max(val, min), max); +} + +template <typename T> +T clampDynamic(T const val, T const a, T const b) { + return std::min(std::max(val, std::min(a, b)), std::max(a, b)); +} + +template <typename IntType, typename PowType> +IntType intPow(IntType i, PowType p) { + starAssert(p >= 0); + + if (p == 0) + return 1; + if (p == 1) + return i; + + IntType tmp = intPow(i, p / 2); + if ((p % 2) == 0) + return tmp * tmp; + else + return i * tmp * tmp; +} + +template <typename Int> +bool isPowerOf2(Int x) { + if (x < 1) + return false; + return (x & (x - 1)) == 0; +} + +inline uint64_t ceilPowerOf2(uint64_t v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +template <typename Float> +Float sigmoid(Float x) { + return 1 / (1 + std::exp(-x)); +} + +// returns a % m such that the answer is always positive. +// For example, -1 mod 10 is 9. +template <typename IntType> +IntType pmod(IntType a, IntType m) { + IntType r = a % m; + return r < 0 ? r + m : r; +} + +// Same as pmod but for float like values. +template <typename Float> +Float pfmod(Float a, Float m) { + if (m == 0) + return a; + + return a - m * floor(a / m); +} + +// Finds the *smallest* distance (in absolute value terms) from b to a (a - b) +// in a non-euclidean wrapping number line. Suppose size is 100, wrapDiff(10, +// 109) would return 1, because 509 is congruent to the point 9. On the other +// hand, wrapDiff(10, 111) would return -1, because 111 is congruent to the +// point 11. +template <typename Type> +Type wrapDiff(Type a, Type b, Type size) { + a = pmod(a, size); + b = pmod(b, size); + + Type diff = a - b; + if (diff > size / 2) + diff -= size; + else if (diff < -size / 2) + diff += size; + + return diff; +} + +// Sampe as wrapDiff but for float like values +template <typename Type> +Type wrapDiffF(Type a, Type b, Type size) { + a = pfmod(a, size); + b = pfmod(b, size); + + Type diff = a - b; + if (diff > size / 2) + diff -= size; + else if (diff < -size / 2) + diff += size; + + return diff; +} + +// like std::pow, except ignores sign, and the return value will match the sign +// of the value passed in. ppow(-2, 2) == -4 +template <typename Float> +Float ppow(Float val, Float pow) { + return copysign(std::pow(std::fabs(val), pow), val); +} + +// Returns angle wrapped around to the range [-pi, pi). +template <typename Float> +Float constrainAngle(Float angle) { + angle = fmod((Float)(angle + Constants::pi), (Float)(Constants::pi * 2)); + if (angle < 0) + angle += Constants::pi * 2; + return angle - Constants::pi; +} + +// Returns the closest angle movement to go from the given angle to the target +// angle, in radians. +template <typename Float> +Float angleDiff(Float angle, Float targetAngle) { + double diff = fmod((Float)(targetAngle - angle + Constants::pi), (Float)(Constants::pi * 2)); + if (diff < 0) + diff += Constants::pi * 2; + return diff - Constants::pi; +} + +// Approach the given goal value from the current value, at a maximum rate of +// change. Rate should always be a positive value. (T must be signed). +template <typename T> +T approach(T goal, T current, T rate) { + if (goal < current) { + return max(current - rate, goal); + } else if (goal > current) { + return min(current + rate, goal); + } else { + return current; + } +} + +// Same as approach, specialied for angles, and always approaches from the +// closest absolute direction. +template <typename T> +T approachAngle(T goal, T current, T rate) { + return constrainAngle(current + clampMagnitude<T>(angleDiff(current, goal), rate)); +} + +// Used in color conversion from floating point to uint8_t +inline uint8_t floatToByte(float val, bool doClamp = false) { + if (doClamp) + val = clamp(val, 0.0f, 1.0f); + return (uint8_t)(val * 255.0f); +} + +// Used in color conversion from uint8_t to normalized float. +inline float byteToFloat(uint8_t val) { + return val / 255.0f; +} + +// Turn a randomized floating point value from [0.0, 1.0] to [-1.0, 1.0] +template <typename Float> +Float randn(Float val) { + return val * 2 - 1; +} + +// Increments a value between min and max inclusive, cycling around to min when +// it would be incremented beyond max. If the value is outside of the range, +// the next increment will start at min. +template <typename Integer> +Integer cycleIncrement(Integer val, Integer min, Integer max) { + if (val < min || val >= max) + return min; + else + return val + 1; +} + +} + +#endif |