diff --git a/headers/vmath.hpp/vmath_fun.hpp b/headers/vmath.hpp/vmath_fun.hpp new file mode 100644 index 0000000..de6146e --- /dev/null +++ b/headers/vmath.hpp/vmath_fun.hpp @@ -0,0 +1,169 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "vmath_fwd.hpp" + +// +// Angle and Trigonometry Functions +// + +namespace vmath_hpp +{ + template < typename T > + constexpr T radians(T degrees) noexcept { + return degrees * T(0.01745329251994329576923690768489); + } + + template < typename T > + constexpr T degrees(T radians) noexcept { + return radians * T(57.295779513082320876798154814105); + } + + using ::std::sin; + using ::std::cos; + using ::std::tan; + + using ::std::asin; + using ::std::acos; + using ::std::atan; + using ::std::atan2; + + using ::std::sinh; + using ::std::cosh; + using ::std::tanh; + + using ::std::asinh; + using ::std::acosh; + using ::std::atanh; +} + +// +// Exponential Functions +// + +namespace vmath_hpp +{ + using ::std::pow; + using ::std::exp; + using ::std::log; + using ::std::exp2; + using ::std::log2; + using ::std::sqrt; + + template < typename T > + T invsqrt(T x) noexcept { + return T(1) / sqrt(x); + } +} + +// +// Common Functions +// + +namespace vmath_hpp +{ + using ::std::abs; + + template < typename T > + T sign(T x) noexcept { + return (T(0) < x) - (x < T(0)); + } + + using ::std::floor; + using ::std::trunc; + using ::std::round; + using ::std::ceil; + + template < typename T > + T fract(T x) noexcept { + return x - floor(x); + } + + using ::std::fmod; + using ::std::modf; + + using ::std::min; + using ::std::max; + + template < typename T > + constexpr T clamp(T x, T min_x, T max_x) noexcept { + return min(max(x, min_x), max_x); + } + + template < typename T > + constexpr T mix(T x, T y, T a) noexcept { + return x * (T(1) - a) + y * a; + } + + template < typename T > + constexpr T mix(T x, T y, bool a) noexcept { + return a ? y : x; + } + + template < typename T > + constexpr T step(T edge, T x) noexcept { + return x < edge ? T(0) : T(1); + } + + template < typename T > + constexpr T smoothstep(T edge0, T edge1, T x) noexcept { + const T t = clamp((x - edge0) / (edge1 - edge0), T(0), T(1)); + return t * t * (T(3) - T(2) * t); + } + + using ::std::isnan; + using ::std::isinf; + using ::std::fma; + + using ::std::frexp; + using ::std::ldexp; +} + +// +// Geometric Functions +// + +namespace vmath_hpp +{ + template < typename T > + T length(T x) noexcept { + return abs(x); + } + + template < typename T > + T distance(T p0, T p1) noexcept { + return length(p0 - p1); + } + + template < typename T > + T dot(T x, T y) noexcept { + return x * y; + } + + template < typename T > + T normalize(T x) noexcept { + return x * invsqrt(dot(x, x)); + } + + template < typename T > + T faceforward(T n, T i, T nref) noexcept { + return dot(nref, i) < T(0) ? n : -n; + } + + template < typename T > + T reflect(T i, T n) noexcept { + return i - n * dot(n, i) * T(2); + } + + template < typename T > + T refract(T i, T n, T eta) noexcept { + const T d = dot(n, i); + const T k = T(1) - eta * eta * (T(1) - d * d); + return T(k >= T(0)) * (eta * i - (eta * d + sqrt(k)) * n); + } +} diff --git a/headers/vmath.hpp/vmath_fwd.hpp b/headers/vmath.hpp/vmath_fwd.hpp index 7657dbb..be41369 100644 --- a/headers/vmath.hpp/vmath_fwd.hpp +++ b/headers/vmath.hpp/vmath_fwd.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/untests/vmath_fun_tests.cpp b/untests/vmath_fun_tests.cpp new file mode 100644 index 0000000..715bc58 --- /dev/null +++ b/untests/vmath_fun_tests.cpp @@ -0,0 +1,135 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include + +#define CATCH_CONFIG_FAST_COMPILE +#include + +namespace +{ + template < typename T > + class approx { + public: + explicit constexpr approx(T v) : value_(v) {} + + friend constexpr bool operator==(const T& l, const approx& r) { + return (r.value_ < l + epsilon) + && (l < r.value_ + epsilon); + } + private: + T value_; + static constexpr T epsilon = std::numeric_limits::epsilon() * 100; + }; +} + +TEST_CASE("vmath/fun") { + using namespace vmath_hpp; + + SECTION("Angle and Trigonometry Functions") { + STATIC_REQUIRE(radians(degrees(12.13f)) == approx(12.13f)); + STATIC_REQUIRE(degrees(radians(12.13f)) == approx(12.13f)); + + sin(0.f); + cos(0.f); + tan(0.f); + + asin(0.f); + acos(0.f); + atan(0.f); + atan2(0.f, 0.f); + + sinh(0.f); + cosh(0.f); + tanh(0.f); + + asinh(0.f); + acosh(0.f); + atanh(0.f); + } + + SECTION("Exponential Functions") { + pow(2.f, 3.f); + exp(2.f); + log(2.f); + exp2(2.f); + log2(2.f); + sqrt(2.f); + invsqrt(2.f); + } + + SECTION("Common Functions") { + REQUIRE(abs(1) == 1); + REQUIRE(abs(-1) == 1); + REQUIRE(abs(1.f) == approx(1.f)); + REQUIRE(abs(-1.f) == approx(1.f)); + + REQUIRE(sign(2) == 1); + REQUIRE(sign(-2) == -1); + REQUIRE(sign(0) == 0); + REQUIRE(sign(2.f) == approx(1.f)); + REQUIRE(sign(-2.f) == approx(-1.f)); + REQUIRE(sign(0.f) == approx(0.f)); + + REQUIRE(floor(1.7f) == approx(1.f)); + REQUIRE(trunc(1.7f) == approx(1.f)); + REQUIRE(round(1.7f) == approx(2.f)); + REQUIRE(ceil(1.7f) == approx(2.f)); + + REQUIRE(fract(1.7f) == approx(0.7f)); + REQUIRE(fract(-2.3f) == approx(0.7f)); + + REQUIRE(fmod(1.7f, 1.2f) == approx(0.5f)); + + { + float out_i{}; + REQUIRE(modf(1.7f, &out_i) == approx(0.7f)); + REQUIRE(out_i == approx(1.f)); + } + + STATIC_REQUIRE(min(1.f, 2.f) == approx(1.f)); + STATIC_REQUIRE(max(1.f, 2.f) == approx(2.f)); + + STATIC_REQUIRE(clamp(1.0f, 2.f, 3.f) == approx(2.0f)); + STATIC_REQUIRE(clamp(2.5f, 2.f, 3.f) == approx(2.5f)); + STATIC_REQUIRE(clamp(3.5f, 2.f, 3.f) == approx(3.0f)); + + STATIC_REQUIRE(mix(0.f, 10.f, 0.5f) == approx(5.f)); + STATIC_REQUIRE(mix(0.f, 10.f, false) == approx(0.f)); + STATIC_REQUIRE(mix(0.f, 10.f, true) == approx(10.f)); + STATIC_REQUIRE(step(0.5f, 0.4f) == approx(0.f)); + STATIC_REQUIRE(step(0.5f, 0.6f) == approx(1.f)); + STATIC_REQUIRE(smoothstep(0.f, 1.f, 0.1f) == approx(0.028f)); + + REQUIRE_FALSE(isnan(1.f)); + REQUIRE_FALSE(isinf(1.f)); + + REQUIRE(fma(2.f, 3.f, 4.f) == approx(10.f)); + + { + int out_exp{}; + REQUIRE(frexp(1.7f, &out_exp) == approx(0.85f)); + REQUIRE(out_exp == 1); + } + + REQUIRE(ldexp(0.85f, 1) == approx(1.7f)); + } + + SECTION("Geometric Functions") { + REQUIRE(length(10.f) == approx(10.f)); + REQUIRE(length(-10.f) == approx(10.f)); + + REQUIRE(distance(5.f, 10.f) == approx(5.f)); + REQUIRE(distance(-5.f, -10.f) == approx(5.f)); + + REQUIRE(dot(2.f, 5.f) == approx(10.f)); + REQUIRE(normalize(0.5f) == approx(1.f)); + + REQUIRE(faceforward(1.f, 2.f, 3.f) == approx(-1.f)); + REQUIRE(reflect(1.f, 2.f) == approx(-7.f)); + REQUIRE(refract(1.f, 2.f, 1.f) == approx(-7.f)); + } +}