#pragma once #include #include #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 std::enable_if::value && std::is_unsigned::value, unsigned>::type countSetBits(Int value) { unsigned count = 0; while (value != 0) { value &= (value - 1); ++count; } return count; } template typename std::enable_if::is_integer && !std::numeric_limits::is_integer && sizeof(T) >= sizeof(T2), bool>::type nearEqual(T x, T2 y, unsigned ulp) { auto epsilon = std::numeric_limits::epsilon(); return abs(x - y) <= epsilon * max(abs(x), (T)abs(y)) * ulp; } template typename std::enable_if::is_integer && !std::numeric_limits::is_integer && sizeof(T) < sizeof(T2), bool>::type nearEqual(T x, T2 y, unsigned ulp) { return nearEqual(y, x, ulp); } template typename std::enable_if::is_integer && !std::numeric_limits::is_integer, bool>::type nearEqual(T x, T2 y, unsigned ulp) { return nearEqual((double)x, y, ulp); } template typename std::enable_if::is_integer && std::numeric_limits::is_integer, bool>::type nearEqual(T x, T2 y, unsigned ulp) { return nearEqual(x, (double)y, ulp); } template typename std::enable_if::is_integer && std::numeric_limits::is_integer, bool>::type nearEqual(T x, T2 y, unsigned) { return x == y; } template bool nearEqual(T x, T2 y) { return nearEqual(x, y, 1); } template typename std::enable_if::is_integer, bool>::type nearZero(T x, unsigned ulp = 2) { return abs(x) <= std::numeric_limits::min() * ulp; } template typename std::enable_if::is_integer, bool>::type nearZero(T x) { return x == 0; } template constexpr T lowest() { return std::numeric_limits::lowest(); } template constexpr T highest() { return std::numeric_limits::max(); } template constexpr T square(T const& x) { return x * x; } template constexpr T cube(T const& x) { return x * x * x; } template int ipart(Float f) { return (int)floor(f); } template Float fpart(Float f) { return f - ipart(f); } template Float rfpart(Float f) { return 1.0 - fpart(f); } template T clampMagnitude(T const& v, T2 const& mag) { if (v > mag) return mag; else if (v < -mag) return -mag; else return v; } template T clamp(T const val, T const min, T const max) { return std::min(std::max(val, min), max); } template 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 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 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 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 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 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 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 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 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 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 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 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 T approachAngle(T goal, T current, T rate) { return constrainAngle(current + clampMagnitude(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 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 Integer cycleIncrement(Integer val, Integer min, Integer max) { if (val < min || val >= max) return min; else return val + 1; } }