/// Josh's 3D Math Library /// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing. /// Developed and Maintained by Josh O'Leary @ Redacted Software. /// Special Thanks to William Tomasine II and Maxine Hayes. /// (c) 2024 Redacted Software /// This work is dedicated to the public domain. /// @file J3ML.h /// @desc Core mathematical and utility functions, concrete types, and math constants. /// @edit 2024-07-05 #pragma once #include #include #include #include #include /// TODO: Implement lookup tables. #ifdef USE_LOOKUP_TABLES static float fast_cossin_table[MAX_CIRCLE_ANGLE]; #endif #include /// Swaps two elements in-place without copying their data. template void Swap(T &a, T &b) { T temp = std::move(a); a = std::move(b); b = std::move(temp); } /// Clean symbolic names for integers of specific size. namespace J3ML::SizedIntegralTypes { using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using s8 = int8_t; using s16 = int16_t; using s32 = int32_t; using s64 = int64_t; } namespace J3ML::SizedFloatTypes { // TODO: Use C++23 using f16 = float; using f32 = float; using f64 = double; using f128 = long double; } using namespace J3ML::SizedIntegralTypes; using namespace J3ML::SizedFloatTypes; namespace J3ML::Math::BitTwiddling { /// Parses a string of form "011101010" to a u32 u32 BinaryStringToValue(const char* s); /// Returns the number of 1's set in the given value. inline int CountBitsSet(u32 value); } // TODO: Implement "Wrappers" for most standard math functions. // We want to later-on implement lookup tables and SSE as conditional macros. namespace J3ML::Math::Constants { /// sqrt(2pi) ^ -1 constexpr float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364; /// pi - https://www.mathsisfun.com/numbers/pi.html constexpr float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; /// e - https://www.mathsisfun.com/numbers/e-eulers-number.html constexpr float EulersNumber = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; /// 2pi - The ratio of a circle's circumferecne to its radius, and the number of radians in one turn. constexpr float Tau = 6.28318530717958647692; /// sqrt(2) constexpr float PythagorasConstant = 1.41421356237309504880; /// sqrt(3) constexpr float TheodorusConstant = 1.73205080756887729352; /// Golden Ratio constexpr float Phi = 1.61803398874989484820; /// ln 2 constexpr float NaturalLog2 = 0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875; /// ln 10 constexpr float NaturalLog10 = 2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983; constexpr float Infinity = INFINITY; constexpr float NegativeInfinity = -INFINITY; constexpr float NotANumber = NAN; } /// This set of functions may be set to use lookup tables or SIMD operations. /// If no options are set, they will default to using standard library implementation. #undef USE_LOOKUP_TABLES /// Pre-computed lookup tables. #undef USE_SSE /// Streaming SIMD Extensions (x86) #undef USE_NEON /// ARM Vector Processing #undef USE_AVX /// Advanced Vector Extensions (x86) namespace J3ML::Math::Functions { /// Clamps the given input value to the range [min, max]. /** @see Clamp01(), Min(), Max(). */ template T Clamp(const T &val, const T &floor, const T &ceil) { assert(floor <= ceil); return val <= ceil ? (val >= floor ? val : floor) : ceil; } /// Clamps the given input value to the range [0, 1]. /** @see Clamp(), Min(), Max(). */ template T Clamp01(const T &val) { return Clamp(val, T(0), T(1)); } /// Computes the smaller of the two values. /** @see Clamp(), Clamp01(), Max() */ template T Min(const T& a, const T& b) { return a <= b ? a : b; } /// Computes the larger of two values. /** @see Clamp(), Clamp01(), Max() */ template T Max(const T& a, const T& b) { return a >= b ? a : b; } /// Computes the smallest in an arbitrary list of values. /** @see Clamp(), Clamp01(), Max() */ template T Min(const std::initializer_list& list) { T minimum = list[0]; for (T entry : list) { if (entry <= minimum) minimum = entry; } return minimum; } /// Computes the largest in an arbitrary list of values. /** @see Clamp(), Clamp01(), Max() */ template T Max(const std::initializer_list& list) { T maximum = list[0]; for (T entry : list) { if (entry >= maximum) maximum = entry; } return maximum; } /** @return True if a > b. */ template bool GreaterThan(const T& a, const T& b) { return a > b; } /** @return True if a < b. */ template bool LessThan(const T& a, const T& b) { return a < b; } /** @return The absolute value of a. */ template T Abs(const T& a) { return a >= 0 ? a : -a; } template<> inline float Abs(const float& a) { #ifdef USE_SSE #else return a >= 0 ? a : -a; #endif } /// @return True if a and b are equal, using operator ==(). template bool EqualExact(const T& a, const T& b) { return a == b; } /** Compares the two values for equality up to a small epsilon. */ bool Equal(float a, float b, float epsilon = 1e-3f); /** Compares the two values for equality up to a small epsilon. */ bool Equal(double a, double b, float epsilon = 1e-3f); /** Compares the two values for equality, allowing the given amount of absolute error. */ bool EqualAbs(float a, float b, float epsilon = 1e-3f); /// Computes the relative error of the two variables. float RelativeError(float a, float b); template bool IsFinite(T) { return true;} template<> inline bool IsFinite(float f) { return (ReinterpretAs(f) << 1) < 0xFF000000u; } template<> inline bool IsFinite(double d) { return (ReinterpretAs(d) << 1) < 0xFFE0000000000000ULL; } template<> inline bool IsFinite(u32 i) { return (i << 1) < 0xFF000000u; } template<> inline bool IsFinite(u64 i) { return (i << 1) < 0xFFE0000000000000ULL;} inline bool IsNotANumber(float f) { return (ReinterpretAs(f) << 1) > 0xFF000000u; } inline bool IsNotANumber(double d) { return (ReinterpretAs(d) << 1) > 0xFFE0000000000000ULL; } inline bool IsInfinite(float f) { return (ReinterpretAs(f) << 1) == 0xFF000000u; } inline bool IsInfinite(double d) { return (ReinterpretAs(d) << 1) == 0xFFE0000000000000ULL; } float Radians(float deg); /// Converts the given amount of degrees into radians. float Degrees(float rad); /// Converts the given amount of radians into degrees. float Sin(float x); /// Computes the sine of x, in radians. float Cos(float x); /// Computes the cosine of x, in radians. float Tan(float x); /// Computes the tangent of x, in radians. /// Simultaneously computes both sine and cosine of x, in radians. /// This yields a small performance increase over computing them separately. /// @see Sin(), Cos(). void SinCos(float x, float& outSin, float& outCos); float Asin(float x); /// Computes the inverse sine of x, in radians. float Acos(float x); /// Computes the inverse cosine of x, in radians. float Atan(float x); /// Computes the inverse tangent of x, in radians. float Atan2(float y, float x); /// Computes the signed (principal value) inverse tangent of y/x, in radians. float Sinh(float x); /// Computes the hyperbolic sine of x, in radians. float Cosh(float x); /// Computes the hyperbolic cosine of x, in radians. float Tanh(float x); /// Computes the hyperbolic tangent of x, in radians. bool IsPow2(u32 number); /// Returns true if the given number is a power of 2. bool IsPow2(u64 number); /// Returns true if the given number is a power of 2. float PowInt(float base, int exponent); /// Raises the given base to an integral exponent. float Pow(float base, float exponent); /// Raises the given base to a float exponent. float Exp(float exp); /// Returns e (the constant 2.71828...) raised to the given power. float Log(float base, float value); /// Computes a logarithm of the given value in the specified base. float Log2(float value); /// Computes a logarithm in base-2. float Ln(float value); /// Computes a logarithm in the natural base (using e as the base). float Log10(float value); /// Computes a logarithm in base-10; float Ceil(float f); /// Returns f rounded up to the next integer, as float. int CeilInt(float f); /// Returns f rounded up to the next integer, as integer. float Floor(float f); /// Returns f rounded down to the previous integer, as float. int FloorInt(float f); /// Returns f rounded down to the previous integer, as integer. float Round(float f); /// Returns f rounded to the nearest integer, as float. int RoundInt(float f); /// Returns f rounded to the nearest integer, as int. float Round(float f, float decimalPlaces); /// Returns f rounded to the given decimal places. float Sign(float f); /// Returns -1 or 1 depending on the sign of f. /// float SignOrZero(float f, float epsilon = 1e-8f); /// Formats larger numbers into shortened 'Truncated' string representations. /// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M std::string Truncate(float input); float RecipFast(float x); struct NumberRange { float LowerBound; float UpperBound; }; float NormalizeToRange(float input, float fromLower, float fromUpper, float toLower, float toUpper); float NormalizeToRange(float input, const NumberRange& from, const NumberRange& to); // auto rotation_normalized = NormalizeToRange(inp, {0, 360}, {-1, 1}); /// Linearly interpolates between a and b. /** @param t A value between [0,1]. @param a The first endpoint to lerp between. @param b The second endpoint to lerp between. @return This function computes a + t*(b-a). That is, if t==0, this function returns a. If t==1, this function returns b. Otherwise, the returned value linearly moves from a to b as t ranges from 0 to 1. @see LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */ float Lerp(float a, float b, float t); /// Linearly interpolates from a to b, under the modulus mod. /** This function takes evaluates a and b in the range [0, mod] and takes the shorter path to reach from a to b. @see Lerp(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */ float LerpMod(float a, float b, float mod, float t); /// Computes the lerp factor a and b have to be Lerp()ed to get x. /// /** @see Lerp(), LerpMod(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */ float InverseLerp(float a, float b, float x); /// See http://msdn.microsoft.com/en-us/library/bb509665(v=VS.85).aspx float Step(float y, float x); /// See http://msdn.microsoft.com/en-us/library/bb509658(v=vs.85).aspx float Ramp(float min, float max, float x); /// Limits x to the range [0, mod], but instead of wrapping around from mod to 0, the result will move back /// from mod to 0 as x goes from mod to 2*mod. /** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), Mod(), ModPos(), Frac(). */ float PingPongMod(float x, float mod); /// Computes a floating-point modulus. /// This function returns a value in the range ]-mod, mod[. /** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), ModPos(), Frac(). */ float Mod(float x, float mod); /// Computes a floating-point modulus using an integer as the modulus. float Mod(float x, int mod); /// Computes a floating-point modulus, but restricts the output to the range [0, mod[. /** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), Frac(). */ float ModPos(float x, float mod); /// Computes a floating-point modulus, but restricts the output to the range [0, mod[. float ModPos(float x, int mod); /// Returns the fractional part of x. /** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(). */ float Frac(float x); float Sqrt(float x); /// Returns the square root of x. float FastSqrt(float x); /// Computes a fast approximation of the square root of x. float RSqrt(float x); /// Returns 1/Sqrt(x). (The reciprocal of the square root of x) float FastRSqrt(float x); /// SSE implementation of reciprocal square root. float Recip(float x); /// Returns 1/x, the reciprocal of x. float RecipFast(float x); /// Returns 1/x, the reciprocal of x, using a fast approximation (SSE rcp instruction). namespace Interp { inline float SmoothStart(float t); } } namespace J3ML::Math { using namespace Math::Constants; using namespace Math::Functions; struct Rotation { public: Rotation(); Rotation(float value); float valueInRadians; float ValueInRadians() const; float ValueInDegrees() const; Rotation operator+(const Rotation& rhs); }; Rotation operator ""_rad(long double rads); Rotation operator ""_radians(long double rads); Rotation operator ""_deg(long double rads); Rotation operator ""_degrees(long double rads); }