378 lines
14 KiB
C++
378 lines
14 KiB
C++
/// 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 <cstdint>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
/// TODO: Implement lookup tables.
|
|
|
|
#ifdef USE_LOOKUP_TABLES
|
|
|
|
static float fast_cossin_table[MAX_CIRCLE_ANGLE];
|
|
|
|
#endif
|
|
|
|
|
|
#include <J3ML/Algorithm/Reinterpret.hpp>
|
|
|
|
|
|
/// Swaps two elements in-place without copying their data.
|
|
template <typename T>
|
|
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 <stdfloat>
|
|
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<typename T>
|
|
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<typename T>
|
|
T Clamp01(const T &val) { return Clamp(val, T(0), T(1)); }
|
|
|
|
/// Computes the smaller of the two values.
|
|
/** @see Clamp(), Clamp01(), Max() */
|
|
template <typename T>
|
|
T Min(const T& a, const T& b) {
|
|
return a <= b ? a : b;
|
|
}
|
|
|
|
/// Computes the larger of two values.
|
|
/** @see Clamp(), Clamp01(), Max() */
|
|
template <typename T>
|
|
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 <typename T>
|
|
T Min(const std::initializer_list<T>& 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 <typename T>
|
|
T Max(const std::initializer_list<T>& list) {
|
|
T maximum = list[0];
|
|
for (T entry : list) {
|
|
if (entry >= maximum)
|
|
maximum = entry;
|
|
}
|
|
return maximum;
|
|
}
|
|
|
|
/** @return True if a > b. */
|
|
template <typename T>
|
|
bool GreaterThan(const T& a, const T& b) {
|
|
return a > b;
|
|
}
|
|
|
|
/** @return True if a < b. */
|
|
template <typename T>
|
|
bool LessThan(const T& a, const T& b) {
|
|
return a < b;
|
|
}
|
|
|
|
/** @return The absolute value of a. */
|
|
template <typename T>
|
|
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 <typename T>
|
|
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 <typename T> bool IsFinite(T) { return true;}
|
|
|
|
template<> inline bool IsFinite(float f) { return (ReinterpretAs<u32>(f) << 1) < 0xFF000000u; }
|
|
template<> inline bool IsFinite(double d) { return (ReinterpretAs<u64>(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<u32>(f) << 1) > 0xFF000000u; }
|
|
inline bool IsNotANumber(double d) { return (ReinterpretAs<u64>(d) << 1) > 0xFFE0000000000000ULL; }
|
|
|
|
inline bool IsInfinite(float f) { return (ReinterpretAs<u32>(f) << 1) == 0xFF000000u; }
|
|
inline bool IsInfinite(double d) { return (ReinterpretAs<u64>(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);
|
|
}
|
|
|
|
|