osb/source/core/StarMathCommon.hpp
2024-02-25 15:46:47 +01:00

326 lines
7.9 KiB
C++

#pragma once
#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;
}
}