blob: 481c9b54177540789b99d3ee529b55f815c1da93 [file] [log] [blame]
 /* * Copyright 2022 Rive */ #ifndef _RIVE_MATH_TYPES_DEFINED_ #define _RIVE_MATH_TYPES_DEFINED_ #include "rive/rive_types.hpp" #include #include #include namespace rive { namespace math { constexpr float PI = 3.14159265f; constexpr float SQRT2 = 1.41421356f; constexpr float EPSILON = 1.f / (1 << 12); // Common threshold for detecting values near zero. RIVE_MAYBE_UNUSED inline bool nearly_zero(float a, float tolerance = EPSILON) { assert(tolerance >= 0); return fabsf(a) <= tolerance; } RIVE_MAYBE_UNUSED inline bool nearly_equal(float a, float b, float tolerance = EPSILON) { return nearly_zero(b - a, tolerance); } // Performs a floating point division with conformant IEEE 754 behavior for NaN and Inf. // // Returns +/-Inf if b == 0. // Returns 0 if b == +/-Inf. // Returns NaN if a and b are both zero. // Returns NaN if a and b are both infinite. // Returns NaN a or b is NaN. // // Reference: // https://stackoverflow.com/questions/42926763/the-behaviour-of-floating-point-division-by-zero RIVE_MAYBE_UNUSED #if defined(__clang__) || defined(__GNUC__) __attribute__((no_sanitize("float-divide-by-zero"), always_inline)) #endif inline static float ieee_float_divide(float a, float b) { static_assert(std::numeric_limits::is_iec559, "conformant IEEE 754 behavior for NaN and Inf is required"); return a / b; } // Reinterprets the underlying bits of src as the given type. template Dst bit_cast(const Src& src) { static_assert(sizeof(Dst) == sizeof(Src), "sizes of both types must match"); Dst dst; RIVE_INLINE_MEMCPY(&dst, &src, sizeof(Dst)); return dst; } // Attempt to generate a "clz" assembly instruction. RIVE_ALWAYS_INLINE static int clz32(uint32_t x) { assert(x != 0); #if __has_builtin(__builtin_clz) return __builtin_clz(x); #else uint64_t doubleBits = bit_cast(static_cast(x)); return 1054 - (doubleBits >> 52); #endif } RIVE_ALWAYS_INLINE static int clz64(uint64_t x) { assert(x != 0); #if __has_builtin(__builtin_clzll) return __builtin_clzll(x); #else uint32_t hi32 = x >> 32; return hi32 != 0 ? clz32(hi32) : 32 + clz32(x & 0xffffffff); #endif } // Returns the 1-based index of the most significat bit in x. // // 0 -> 0 // 1 -> 1 // 2..3 -> 2 // 4..7 -> 3 // ... // RIVE_ALWAYS_INLINE static uint32_t msb(uint32_t x) { return x != 0 ? 32 - clz32(x) : 0; } // Attempt to generate a "rotl" (rotate-left) assembly instruction. RIVE_ALWAYS_INLINE static uint32_t rotateleft32(uint32_t x, int y) { #if __has_builtin(__builtin_rotateleft32) return __builtin_rotateleft32(x, y); #else return (x << y) | (x >> (32 - y)); #endif } // Returns x rounded up to the next multiple of N. // If x is already a multiple of N, returns x. template RIVE_ALWAYS_INLINE constexpr size_t round_up_to_multiple_of(size_t x) { static_assert(N != 0 && (N & (N - 1)) == 0, "math::round_up_to_multiple_of<> only supports powers of 2."); return (x + (N - 1)) & ~(N - 1); } } // namespace math template T lerp(const T& a, const T& b, float t) { return a + (b - a) * t; } } // namespace rive #endif