diff --git a/README.md b/README.md index c5896c1..7388400 100644 --- a/README.md +++ b/README.md @@ -46,4 +46,1133 @@ add_subdirectory(external/vmath.hpp) target_link_libraries(your_project_target vmath.hpp) ``` +## API + +- [Vector Types](#Vector-Types) +- [Matrix Types](#Matrix-Types) +- [Vector Operators](#Vector-Operators) +- [Matrix Operators](#Matrix-Operators) +- [Angle and Trigonometry Functions](#Angle-and-Trigonometry-Functions) +- [Exponential Functions](#Exponential-Functions) +- [Common Functions](#Common-Functions) +- [Geometric Functions](#Geometric-Functions) +- [Relational Functions](#Relational-Functions) +- [Matrix Functions](#Matrix-Functions) +- [Units](#Units) +- [Cast](#Cast) +- [Access](#Access) +- [Matrix Transform 3D](#Matrix-Transform-3D) +- [Matrix Transform 2D](#Matrix-Transform-2D) +- [Matrix Projections](#Matrix-Projections) +- [Vector Transform](#Vector-Transform) + +### Vector Types + +```cpp +template < typename T, size_t Size > +class vec_base; + +template < typename T > +class vec_base { +public: + T x{}, y{}; + + vec_base() = default; + constexpr explicit vec_base(T v); + constexpr vec_base(T x, T y); + constexpr explicit vec_base(const vec_base& xy); + constexpr explicit vec_base(const vec_base& xy); +}; + +template < typename T > +class vec_base { +public: + T x{}, y{}, z{}; + + vec_base() = default; + constexpr explicit vec_base(T v); + constexpr vec_base(T x, T y, T z); + constexpr vec_base(const vec_base& xy, T z); + constexpr vec_base(T x, const vec_base& yz); + constexpr explicit vec_base(const vec_base& xyz); +}; + +template < typename T > +class vec_base { +public: + T x{}, y{}, z{}, w{}; + + vec_base() = default; + constexpr explicit vec_base(T v); + constexpr vec_base(T x, T y, T z, T w); + constexpr vec_base(const vec_base& xy, T z, T w); + constexpr vec_base(T x, const vec_base& yz, T w); + constexpr vec_base(T x, T y, const vec_base& zw); + constexpr vec_base(const vec_base& xy, const vec_base& zw); + constexpr vec_base(const vec_base& xyz, T w); + constexpr vec_base(T x, const vec_base& yzw); +}; + +template < typename T, size_t Size > +class vec final : public vec_base { +public: + using value_type = T; + + using pointer = value_type*; + using const_pointer = const value_type*; + + using reference = value_type&; + using const_reference = const value_type&; + + static constexpr size_t size = Size; + + void swap(vec& other) noexcept(is_nothrow_swappable_v); + + constexpr reference at(size_t index); + constexpr const_reference at(size_t index) const; + + constexpr reference operator[](size_t index) noexcept; + constexpr const_reference operator[](size_t index) const noexcept; +}; + +using bool2 = vec; +using bool3 = vec; +using bool4 = vec; + +using int2 = vec; +using int3 = vec; +using int4 = vec; + +using uint2 = vec; +using uint3 = vec; +using uint4 = vec; + +using float2 = vec; +using float3 = vec; +using float4 = vec; + +using double2 = vec; +using double3 = vec; +using double4 = vec; + +using size2 = vec; +using size3 = vec; +using size4 = vec; + +using ptrdiff2 = vec; +using ptrdiff3 = vec; +using ptrdiff4 = vec; +``` + +### Matrix Types + +```cpp +template < typename T, size_t Size > +class mat_base; + +template < typename T > +class mat_base { +public: + using row_type = vec; + + row_type rows[2] = { + {1, 0}, + {0, 1}}; + + mat_base() = default; + constexpr explicit mat_base(T v); + + constexpr mat_base( + T m11, T m12, + T m21, T m22); + + constexpr mat_base( + const row_type& row0, + const row_type& row1); + + constexpr explicit mat_base(const mat_base& other); + constexpr explicit mat_base(const mat_base& other); +}; + +template < typename T > +class mat_base { +public: + using row_type = vec; + + row_type rows[3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}}; + + mat_base() = default; + constexpr explicit mat_base(T v); + + constexpr mat_base( + T m11, T m12, T m13, + T m21, T m22, T m23, + T m31, T m32, T m33); + + constexpr mat_base( + const row_type& row0, + const row_type& row1, + const row_type& row2); + + constexpr explicit mat_base(const mat_base& other); + constexpr explicit mat_base(const mat_base& other); +}; + +template < typename T > +class mat_base { +public: + using row_type = vec; + + row_type rows[4] = { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}}; + + mat_base() = default; + constexpr explicit mat_base(T v); + + constexpr mat_base( + T m11, T m12, T m13, T m14, + T m21, T m22, T m23, T m24, + T m31, T m32, T m33, T m34, + T m41, T m42, T m43, T m44); + + constexpr mat_base( + const row_type& row0, + const row_type& row1, + const row_type& row2, + const row_type& row3); + + constexpr explicit mat_base(const mat_base& other); + constexpr explicit mat_base(const mat_base& other); +}; + +template < typename T, size_t Size > +class mat final : public mat_base { +public: + using row_type = vec; + + using pointer = row_type*; + using const_pointer = const row_type*; + + using reference = row_type&; + using const_reference = const row_type&; + + static constexpr size_t size = Size; + + void swap(mat& other) noexcept(is_nothrow_swappable_v); + + constexpr reference at(size_t index); + constexpr const_reference at(size_t index) const; + + constexpr reference operator[](size_t index) noexcept; + constexpr const_reference operator[](size_t index) const noexcept; +}; + +using bool2x2 = mat; +using bool3x3 = mat; +using bool4x4 = mat; + +using int2x2 = mat; +using int3x3 = mat; +using int4x4 = mat; + +using uint2x2 = mat; +using uint3x3 = mat; +using uint4x4 = mat; + +using float2x2 = mat; +using float3x3 = mat; +using float4x4 = mat; + +using double2x2 = mat; +using double3x3 = mat; +using double4x4 = mat; + +using size2x2 = mat; +using size3x3 = mat; +using size4x4 = mat; + +using ptrdiff2x2 = mat; +using ptrdiff3x3 = mat; +using ptrdiff4x4 = mat; +``` + +### Vector Operators + +```cpp +// -operator + +template < typename T, size_t Size > +constexpr vec operator-(const vec& xs); + +// operator+ + +template < typename T, size_t Size > +constexpr vec operator+(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec operator+(T x, const vec& ys); + +template < typename T, size_t Size > +constexpr vec operator+(const vec& xs, const vec& ys); + +// operator+= + +template < typename T, size_t Size > +constexpr vec& operator+=(vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec& operator+=(vec& xs, const vec& ys); + +// operator- + +template < typename T, size_t Size > +constexpr vec operator-(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec operator-(T x, const vec& ys); + +template < typename T, size_t Size > +constexpr vec operator-(const vec& xs, const vec& ys); + +// operator-= + +template < typename T, size_t Size > +constexpr vec& operator-=(vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec& operator-=(vec& xs, const vec& ys); + +// operator* + +template < typename T, size_t Size > +constexpr vec operator*(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec operator*(T x, const vec& ys); + +template < typename T, size_t Size > +constexpr vec operator*(const vec& xs, const vec& ys); + +// operator*= + +template < typename T, size_t Size > +constexpr vec& operator*=(vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec& operator*=(vec& xs, const vec& ys); + +// operator/ + +template < typename T, size_t Size > +constexpr vec operator/(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec operator/(T x, const vec& ys); + +template < typename T, size_t Size > +constexpr vec operator/(const vec& xs, const vec& ys); + +// operator/= + +template < typename T, size_t Size > +constexpr vec& operator/=(vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec& operator/=(vec& xs, const vec& ys); + +// operator== + +template < typename T, size_t Size > +constexpr bool operator==(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr bool operator!=(const vec& xs, const vec& ys); + +// operator< + +template < typename T, size_t Size > +constexpr bool operator<(const vec& xs, const vec& ys); +``` + +### Matrix Operators + +```cpp +// -operator + +template < typename T, size_t Size > +constexpr mat operator-(const mat& xs); + +// operator+ + +template < typename T, size_t Size > +constexpr mat operator+(const mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat operator+(T x, const mat& ys); + +template < typename T, size_t Size > +constexpr mat operator+(const mat& xs, const mat& ys); + +// operator+= + +template < typename T, size_t Size > +constexpr mat& operator+=(mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat& operator+=(mat& xs, const mat& ys); + +// operator- + +template < typename T, size_t Size > +constexpr mat operator-(const mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat operator-(T x, const mat& ys); + +template < typename T, size_t Size > +constexpr mat operator-(const mat& xs, const mat& ys); + +// operator-= + +template < typename T, size_t Size > +constexpr mat& operator-=(mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat& operator-=(mat& xs, const mat& ys); + +// operator* + +template < typename T, size_t Size > +constexpr mat operator*(const mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat operator*(T x, const mat& ys); + +template < typename T, size_t Size > +constexpr vec operator*(const vec& xs, const mat& ys); + +template < typename T, size_t Size > +constexpr mat operator*(const mat& xs, const mat& ys); + +// operator*= + +template < typename T, size_t Size > +constexpr mat& operator*=(mat& xs, T y); + +template < typename T, size_t Size > +constexpr vec& operator*=(vec& xs, const mat& ys); + +template < typename T, size_t Size > +constexpr mat& operator*=(mat& xs, const mat& ys); + +// operator/ + +template < typename T, size_t Size > +constexpr mat operator/(const mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat operator/(T x, const mat& ys); + +template < typename T, size_t Size > +constexpr mat operator/(const mat& xs, const mat& ys); + +// operator/= + +template < typename T, size_t Size > +constexpr mat& operator/=(mat& xs, T y); + +template < typename T, size_t Size > +constexpr mat& operator/=(mat& xs, const mat& ys); + +// operator== + +template < typename T, size_t Size > +constexpr bool operator==(const mat& xs, const mat& ys); + +template < typename T, size_t Size > +constexpr bool operator!=(const mat& xs, const mat& ys); + +// operator< + +template < typename T, size_t Size > +constexpr bool operator<(const mat& xs, const mat& ys); +``` + +### Angle and Trigonometry Functions + +```cpp +// Scalar + +template < floating_point T > +constexpr T radians(T degrees) noexcept; + +template < floating_point T > +constexpr T degrees(T radians) noexcept; + +template < floating_point T > +T sin(T x) noexcept; + +template < floating_point T > +T cos(T x) noexcept; + +template < floating_point T > +T tan(T x) noexcept; + +template < floating_point T > +T asin(T x) noexcept; + +template < floating_point T > +T acos(T x) noexcept; + +template < floating_point T > +T atan(T x) noexcept; + +template < floating_point T > +T atan2(T y, T x) noexcept; + +template < floating_point T > +T sinh(T x) noexcept; + +template < floating_point T > +T cosh(T x) noexcept; + +template < floating_point T > +T tanh(T x) noexcept; + +template < floating_point T > +T asinh(T x) noexcept; + +template < floating_point T > +T acosh(T x) noexcept; + +template < floating_point T > +T atanh(T x) noexcept; + +// Vector + +template < typename T, size_t Size > +constexpr vec radians(const vec& degrees); + +template < typename T, size_t Size > +constexpr vec degrees(const vec& radians); + +template < typename T, size_t Size > +vec sin(const vec& xs); + +template < typename T, size_t Size > +vec cos(const vec& xs); + +template < typename T, size_t Size > +vec tan(const vec& xs); + +template < typename T, size_t Size > +vec asin(const vec& xs); + +template < typename T, size_t Size > +vec acos(const vec& xs); + +template < typename T, size_t Size > +vec atan(const vec& xs); + +template < typename T, size_t Size > +vec atan2(const vec& ys, const vec& xs); + +template < typename T, size_t Size > +vec sinh(const vec& xs); + +template < typename T, size_t Size > +vec cosh(const vec& xs); + +template < typename T, size_t Size > +vec tanh(const vec& xs); + +template < typename T, size_t Size > +vec asinh(const vec& xs); + +template < typename T, size_t Size > +vec acosh(const vec& xs); + +template < typename T, size_t Size > +vec atanh(const vec& xs); +``` + +### Exponential Functions + +```cpp +// Scalar + +template < floating_point T > +T pow(T x, T y) noexcept; + +template < floating_point T > +T exp(T x) noexcept; + +template < floating_point T > +T log(T x) noexcept; + +template < floating_point T > +T exp2(T x) noexcept; + +template < floating_point T > +T log2(T x) noexcept; + +template < floating_point T > +T sqrt(T x) noexcept; + +template < floating_point T > +T rsqrt(T x) noexcept; + +// Vector + +template < typename T, size_t Size > +vec pow(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +vec exp(const vec& xs); + +template < typename T, size_t Size > +vec log(const vec& xs); + +template < typename T, size_t Size > +vec exp2(const vec& xs); + +template < typename T, size_t Size > +vec log2(const vec& xs); + +template < typename T, size_t Size > +vec sqrt(const vec& xs); + +template < typename T, size_t Size > +vec rsqrt(const vec& xs); +``` + +### Common Functions + +```cpp +// Scalar + +template < arithmetic T > +constexpr T abs(T x) noexcept; + +template < arithmetic T > +constexpr T sign(T x) noexcept; + +template < floating_point T > +constexpr T reciprocal(T x) noexcept; + +template < floating_point T > +T floor(T x) noexcept; + +template < floating_point T > +T trunc(T x) noexcept; + +template < floating_point T > +T round(T x) noexcept; + +template < floating_point T > +T ceil(T x) noexcept; + +template < floating_point T > +T fract(T x) noexcept; + +template < floating_point T > +T fmod(T x, T y) noexcept; + +template < floating_point T > +T modf(T x, T* y) noexcept; + +template < arithmetic T > +constexpr T min(T x, T y) noexcept; + +template < arithmetic T > +constexpr T max(T x, T y) noexcept; + +template < arithmetic T > +constexpr T clamp(T x, T min_x, T max_x) noexcept; + +template < arithmetic T > +constexpr T saturate(T x) noexcept; + +template < floating_point T > +constexpr T lerp(T x, T y, T a) noexcept; + +template < floating_point T > +constexpr T step(T edge, T x) noexcept; + +template < floating_point T > +constexpr T smoothstep(T edge0, T edge1, T x) noexcept; + +template < arithmetic T > +bool isnan(T x) noexcept; + +template < arithmetic T > +bool isinf(T x) noexcept; + +template < arithmetic T > +bool isfinite(T x) noexcept; + +template < floating_point T > +T fma(T x, T y, T z) noexcept; + +template < floating_point T > +T frexp(T x, int* exp) noexcept; + +template < floating_point T > +T ldexp(T x, int exp) noexcept; + +// Vector + +template < typename T, size_t Size > +constexpr vec abs(const vec& xs); + +template < typename T, size_t Size > +constexpr vec sign(const vec& xs); + +template < typename T, size_t Size > +constexpr vec reciprocal(const vec& xs); + +template < typename T, size_t Size > +vec floor(const vec& xs); + +template < typename T, size_t Size > +vec trunc(const vec& xs); + +template < typename T, size_t Size > +vec round(const vec& xs); + +template < typename T, size_t Size > +vec ceil(const vec& xs); + +template < typename T, size_t Size > +vec fract(const vec& xs); + +template < typename T, size_t Size > +vec fmod(const vec& xs, T y); + +template < typename T, size_t Size > +vec fmod(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +vec modf(const vec& xs, vec* is); + +template < typename T, size_t Size > +constexpr T min(const vec& xs); + +template < typename T, size_t Size > +constexpr vec min(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec min(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr T max(const vec& xs); + +template < typename T, size_t Size > +constexpr vec max(const vec& xs, T y); + +template < typename T, size_t Size > +constexpr vec max(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec clamp(const vec& xs, T min_x, T max_x); + +template < typename T, size_t Size > +constexpr vec clamp(const vec& xs, const vec& min_xs, const vec& max_xs); + +template < typename T, size_t Size > +constexpr vec saturate(const vec& xs); + +template < typename T, size_t Size > +constexpr vec lerp(const vec& xs, const vec& ys, T a); + +template < typename T, size_t Size > +constexpr vec lerp(const vec& xs, const vec& ys, const vec& as); + +template < typename T, size_t Size > +constexpr vec step(T edge, const vec& xs); + +template < typename T, size_t Size > +constexpr vec step(const vec& edges, const vec& xs); + +template < typename T, size_t Size > +constexpr vec smoothstep(T edge0, T edge1, const vec& xs); + +template < typename T, size_t Size > +constexpr vec smoothstep(const vec& edges0, const vec& edges1, const vec& xs); + +template < typename T, size_t Size > +vec isnan(const vec& xs); + +template < typename T, size_t Size > +vec isinf(const vec& xs); + +template < typename T, size_t Size > +vec isfinite(const vec& xs); + +template < typename T, size_t Size > +vec fma(const vec& as, const vec& bs, const vec& cs); + +template < typename T, size_t Size > +vec frexp(const vec& xs, vec* exps); + +template < typename T, size_t Size > +vec ldexp(const vec& xs, const vec& exps); +``` + +### Geometric Functions + +```cpp +// Scalar + +template < arithmetic T > +constexpr T dot(T x, T y) noexcept; + +template < arithmetic T > +constexpr T length(T x) noexcept; + +template < arithmetic T > +constexpr T length2(T x) noexcept; + +template < arithmetic T > +constexpr T distance(T x, T y) noexcept; + +template < arithmetic T > +constexpr T distance2(T x, T y) noexcept; + +template < floating_point T > +T normalize(T x) noexcept; + +template < floating_point T > +constexpr T faceforward(T n, T i, T nref) noexcept; + +template < floating_point T > +constexpr T reflect(T i, T n) noexcept; + +template < floating_point T > +T refract(T i, T n, T eta) noexcept; + +// Vector + +template < typename T, size_t Size > +constexpr T dot(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +T length(const vec& xs); + +template < typename T, size_t Size > +constexpr T length2(const vec& xs); + +template < typename T, size_t Size > +T distance(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr T distance2(const vec& xs, const vec& ys); + +template < typename T > +constexpr T cross(const vec& xs, const vec& ys); + +template < typename T > +constexpr vec cross(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +vec normalize(const vec& xs); + +template < typename T, size_t Size > +constexpr vec faceforward(const vec& n, const vec& i, const vec& nref); + +template < typename T, size_t Size > +constexpr vec reflect(const vec& i, const vec& n); + +template < typename T, size_t Size > +vec refract(const vec& i, const vec& n, T eta); +``` + +### Relational Functions + +```cpp +// Scalar + +template < arithmetic T > +constexpr bool less(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool less_equal(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool greater(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool greater_equal(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool equal_to(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool equal_to(T x, T y, T epsilon) noexcept; + +template < arithmetic T > +constexpr bool not_equal_to(T x, T y) noexcept; + +template < arithmetic T > +constexpr bool not_equal_to(T x, T y, T epsilon) noexcept; + +template < arithmetic T > +constexpr bool any(T x) noexcept; + +template < arithmetic T > +constexpr bool all(T x) noexcept; + +// Vector + +template < typename T, size_t Size > +constexpr vec less(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec less_equal(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec greater(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec greater_equal(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec equal_to(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec equal_to(const vec& xs, const vec& ys, T epsilon); + +template < typename T, size_t Size > +constexpr vec not_equal_to(const vec& xs, const vec& ys); + +template < typename T, size_t Size > +constexpr vec not_equal_to(const vec& xs, const vec& ys, T epsilon); + +template < typename T, size_t Size > +constexpr bool any(const vec& xs); + +template < typename T, size_t Size > +constexpr bool all(const vec& xs); +``` + +### Matrix Functions + +```cpp +template < typename T > +constexpr mat transpose(const mat& m); + +template < typename T > +constexpr mat transpose(const mat& m); + +template < typename T > +constexpr mat transpose(const mat& m); + +template < typename T > +constexpr T determinant(const mat& m); + +template < typename T > +constexpr T determinant(const mat& m); + +template < typename T > +constexpr T determinant(const mat& m); + +template < typename T > +constexpr mat inverse(const mat& m); + +template < typename T > +constexpr mat inverse(const mat& m); + +template < typename T > +constexpr mat inverse(const mat& m); +``` + +### Units + +```cpp +template < typename T > inline constexpr vec zero2; +template < typename T > inline constexpr vec zero3; +template < typename T > inline constexpr vec zero4; + +template < typename T > inline constexpr vec unit2; +template < typename T > inline constexpr vec unit3; +template < typename T > inline constexpr vec unit4; + +template < typename T > inline constexpr vec unit2_x; +template < typename T > inline constexpr vec unit2_y; + +template < typename T > inline constexpr vec unit3_x; +template < typename T > inline constexpr vec unit3_y; +template < typename T > inline constexpr vec unit3_z; + +template < typename T > inline constexpr vec unit4_x; +template < typename T > inline constexpr vec unit4_y; +template < typename T > inline constexpr vec unit4_z; +template < typename T > inline constexpr vec unit4_w; + +template < typename T > inline constexpr mat zero2x2; +template < typename T > inline constexpr mat zero3x3; +template < typename T > inline constexpr mat zero4x4; + +template < typename T > inline constexpr mat unit2x2; +template < typename T > inline constexpr mat unit3x3; +template < typename T > inline constexpr mat unit4x4; + +template < typename T > inline constexpr mat identity2x2; +template < typename T > inline constexpr mat identity3x3; +template < typename T > inline constexpr mat identity4x4; +``` + +### Cast + +```cpp +template < arithmetic To, arithmetic From > +constexpr To cast_to(From x) noexcept; + +template < typename To, typename From, size_t Size > +constexpr vec cast_to(const vec& v); + +template < typename To, typename From, size_t Size > +constexpr mat cast_to(const mat& m); +``` + +### Access + +```cpp +template < typename T, size_t Size > +constexpr T component(const vec& v, size_t index); + +template < typename T, size_t Size > +constexpr vec component(vec v, size_t index, T x); + +template < typename T, size_t Size > +constexpr vec row(const mat& m, size_t index); + +template < typename T, size_t Size > +constexpr mat row(mat m, size_t index, const vec& v); + +template < typename T, size_t Size > +constexpr vec column(const mat& m, size_t index); + +template < typename T, size_t Size > +constexpr mat column(const mat& m, size_t index, const vec& v); +``` + +### Matrix Transform 3D + +```cpp +template < typename T > +constexpr mat translate(const vec& v); + +template < typename T > +constexpr mat translate(const mat& m, const vec& v); + +template < typename T > +mat rotate(T angle, const vec& axis); + +template < typename T > +mat rotate(const mat& m, T angle, const vec& axis); + +template < typename T > +constexpr mat scale(const vec& v); + +template < typename T > +constexpr mat scale(const mat& m, const vec& v); + +template < typename T > +mat look_at_lh(const vec& eye, const vec& at, const vec& up); + +template < typename T > +mat look_at_rh(const vec& eye, const vec& at, const vec& up); +``` + +### Matrix Transform 2D + +```cpp +template < typename T > +constexpr mat translate(const vec& v); + +template < typename T > +constexpr mat translate(const mat& m, const vec& v); + +template < typename T > +mat rotate(T angle); + +template < typename T > +mat rotate(const mat& m, T angle); + +template < typename T > +constexpr mat scale(const vec& v); + +template < typename T > +constexpr mat scale(const mat& m, const vec& v); + +template < typename T > +constexpr mat shear(const vec& v); + +template < typename T > +constexpr mat shear(const mat& m, const vec& v); + +template < typename T > +constexpr mat shear_x(T y); + +template < typename T > +constexpr mat shear_x(const mat& m, T y); + +template < typename T > +constexpr mat shear_y(T x); + +template < typename T > +constexpr mat shear_y(const mat& m, T x); +``` + +### Matrix Projections + +```cpp +template < typename T > +mat orthographic_lh_zo(T left, T right, T bottom, T top, T znear, T zfar); + +template < typename T > +mat orthographic_lh_no(T left, T right, T bottom, T top, T znear, T zfar); + +template < typename T > +mat orthographic_rh_zo(T left, T right, T bottom, T top, T znear, T zfar); + +template < typename T > +mat orthographic_rh_no(T left, T right, T bottom, T top, T znear, T zfar); + +template < typename T > +mat perspective_lh_zo(T fov, T aspect, T znear, T zfar); + +template < typename T > +mat perspective_lh_no(T fov, T aspect, T znear, T zfar); + +template < typename T > +mat perspective_rh_zo(T fov, T aspect, T znear, T zfar); + +template < typename T > +mat perspective_rh_no(T fov, T aspect, T znear, T zfar); +``` + +### Vector Transform + +```cpp +template < typename T, size_t Size > +T angle(const vec& x, const vec& y); + +template < typename T > +vec rotate(const vec& v, T angle); + +template < typename T > +vec rotate(const vec& v, T angle, const vec& normal); + +template < typename T > +vec rotate(const vec& v, T angle, const vec& normal); +``` + ## [License (MIT)](./LICENSE.md) diff --git a/headers/vmath.hpp/vmath.hpp b/headers/vmath.hpp/vmath.hpp index 823789d..4151a03 100644 --- a/headers/vmath.hpp/vmath.hpp +++ b/headers/vmath.hpp/vmath.hpp @@ -6,6 +6,13 @@ #pragma once -namespace vmath_hpp -{ -} +#include "vmath_fwd.hpp" + +#include "vmath_fun.hpp" +#include "vmath_ext.hpp" + +#include "vmath_mat.hpp" +#include "vmath_mat_fun.hpp" + +#include "vmath_vec.hpp" +#include "vmath_vec_fun.hpp" diff --git a/headers/vmath.hpp/vmath_ext.hpp b/headers/vmath.hpp/vmath_ext.hpp new file mode 100644 index 0000000..406e4e0 --- /dev/null +++ b/headers/vmath.hpp/vmath_ext.hpp @@ -0,0 +1,539 @@ +/******************************************************************************* + * 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" + +#include "vmath_fun.hpp" +#include "vmath_vec_fun.hpp" +#include "vmath_mat_fun.hpp" + +// +// Units +// + +namespace vmath_hpp +{ + template < typename T > inline constexpr vec zero2{0, 0}; + template < typename T > inline constexpr vec zero3{0, 0, 0}; + template < typename T > inline constexpr vec zero4{0, 0, 0, 0}; + + template < typename T > inline constexpr vec unit2{1, 1}; + template < typename T > inline constexpr vec unit3{1, 1, 1}; + template < typename T > inline constexpr vec unit4{1, 1, 1, 1}; + + template < typename T > inline constexpr vec unit2_x{1, 0}; + template < typename T > inline constexpr vec unit2_y{0, 1}; + + template < typename T > inline constexpr vec unit3_x{1, 0, 0}; + template < typename T > inline constexpr vec unit3_y{0, 1, 0}; + template < typename T > inline constexpr vec unit3_z{0, 0, 1}; + + template < typename T > inline constexpr vec unit4_x{1, 0, 0, 0}; + template < typename T > inline constexpr vec unit4_y{0, 1, 0, 0}; + template < typename T > inline constexpr vec unit4_z{0, 0, 1, 0}; + template < typename T > inline constexpr vec unit4_w{0, 0, 0, 1}; + + template < typename T > inline constexpr mat zero2x2{0, 0, 0, 0}; + template < typename T > inline constexpr mat zero3x3{0, 0, 0, 0, 0, 0, 0, 0, 0}; + template < typename T > inline constexpr mat zero4x4{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + template < typename T > inline constexpr mat unit2x2{1, 1, 1, 1}; + template < typename T > inline constexpr mat unit3x3{1, 1, 1, 1, 1, 1, 1, 1, 1}; + template < typename T > inline constexpr mat unit4x4{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + + template < typename T > inline constexpr mat identity2x2{1, 0, 0, 1}; + template < typename T > inline constexpr mat identity3x3{1, 0, 0, 0, 1, 0, 0, 0, 1}; + template < typename T > inline constexpr mat identity4x4{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; +} + +// +// Hash +// + +namespace vmath_hpp::detail +{ + struct hash_combiner { + template < typename T > + [[nodiscard]] std::size_t operator()(std::size_t seed, const T& x) noexcept { + return (seed ^= std::hash{}(x) + 0x9e3779b9 + (seed << 6) + ( seed >> 2)); + } + }; + + template < typename T, size_t Size > + [[nodiscard]] std::size_t hash(const vec& v) noexcept { + return fold(hash_combiner{}, std::size_t{}, v); + } + + template < typename T, size_t Size > + [[nodiscard]] std::size_t hash(const mat& m) noexcept { + return fold(hash_combiner{}, std::size_t{}, m); + } +} + +namespace std +{ + template < typename T, size_t Size > + struct hash> { + size_t operator()(const vmath_hpp::vec& v) const noexcept { + return vmath_hpp::detail::hash(v); + } + }; + + template < typename T, size_t Size > + struct hash> { + size_t operator()(const vmath_hpp::mat& m) const noexcept { + return vmath_hpp::detail::hash(m); + } + }; +} + +// +// Cast +// + +namespace vmath_hpp +{ + template < typename To, typename From > + [[nodiscard]] std::enable_if_t< + std::is_arithmetic_v && std::is_arithmetic_v + , To> + constexpr cast_to(From x) noexcept { + return static_cast(x); + } + + template < typename To, typename From, std::size_t Size > + [[nodiscard]] constexpr vec cast_to(const vec& v) { + return detail::map([](From x){ return cast_to(x); }, v); + } + + template < typename To, typename From, std::size_t Size > + [[nodiscard]] constexpr mat cast_to(const mat& m) { + return detail::map([](const vec& v){ return cast_to(v); }, m); + } +} + +// +// Access +// + +namespace vmath_hpp +{ + // component + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T component(const vec& v, std::size_t index) { + return v[index]; + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec component(vec v, std::size_t index, T x) { + v[index] = x; + return v; + } + + // row + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec row(const mat& m, std::size_t index) { + return m.rows[index]; + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat row(mat m, std::size_t index, const vec& v) { + m.rows[index] = v; + return m; + } + + // column + + namespace impl + { + template < typename T, std::size_t Size, std::size_t... Is > + [[nodiscard]] constexpr vec column_impl(const mat& m, std::size_t index, std::index_sequence) { + return { m[Is][index]... }; + } + + template < typename T, std::size_t Size, std::size_t... Is > + [[nodiscard]] constexpr mat column_impl(const mat& m, std::size_t index, const vec& v, std::index_sequence) { + return { component(m[Is], index, v[Is])... }; + } + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec column(const mat& m, std::size_t index) { + return impl::column_impl(m, index, std::make_index_sequence{}); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat column(const mat& m, std::size_t index, const vec& v) { + return impl::column_impl(m, index, v, std::make_index_sequence{}); + } +} + +// +// Matrix Transform 3D +// + +namespace vmath_hpp +{ + // translate + + template < typename T > + [[nodiscard]] constexpr mat translate(const vec& v) { + return { + { 1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 1, 0}, + {v.x, v.y, v.z, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat translate(const mat& m, const vec& v) { + return m * translate(v); + } + + // rotate + + template < typename T > + [[nodiscard]] mat rotate(T angle, const vec& axis) { + const T x = axis.x; + const T y = axis.y; + const T z = axis.z; + const T px = x * x; + const T py = y * y; + const T pz = z * z; + const T cs = cos(angle); + const T sn = sin(angle); + const T ics = T(1) - cs; + const T xym = x * y * ics; + const T xzm = x * z * ics; + const T yzm = y * z * ics; + const T xsn = x * sn; + const T ysn = y * sn; + const T zsn = z * sn; + return { + px * ics + cs, xym + zsn, xzm - ysn, 0, + xym - zsn, py * ics + cs, yzm + xsn, 0, + xzm + ysn, yzm - xsn, pz * ics + cs, 0, + 0, 0, 0, 1}; + } + + template < typename T > + [[nodiscard]] mat rotate(const mat& m, T angle, const vec& axis) { + return m * rotate(angle, axis); + } + + // scale + + template < typename T > + [[nodiscard]] constexpr mat scale(const vec& v) { + return { + {v.x, 0, 0, 0}, + { 0, v.y, 0, 0}, + { 0, 0, v.z, 0}, + { 0, 0, 0, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat scale(const mat& m, const vec& v) { + return m * scale(v); + } + + // look_at + + template < typename T > + [[nodiscard]] mat look_at_lh(const vec& eye, const vec& at, const vec& up) { + const vec az = normalize(at - eye); + const vec ax = normalize(cross(up, az)); + const vec ay = cross(az, ax); + const T dx = dot(ax, eye); + const T dy = dot(ay, eye); + const T dz = dot(az, eye); + return { + ax.x, ay.x, az.x, 0, + ax.y, ay.y, az.y, 0, + ax.z, ay.z, az.z, 0, + -dx, -dy, -dz, 1}; + } + + template < typename T > + [[nodiscard]] mat look_at_rh(const vec& eye, const vec& at, const vec& up) { + const vec az = normalize(eye - at); + const vec ax = normalize(cross(up, az)); + const vec ay = cross(az, ax); + const T dx = dot(ax, eye); + const T dy = dot(ay, eye); + const T dz = dot(az, eye); + return { + ax.x, ay.x, az.x, 0, + ax.y, ay.y, az.y, 0, + ax.z, ay.z, az.z, 0, + -dx, -dy, -dz, 1}; + } +} + +// +// Matrix Transform 2D +// + +namespace vmath_hpp +{ + // translate + + template < typename T > + [[nodiscard]] constexpr mat translate(const vec& v) { + return { + { 1, 0, 0}, + { 0, 1, 0}, + {v.x, v.y, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat translate(const mat& m, const vec& v) { + return m * translate(v); + } + + // rotate + + template < typename T > + [[nodiscard]] mat rotate(T angle) { + const T cs = cos(angle); + const T sn = sin(angle); + return { + cs, sn, 0, + -sn, cs, 0, + 0, 0, 1}; + } + + template < typename T > + [[nodiscard]] mat rotate(const mat& m, T angle) { + return m * rotate(angle); + } + + // scale + + template < typename T > + [[nodiscard]] constexpr mat scale(const vec& v) { + return { + {v.x, 0, 0}, + { 0, v.y, 0}, + { 0, 0, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat scale(const mat& m, const vec& v) { + return m * scale(v); + } + + // shear + + template < typename T > + [[nodiscard]] constexpr mat shear(const vec& v) { + return { + { 1, v.y, 0}, + {v.x, 1, 0}, + { 0, 0, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat shear(const mat& m, const vec& v) { + return m * shear(v); + } + + template < typename T > + [[nodiscard]] constexpr mat shear_x(T y) { + return { + {1, 0, 0}, + {y, 1, 0}, + {0, 0, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat shear_x(const mat& m, T y) { + return m * shear_x(y); + } + + template < typename T > + [[nodiscard]] constexpr mat shear_y(T x) { + return { + {1, x, 0}, + {0, 1, 0}, + {0, 0, 1}}; + } + + template < typename T > + [[nodiscard]] constexpr mat shear_y(const mat& m, T x) { + return m * shear_y(x); + } +} + +// +// Matrix Projections +// + +namespace vmath_hpp +{ + // orthographic + + template < typename T > + [[nodiscard]] mat orthographic_lh_zo(T left, T right, T bottom, T top, T znear, T zfar) { + const T sx = T(2) / (right - left); + const T sy = T(2) / (top - bottom); + const T sz = T(1) / (zfar - znear); + + const T tx = - (right + left) / (right - left); + const T ty = - (top + bottom) / (top - bottom); + const T tz = - znear / (zfar - znear); + + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + tx, ty, tz, 1}; + } + + template < typename T > + [[nodiscard]] mat orthographic_lh_no(T left, T right, T bottom, T top, T znear, T zfar) { + const T sx = T(2) / (right - left); + const T sy = T(2) / (top - bottom); + const T sz = T(2) / (zfar - znear); + + const T tx = - (right + left) / (right - left); + const T ty = - (top + bottom) / (top - bottom); + const T tz = - (zfar + znear) / (zfar - znear); + + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + tx, ty, tz, 1}; + } + + template < typename T > + [[nodiscard]] mat orthographic_rh_zo(T left, T right, T bottom, T top, T znear, T zfar) { + const T sx = T(2) / (right - left); + const T sy = T(2) / (top - bottom); + const T sz = -T(1) / (zfar - znear); + + const T tx = - (right + left) / (right - left); + const T ty = - (top + bottom) / (top - bottom); + const T tz = - znear / (zfar - znear); + + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + tx, ty, tz, 1}; + } + + template < typename T > + [[nodiscard]] mat orthographic_rh_no(T left, T right, T bottom, T top, T znear, T zfar) { + const T sx = T(2) / (right - left); + const T sy = T(2) / (top - bottom); + const T sz = -T(2) / (zfar - znear); + + const T tx = - (right + left) / (right - left); + const T ty = - (top + bottom) / (top - bottom); + const T tz = - (zfar + znear) / (zfar - znear); + + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + tx, ty, tz, 1}; + } + + // perspective + + template < typename T > + [[nodiscard]] mat perspective_lh_zo(T fov, T aspect, T znear, T zfar) { + const T sy = T(1) / tan(fov * T(0.5)); + const T sx = sy / aspect; + const T sz = zfar / (zfar - znear); + const T tz = (znear * zfar) / (znear - zfar); + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 1, + 0, 0, tz, 0}; + } + + template < typename T > + [[nodiscard]] mat perspective_lh_no(T fov, T aspect, T znear, T zfar) { + const T sy = T(1) / tan(fov * T(0.5)); + const T sx = sy / aspect; + const T sz = (zfar + znear) / (zfar - znear); + const T tz = (T(2) * znear * zfar) / (znear - zfar); + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 1, + 0, 0, tz, 0}; + } + + template < typename T > + [[nodiscard]] mat perspective_rh_zo(T fov, T aspect, T znear, T zfar) { + const T sy = T(1) / tan(fov * T(0.5)); + const T sx = sy / aspect; + const T sz = zfar / (znear - zfar); + const T tz = (znear * zfar) / (znear - zfar); + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, -1, + 0, 0, tz, 0}; + } + + template < typename T > + [[nodiscard]] mat perspective_rh_no(T fov, T aspect, T znear, T zfar) { + const T sy = T(1) / tan(fov * T(0.5)); + const T sx = sy / aspect; + const T sz = (zfar + znear) / (znear - zfar); + const T tz = (T(2) * znear * zfar) / (znear - zfar); + return { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, -1, + 0, 0, tz, 0}; + } +} + +// +// Vector Transform +// + +namespace vmath_hpp +{ + // angle + + template < typename T, std::size_t Size > + [[nodiscard]] T angle(const vec& x, const vec& y) { + return acos(dot(x, y) * rsqrt(length2(x) * length2(y))); + } + + // rotate + + template < typename T > + [[nodiscard]] vec rotate(const vec& v, T angle) { + const T cs = cos(angle); + const T sn = sin(angle); + return { + v.x * cs - v.y * sn, + v.x * sn + v.y * cs}; + } + + template < typename T > + [[nodiscard]] vec rotate(const vec& v, T angle, const vec& normal) { + return v * mat(rotate(angle, normal)); + } + + template < typename T > + [[nodiscard]] vec rotate(const vec& v, T angle, const vec& normal) { + return v * rotate(angle, normal); + } +} diff --git a/headers/vmath.hpp/vmath_fun.hpp b/headers/vmath.hpp/vmath_fun.hpp new file mode 100644 index 0000000..7e8ba90 --- /dev/null +++ b/headers/vmath.hpp/vmath_fun.hpp @@ -0,0 +1,439 @@ +/******************************************************************************* + * 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 > + [[nodiscard]] std::enable_if_t, T> + constexpr radians(T degrees) noexcept { + return degrees * T(0.01745329251994329576923690768489); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr degrees(T radians) noexcept { + return radians * T(57.295779513082320876798154814105); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + sin(T x) noexcept { + return std::sin(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + cos(T x) noexcept { + return std::cos(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + tan(T x) noexcept { + return std::tan(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + asin(T x) noexcept { + return std::asin(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + acos(T x) noexcept { + return std::acos(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + atan(T x) noexcept { + return std::atan(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + atan2(T y, T x) noexcept { + return std::atan2(y, x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + sinh(T x) noexcept { + return std::sinh(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + cosh(T x) noexcept { + return std::cosh(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + tanh(T x) noexcept { + return std::tanh(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + asinh(T x) noexcept { + return std::asinh(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + acosh(T x) noexcept { + return std::acosh(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + atanh(T x) noexcept { + return std::atanh(x); + } +} + +// +// Exponential Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] std::enable_if_t, T> + pow(T x, T y) noexcept { + return std::pow(x, y); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + exp(T x) noexcept { + return std::exp(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + log(T x) noexcept { + return std::log(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + exp2(T x) noexcept { + return std::exp2(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + log2(T x) noexcept { + return std::log2(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + sqrt(T x) noexcept { + return std::sqrt(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + rsqrt(T x) noexcept { + return T(1) / sqrt(x); + } +} + +// +// Common Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr abs(T x) noexcept { + return x; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr abs(T x) noexcept { + return x >= T(0) ? x : -x; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr sign(T x) noexcept { + return static_cast((T(0) < x) - (x < T(0))); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr reciprocal(T x) noexcept { + return T(1) / x; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + floor(T x) noexcept { + return std::floor(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + trunc(T x) noexcept { + return std::trunc(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + round(T x) noexcept { + return std::round(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + ceil(T x) noexcept { + return std::ceil(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + fract(T x) noexcept { + return x - floor(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + fmod(T x, T y) noexcept { + return std::fmod(x, y); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + modf(T x, T* y) noexcept { + return std::modf(x, y); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr min(T x, T y) noexcept { + return std::min(x, y); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr max(T x, T y) noexcept { + return std::max(x, y); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr clamp(T x, T min_x, T max_x) noexcept { + return min(max(x, min_x), max_x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr saturate(T x) noexcept { + return clamp(x, T(0), T(1)); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr lerp(T x, T y, T a) noexcept { + return x * (T(1) - a) + y * a; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr step(T edge, T x) noexcept { + return x < edge ? T(0) : T(1); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr 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); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + isnan(T x) noexcept { + return std::isnan(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + isinf(T x) noexcept { + return std::isinf(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + isfinite(T x) noexcept { + return std::isfinite(x); + } + + // + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + fma(T x, T y, T z) noexcept { + return std::fma(x, y, z); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + frexp(T x, int* exp) noexcept { + return std::frexp(x, exp); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + ldexp(T x, int exp) noexcept { + return std::ldexp(x, exp); + } +} + +// +// Geometric Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr dot(T x, T y) noexcept { + return x * y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr length(T x) noexcept { + return abs(x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr length2(T x) noexcept { + return dot(x, x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr distance(T x, T y) noexcept { + return length(y - x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr distance2(T x, T y) noexcept { + return length2(y - x); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + normalize(T x) noexcept { + return x * rsqrt(dot(x, x)); + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr faceforward(T n, T i, T nref) noexcept { + return dot(nref, i) < T(0) ? n : -n; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, T> + constexpr reflect(T i, T n) noexcept { + return i - n * dot(n, i) * T(2); + } + + template < typename T > + [[nodiscard]] std::enable_if_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); + } +} + +// +// Scalar Relational Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr less(T x, T y) noexcept { + return x < y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr less_equal(T x, T y) noexcept { + return x <= y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr greater(T x, T y) noexcept { + return x > y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr greater_equal(T x, T y) noexcept { + return x >= y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr equal_to(T x, T y) noexcept { + return x == y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr equal_to(T x, T y, T epsilon) noexcept { + return abs(x - y) <= epsilon; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr not_equal_to(T x, T y) noexcept { + return x != y; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr not_equal_to(T x, T y, T epsilon) noexcept { + return abs(x - y) > epsilon; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr any(T x) noexcept { + return !!x; + } + + template < typename T > + [[nodiscard]] std::enable_if_t, bool> + constexpr all(T x) noexcept { + return !!x; + } +} diff --git a/headers/vmath.hpp/vmath_fwd.hpp b/headers/vmath.hpp/vmath_fwd.hpp new file mode 100644 index 0000000..01f8ea6 --- /dev/null +++ b/headers/vmath.hpp/vmath_fwd.hpp @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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 +#include + +#include +#include +#include +#include +#include + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class vec; + + using bool2 = vec; + using bool3 = vec; + using bool4 = vec; + + using int2 = vec; + using int3 = vec; + using int4 = vec; + + using uint2 = vec; + using uint3 = vec; + using uint4 = vec; + + using float2 = vec; + using float3 = vec; + using float4 = vec; + + using double2 = vec; + using double3 = vec; + using double4 = vec; + + using size2 = vec; + using size3 = vec; + using size4 = vec; + + using ptrdiff2 = vec; + using ptrdiff3 = vec; + using ptrdiff4 = vec; +} + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class mat; + + using bool2x2 = mat; + using bool3x3 = mat; + using bool4x4 = mat; + + using int2x2 = mat; + using int3x3 = mat; + using int4x4 = mat; + + using uint2x2 = mat; + using uint3x3 = mat; + using uint4x4 = mat; + + using float2x2 = mat; + using float3x3 = mat; + using float4x4 = mat; + + using double2x2 = mat; + using double3x3 = mat; + using double4x4 = mat; + + using size2x2 = mat; + using size3x3 = mat; + using size4x4 = mat; + + using ptrdiff2x2 = mat; + using ptrdiff3x3 = mat; + using ptrdiff4x4 = mat; +} diff --git a/headers/vmath.hpp/vmath_mat.hpp b/headers/vmath.hpp/vmath_mat.hpp new file mode 100644 index 0000000..ecb7a32 --- /dev/null +++ b/headers/vmath.hpp/vmath_mat.hpp @@ -0,0 +1,224 @@ +/******************************************************************************* + * 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" + +#include "vmath_vec.hpp" +#include "vmath_vec_fun.hpp" + +namespace vmath_hpp::detail +{ + template < typename T, std::size_t Size > + class mat_base; + + template < typename T > + class mat_base { + public: + using row_type = vec; + row_type rows[2] = { + {1, 0}, + {0, 1}}; + public: + mat_base() = default; + + constexpr explicit mat_base(T v) + : rows{ + row_type{v, 0}, + row_type{0, v}} {} + + constexpr mat_base( + T m11, T m12, + T m21, T m22) + : rows{ + row_type{m11, m12}, + row_type{m21, m22}} {} + + constexpr mat_base( + const row_type& row0, + const row_type& row1) + : rows{row0, row1} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0]}, + row_type{other.rows[1]}} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0]}, + row_type{other.rows[1]}} {} + }; + + template < typename T > + class mat_base { + public: + using row_type = vec; + row_type rows[3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}}; + public: + mat_base() = default; + + constexpr explicit mat_base(T v) + : rows{ + row_type{v, 0, 0}, + row_type{0, v, 0}, + row_type{0, 0, v}} {} + + constexpr mat_base( + T m11, T m12, T m13, + T m21, T m22, T m23, + T m31, T m32, T m33) + : rows{ + row_type{m11, m12, m13}, + row_type{m21, m22, m23}, + row_type{m31, m32, m33}} {} + + constexpr mat_base( + const row_type& row0, + const row_type& row1, + const row_type& row2) + : rows{row0, row1, row2} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0], 0}, + row_type{other.rows[1], 0}, + row_type{0, 0, 1}} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0]}, + row_type{other.rows[1]}, + row_type{other.rows[2]}} {} + }; + + template < typename T > + class mat_base { + public: + using row_type = vec; + row_type rows[4] = { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}}; + public: + mat_base() = default; + + constexpr explicit mat_base(T v) + : rows{ + row_type{v, 0, 0, 0}, + row_type{0, v, 0, 0}, + row_type{0, 0, v, 0}, + row_type{0, 0, 0, v}} {} + + constexpr mat_base( + T m11, T m12, T m13, T m14, + T m21, T m22, T m23, T m24, + T m31, T m32, T m33, T m34, + T m41, T m42, T m43, T m44) + : rows{ + row_type{m11, m12, m13, m14}, + row_type{m21, m22, m23, m24}, + row_type{m31, m32, m33, m34}, + row_type{m41, m42, m43, m44}} {} + + constexpr mat_base( + const row_type& row0, + const row_type& row1, + const row_type& row2, + const row_type& row3) + : rows{row0, row1, row2, row3} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0], 0, 0}, + row_type{other.rows[1], 0, 0}, + row_type{0, 0, 1, 0}, + row_type{0, 0, 0, 1}} {} + + constexpr explicit mat_base( + const mat_base& other) + : rows{ + row_type{other.rows[0], 0}, + row_type{other.rows[1], 0}, + row_type{other.rows[2], 0}, + row_type{0, 0, 0, 1}} {} + }; +} + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class mat final : public detail::mat_base { + public: + using self_type = mat; + using base_type = detail::mat_base; + public: + using row_type = vec; + + using pointer = row_type*; + using const_pointer = const row_type*; + + using reference = row_type&; + using const_reference = const row_type&; + + static constexpr std::size_t size = Size; + public: + using base_type::mat_base; + using base_type::rows; + + mat() = default; + + mat(mat&&) = default; + mat& operator=(mat&&) = default; + + mat(const mat&) = default; + mat& operator=(const mat&) = default; + + void swap(mat& other) noexcept(std::is_nothrow_swappable_v) { + for ( std::size_t i = 0; i < Size; ++i ) { + using std::swap; + swap((*this)[i], other[i]); + } + } + + [[nodiscard]] constexpr reference operator[](std::size_t index) noexcept { + return rows[index]; + } + + [[nodiscard]] constexpr const_reference operator[](std::size_t index) const noexcept { + return rows[index]; + } + + [[nodiscard]] constexpr reference at(std::size_t index) { + if ( index >= Size ) { + throw std::out_of_range("mat::at"); + } + return rows[index]; + } + + [[nodiscard]] constexpr const_reference at(std::size_t index) const { + if ( index >= Size ) { + throw std::out_of_range("mat::at"); + } + return rows[index]; + } + }; + + template < typename T, std::size_t Size > + void swap(mat& l, mat& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } +} diff --git a/headers/vmath.hpp/vmath_mat_fun.hpp b/headers/vmath.hpp/vmath_mat_fun.hpp new file mode 100644 index 0000000..273295d --- /dev/null +++ b/headers/vmath.hpp/vmath_mat_fun.hpp @@ -0,0 +1,565 @@ +/******************************************************************************* + * 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" + +#include "vmath_fun.hpp" +#include "vmath_mat.hpp" + +#include "vmath_vec.hpp" +#include "vmath_vec_fun.hpp" + +namespace vmath_hpp::detail +{ + namespace impl + { + template < typename A, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto map_impl(F&& f, const mat& a, std::index_sequence) + -> mat>::value_type, Size> + { + return { f(a[Is])... }; + } + + template < typename A, typename B, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto zip_impl(F&& f, const mat& a, const mat& b, std::index_sequence) + -> mat, vec>::value_type, Size> + { + return { f(a[Is], b[Is])... }; + } + + template < typename A, typename B, typename C, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto zip_impl(F&& f, const mat& a, const mat& b, const mat& c, std::index_sequence) + -> mat, vec, vec>::value_type, Size> + { + return { f(a[Is], b[Is], c[Is])... }; + } + + template < typename A, typename B, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr A fold_impl(F&& f, A init, const mat& b, std::index_sequence) { + return ((init = f(std::move(init), b[Is])), ...); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr A fold_impl(F&& f, A init, const mat& b, const mat& c, std::index_sequence) { + return ((init = f(std::move(init), b[Is], c[Is])), ...); + } + + template < typename A, std::size_t Size, typename F, std::size_t I, std::size_t... Is > + [[nodiscard]] constexpr vec fold1_impl(F&& f, const mat& a, std::index_sequence) { + vec init = a[I]; + return ((init = f(std::move(init), a[Is])), ...); + } + } + + template < typename A, std::size_t Size, typename F > + [[nodiscard]] constexpr auto map(F&& f, const mat& a) + -> mat>::value_type, Size> + { + return impl::map_impl(std::forward(f), a, std::make_index_sequence{}); + } + + template < typename A, typename B, std::size_t Size, typename F > + [[nodiscard]] constexpr auto zip(F&& f, const mat& a, const mat& b) + -> mat, vec>::value_type, Size> + { + return impl::zip_impl(std::forward(f), a, b, std::make_index_sequence{}); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F > + [[nodiscard]] constexpr auto zip(F&& f, const mat& a, const mat& b, const mat& c) + -> mat, vec, vec>::value_type, Size> + { + return impl::zip_impl(std::forward(f), a, b, c, std::make_index_sequence{}); + } + + template < typename A, typename B, std::size_t Size, typename F > + [[nodiscard]] constexpr A fold(F&& f, A init, const mat& b) { + return impl::fold_impl(std::forward(f), std::move(init), b, std::make_index_sequence{}); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F > + [[nodiscard]] constexpr A fold(F&& f, A init, const mat& b, const mat& c) { + return impl::fold_impl(std::forward(f), std::move(init), b, c, std::make_index_sequence{}); + } + + template < typename A, std::size_t Size, typename F > + [[nodiscard]] constexpr vec fold1(F&& f, const mat& a) { + return impl::fold1_impl(std::forward(f), a, std::make_index_sequence{}); + } +} + +// +// Operators +// + +namespace vmath_hpp +{ + // -operator + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator-(const mat& xs) { + return map(std::negate<>(), xs); + } + + // operator+ + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator+(const mat& xs, T y) { + return map([y](const vec& x){ return x + y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator+(T x, const mat& ys) { + return map([x](const vec& y){ return x + y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator+(const mat& xs, const mat& ys) { + return zip(std::plus<>(), xs, ys); + } + + // operator+= + + template < typename T, std::size_t Size > + constexpr mat& operator+=(mat& xs, T y) { + return (xs = xs + y); + } + + template < typename T, std::size_t Size > + constexpr mat& operator+=(mat& xs, const mat& ys) { + return (xs = xs + ys); + } + + // operator- + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator-(const mat& xs, T y) { + return map([y](const vec& x){ return x - y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator-(T x, const mat& ys) { + return map([x](const vec& y){ return x - y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator-(const mat& xs, const mat& ys) { + return zip(std::minus<>(), xs, ys); + } + + // operator-= + + template < typename T, std::size_t Size > + constexpr mat& operator-=(mat& xs, T y) { + return (xs = xs - y); + } + + template < typename T, std::size_t Size > + constexpr mat& operator-=(mat& xs, const mat& ys) { + return (xs = xs - ys); + } + + // operator* + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator*(const mat& xs, T y) { + return map([y](const vec& x){ return x * y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator*(T x, const mat& ys) { + return map([x](const vec& y){ return x * y; }, ys); + } + + template < typename T > + [[nodiscard]] constexpr vec operator*(const vec& xs, const mat& ys) { + return { + xs.x * ys[0][0] + xs.y * ys[1][0], + xs.x * ys[0][1] + xs.y * ys[1][1]}; + } + + template < typename T > + [[nodiscard]] constexpr mat operator*(const mat& xs, const mat& ys) { + return { + xs[0][0] * ys[0][0] + xs[0][1] * ys[1][0], + xs[0][0] * ys[0][1] + xs[0][1] * ys[1][1], + + xs[1][0] * ys[0][0] + xs[1][1] * ys[1][0], + xs[1][0] * ys[0][1] + xs[1][1] * ys[1][1]}; + } + + template < typename T > + [[nodiscard]] constexpr vec operator*(const vec& xs, const mat& ys) { + return { + xs.x * ys[0][0] + xs.y * ys[1][0] + xs.z * ys[2][0], + xs.x * ys[0][1] + xs.y * ys[1][1] + xs.z * ys[2][1], + xs.x * ys[0][2] + xs.y * ys[1][2] + xs.z * ys[2][2]}; + } + + template < typename T > + [[nodiscard]] constexpr mat operator*(const mat& xs, const mat& ys) { + return { + xs[0][0] * ys[0][0] + xs[0][1] * ys[1][0] + xs[0][2] * ys[2][0], + xs[0][0] * ys[0][1] + xs[0][1] * ys[1][1] + xs[0][2] * ys[2][1], + xs[0][0] * ys[0][2] + xs[0][1] * ys[1][2] + xs[0][2] * ys[2][2], + + xs[1][0] * ys[0][0] + xs[1][1] * ys[1][0] + xs[1][2] * ys[2][0], + xs[1][0] * ys[0][1] + xs[1][1] * ys[1][1] + xs[1][2] * ys[2][1], + xs[1][0] * ys[0][2] + xs[1][1] * ys[1][2] + xs[1][2] * ys[2][2], + + xs[2][0] * ys[0][0] + xs[2][1] * ys[1][0] + xs[2][2] * ys[2][0], + xs[2][0] * ys[0][1] + xs[2][1] * ys[1][1] + xs[2][2] * ys[2][1], + xs[2][0] * ys[0][2] + xs[2][1] * ys[1][2] + xs[2][2] * ys[2][2]}; + } + + template < typename T > + [[nodiscard]] constexpr vec operator*(const vec& xs, const mat& ys) { + return { + xs.x * ys[0][0] + xs.y * ys[1][0] + xs.z * ys[2][0] + xs.w * ys[3][0], + xs.x * ys[0][1] + xs.y * ys[1][1] + xs.z * ys[2][1] + xs.w * ys[3][1], + xs.x * ys[0][2] + xs.y * ys[1][2] + xs.z * ys[2][2] + xs.w * ys[3][2], + xs.x * ys[0][3] + xs.y * ys[1][3] + xs.z * ys[2][3] + xs.w * ys[3][3]}; + } + + template < typename T > + [[nodiscard]] constexpr mat operator*(const mat& xs, const mat& ys) { + return { + xs[0][0] * ys[0][0] + xs[0][1] * ys[1][0] + xs[0][2] * ys[2][0] + xs[0][3] * ys[3][0], + xs[0][0] * ys[0][1] + xs[0][1] * ys[1][1] + xs[0][2] * ys[2][1] + xs[0][3] * ys[3][1], + xs[0][0] * ys[0][2] + xs[0][1] * ys[1][2] + xs[0][2] * ys[2][2] + xs[0][3] * ys[3][2], + xs[0][0] * ys[0][3] + xs[0][1] * ys[1][3] + xs[0][2] * ys[2][3] + xs[0][3] * ys[3][3], + + xs[1][0] * ys[0][0] + xs[1][1] * ys[1][0] + xs[1][2] * ys[2][0] + xs[1][3] * ys[3][0], + xs[1][0] * ys[0][1] + xs[1][1] * ys[1][1] + xs[1][2] * ys[2][1] + xs[1][3] * ys[3][1], + xs[1][0] * ys[0][2] + xs[1][1] * ys[1][2] + xs[1][2] * ys[2][2] + xs[1][3] * ys[3][2], + xs[1][0] * ys[0][3] + xs[1][1] * ys[1][3] + xs[1][2] * ys[2][3] + xs[1][3] * ys[3][3], + + xs[2][0] * ys[0][0] + xs[2][1] * ys[1][0] + xs[2][2] * ys[2][0] + xs[2][3] * ys[3][0], + xs[2][0] * ys[0][1] + xs[2][1] * ys[1][1] + xs[2][2] * ys[2][1] + xs[2][3] * ys[3][1], + xs[2][0] * ys[0][2] + xs[2][1] * ys[1][2] + xs[2][2] * ys[2][2] + xs[2][3] * ys[3][2], + xs[2][0] * ys[0][3] + xs[2][1] * ys[1][3] + xs[2][2] * ys[2][3] + xs[2][3] * ys[3][3], + + xs[3][0] * ys[0][0] + xs[3][1] * ys[1][0] + xs[3][2] * ys[2][0] + xs[3][3] * ys[3][0], + xs[3][0] * ys[0][1] + xs[3][1] * ys[1][1] + xs[3][2] * ys[2][1] + xs[3][3] * ys[3][1], + xs[3][0] * ys[0][2] + xs[3][1] * ys[1][2] + xs[3][2] * ys[2][2] + xs[3][3] * ys[3][2], + xs[3][0] * ys[0][3] + xs[3][1] * ys[1][3] + xs[3][2] * ys[2][3] + xs[3][3] * ys[3][3]}; + } + + // operator*= + + template < typename T, std::size_t Size > + constexpr mat& operator*=(mat& xs, T y) { + return (xs = xs * y); + } + + template < typename T, std::size_t Size > + constexpr vec& operator*=(vec& xs, const mat& ys) { + return (xs = xs * ys); + } + + template < typename T, std::size_t Size > + constexpr mat& operator*=(mat& xs, const mat& ys) { + return (xs = xs * ys); + } + + // operator/ + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator/(const mat& xs, T y) { + return map([y](const vec& x){ return x / y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator/(T x, const mat& ys) { + return map([x](const vec& y){ return x / y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr mat operator/(const mat& xs, const mat& ys) { + return zip(std::divides<>(), xs, ys); + } + + // operator/= + + template < typename T, std::size_t Size > + constexpr mat& operator/=(mat& xs, T y) { + return (xs = xs / y); + } + + template < typename T, std::size_t Size > + constexpr mat& operator/=(mat& xs, const mat& ys) { + return (xs = xs / ys); + } + + // operator== + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator==(const mat& xs, const mat& ys) { + return fold([](bool acc, const vec& x, const vec& y){ + return acc && (x == y); + }, true, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator!=(const mat& xs, const mat& ys) { + return fold([](bool acc, const vec& x, const vec& y){ + return acc || (x != y); + }, false, xs, ys); + } + + // operator< + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator<(const mat& xs, const mat& ys) { + for ( std::size_t i = 0; i < Size; ++i ) { + if ( xs[i] < ys[i] ) { + return true; + } + if ( ys[i] < xs[i] ) { + return false; + } + } + return false; + } +} + +// +// Matrix Functions +// + +namespace vmath_hpp +{ + namespace impl + { + template < typename T > + [[nodiscard]] constexpr mat transpose_2x2_impl( + T a, T c, + T b, T d) + { + return { + a, b, + c, d}; + } + + template < typename T > + [[nodiscard]] constexpr mat transpose_3x3_impl( + T a, T d, T g, + T b, T e, T h, + T c, T f, T i) + { + return { + a, b, c, + d, e, f, + g, h, i}; + } + + template < typename T > + [[nodiscard]] constexpr mat transpose_4x4_impl( + T a, T e, T i, T m, + T b, T f, T j, T n, + T c, T g, T k, T o, + T d, T h, T l, T p) + { + return { + a, b, c, d, + e, f, g, h, + i, j, k, l, + m, n, o, p}; + } + } + + template < typename T > + [[nodiscard]] constexpr mat transpose(const mat& m) { + return impl::transpose_2x2_impl( + m[0][0], m[0][1], + m[1][0], m[1][1]); + } + + template < typename T > + [[nodiscard]] constexpr mat transpose(const mat& m) { + return impl::transpose_3x3_impl( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + + template < typename T > + [[nodiscard]] constexpr mat transpose(const mat& m) { + return impl::transpose_4x4_impl( + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); + } + + namespace impl + { + template < typename T > + [[nodiscard]] constexpr T determinant_2x2_impl( + T a, T b, + T c, T d) + { + return + + a * d + - b * c; + } + + template < typename T > + [[nodiscard]] constexpr T determinant_3x3_impl( + T a, T b, T c, + T d, T e, T f, + T g, T h, T i) + { + return + + a * determinant_2x2_impl(e, f, h, i) + - b * determinant_2x2_impl(d, f, g, i) + + c * determinant_2x2_impl(d, e, g, h); + + } + + template < typename T > + [[nodiscard]] constexpr T determinant_4x4_impl( + T a, T b, T c, T d, + T e, T f, T g, T h, + T i, T j, T k, T l, + T m, T n, T o, T p) + { + return + + a * determinant_3x3_impl(f, g, h, j, k, l, n, o, p) + - b * determinant_3x3_impl(e, g, h, i, k, l, m, o, p) + + c * determinant_3x3_impl(e, f, h, i, j, l, m, n, p) + - d * determinant_3x3_impl(e, f, g, i, j, k, m, n, o); + } + } + + template < typename T > + [[nodiscard]] constexpr T determinant(const mat& m) { + return impl::determinant_2x2_impl( + m[0][0], m[0][1], + m[1][0], m[1][1]); + } + + template < typename T > + [[nodiscard]] constexpr T determinant(const mat& m) { + return impl::determinant_3x3_impl( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + + template < typename T > + [[nodiscard]] constexpr T determinant(const mat& m) { + return impl::determinant_4x4_impl( + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); + } + + namespace impl + { + template < typename T > + [[nodiscard]] constexpr mat inverse_2x2_impl( + T a, T b, + T c, T d) + { + const T inv_det = reciprocal(determinant_2x2_impl( + a, b, + c, d)); + + const mat inv_m( + d, -b, + -c, a); + + return inv_m * inv_det; + } + + template < typename T > + [[nodiscard]] constexpr mat inverse_3x3_impl( + T a, T b, T c, + T d, T e, T f, + T g, T h, T i) + { + const T inv_det = reciprocal(determinant_3x3_impl( + a, b, c, + d, e, f, + g, h, i)); + + const mat inv_m( + e * i - f * h, + c * h - b * i, + b * f - c * e, + f * g - d * i, + a * i - c * g, + c * d - a * f, + d * h - e * g, + b * g - a * h, + a * e - b * d); + + return inv_m * inv_det; + } + + template < typename T > + [[nodiscard]] constexpr mat inverse_4x4_impl( + T a, T b, T c, T d, + T e, T f, T g, T h, + T i, T j, T k, T l, + T m, T n, T o, T p) + { + const T inv_det = reciprocal(determinant_4x4_impl( + a, b, c, d, + e, f, g, h, + i, j, k, l, + m, n, o, p)); + + const mat inv_m( + (f * (k * p - l * o) + g * (l * n - j * p) + h * (j * o - k * n)), + (j * (c * p - d * o) + k * (d * n - b * p) + l * (b * o - c * n)), + (n * (c * h - d * g) + o * (d * f - b * h) + p * (b * g - c * f)), + (b * (h * k - g * l) + c * (f * l - h * j) + d * (g * j - f * k)), + (g * (i * p - l * m) + h * (k * m - i * o) + e * (l * o - k * p)), + (k * (a * p - d * m) + l * (c * m - a * o) + i * (d * o - c * p)), + (o * (a * h - d * e) + p * (c * e - a * g) + m * (d * g - c * h)), + (c * (h * i - e * l) + d * (e * k - g * i) + a * (g * l - h * k)), + (h * (i * n - j * m) + e * (j * p - l * n) + f * (l * m - i * p)), + (l * (a * n - b * m) + i * (b * p - d * n) + j * (d * m - a * p)), + (p * (a * f - b * e) + m * (b * h - d * f) + n * (d * e - a * h)), + (d * (f * i - e * j) + a * (h * j - f * l) + b * (e * l - h * i)), + (e * (k * n - j * o) + f * (i * o - k * m) + g * (j * m - i * n)), + (i * (c * n - b * o) + j * (a * o - c * m) + k * (b * m - a * n)), + (m * (c * f - b * g) + n * (a * g - c * e) + o * (b * e - a * f)), + (a * (f * k - g * j) + b * (g * i - e * k) + c * (e * j - f * i))); + + return inv_m * inv_det; + } + } + + template < typename T > + [[nodiscard]] constexpr mat inverse(const mat& m) { + return impl::inverse_2x2_impl( + m[0][0], m[0][1], + m[1][0], m[1][1]); + } + + template < typename T > + [[nodiscard]] constexpr mat inverse(const mat& m) { + return impl::inverse_3x3_impl( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + + template < typename T > + [[nodiscard]] constexpr mat inverse(const mat& m) { + return impl::inverse_4x4_impl( + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); + } +} diff --git a/headers/vmath.hpp/vmath_vec.hpp b/headers/vmath.hpp/vmath_vec.hpp new file mode 100644 index 0000000..cd80289 --- /dev/null +++ b/headers/vmath.hpp/vmath_vec.hpp @@ -0,0 +1,201 @@ +/******************************************************************************* + * 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" + +namespace vmath_hpp::detail +{ + template < typename T, std::size_t Size > + class vec_base; + + template < typename T > + class vec_base { + public: + T x{}, y{}; + public: + vec_base() = default; + + constexpr explicit vec_base(T v) + : x{v}, y{v} {} + + constexpr vec_base(T x, T y) + : x{x}, y{y} {} + + constexpr explicit vec_base(const vec_base& xy) + : x{xy[0]}, y{xy[1]} {} + + constexpr explicit vec_base(const vec_base& xy) + : x{xy[0]}, y{xy[1]} {} + + [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + } + } + + [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + } + } + }; + + template < typename T > + class vec_base { + public: + T x{}, y{}, z{}; + public: + vec_base() = default; + + constexpr explicit vec_base(T v) + : x{v}, y{v}, z{v} {} + + constexpr vec_base(T x, T y, T z) + : x{x}, y{y}, z{z} {} + + constexpr vec_base(const vec_base& xy, T z) + : x{xy[0]}, y{xy[1]}, z{z} {} + + constexpr vec_base(T x, const vec_base& yz) + : x{x}, y{yz[0]}, z{yz[1]} {} + + constexpr explicit vec_base(const vec_base& xyz) + : x{xyz[0]}, y{xyz[1]}, z{xyz[2]} {} + + [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + } + } + + [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + } + } + }; + + template < typename T > + class vec_base { + public: + T x{}, y{}, z{}, w{}; + public: + vec_base() = default; + + constexpr explicit vec_base(T v) + : x{v}, y{v}, z{v}, w{v} {} + + constexpr vec_base(T x, T y, T z, T w) + : x{x}, y{y}, z{z}, w{w} {} + + constexpr vec_base(const vec_base& xy, T z, T w) + : x{xy[0]}, y{xy[1]}, z{z}, w{w} {} + + constexpr vec_base(T x, const vec_base& yz, T w) + : x{x}, y{yz[0]}, z{yz[1]}, w{w} {} + + constexpr vec_base(T x, T y, const vec_base& zw) + : x{x}, y{y}, z{zw[0]}, w{zw[1]} {} + + constexpr vec_base(const vec_base& xy, const vec_base& zw) + : x{xy[0]}, y{xy[1]}, z{zw[0]}, w{zw[1]} {} + + constexpr vec_base(const vec_base& xyz, T w) + : x{xyz[0]}, y{xyz[1]}, z{xyz[2]}, w{w} {} + + constexpr vec_base(T x, const vec_base& yzw) + : x{x}, y{yzw[0]}, z{yzw[1]}, w{yzw[2]} {} + + [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + }; +} + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class vec final : public detail::vec_base { + public: + using self_type = vec; + using base_type = detail::vec_base; + public: + using value_type = T; + + using pointer = value_type*; + using const_pointer = const value_type*; + + using reference = value_type&; + using const_reference = const value_type&; + + static constexpr std::size_t size = Size; + public: + using base_type::vec_base; + using base_type::operator[]; + + vec() = default; + + vec(vec&&) = default; + vec& operator=(vec&&) = default; + + vec(const vec&) = default; + vec& operator=(const vec&) = default; + + void swap(vec& other) noexcept(std::is_nothrow_swappable_v) { + for ( std::size_t i = 0; i < Size; ++i ) { + using std::swap; + swap((*this)[i], other[i]); + } + } + + [[nodiscard]] constexpr reference at(std::size_t index) { + if ( index >= Size ) { + throw std::out_of_range("vec::at"); + } + return (*this)[index]; + } + + [[nodiscard]] constexpr const_reference at(std::size_t index) const { + if ( index >= Size ) { + throw std::out_of_range("vec::at"); + } + return (*this)[index]; + } + }; + + template < typename T, std::size_t Size > + void swap(vec& l, vec& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } +} diff --git a/headers/vmath.hpp/vmath_vec_fun.hpp b/headers/vmath.hpp/vmath_vec_fun.hpp new file mode 100644 index 0000000..4227849 --- /dev/null +++ b/headers/vmath.hpp/vmath_vec_fun.hpp @@ -0,0 +1,685 @@ +/******************************************************************************* + * 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" + +#include "vmath_fun.hpp" +#include "vmath_vec.hpp" + +namespace vmath_hpp::detail +{ + namespace impl + { + template < typename A, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto map_impl(F&& f, const vec& a, std::index_sequence) + -> vec, Size> + { + return { f(a[Is])... }; + } + + template < typename A, typename B, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto zip_impl(F&& f, const vec& a, const vec& b, std::index_sequence) + -> vec, Size> + { + return { f(a[Is], b[Is])... }; + } + + template < typename A, typename B, typename C, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr auto zip_impl(F&& f, const vec& a, const vec& b, const vec& c, std::index_sequence) + -> vec, Size> + { + return { f(a[Is], b[Is], c[Is])... }; + } + + template < typename A, typename B, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr A fold_impl(F&& f, A init, const vec& b, std::index_sequence) { + return ((init = f(std::move(init), b[Is])), ...); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F, std::size_t... Is > + [[nodiscard]] constexpr A fold_impl(F&& f, A init, const vec& b, const vec& c, std::index_sequence) { + return ((init = f(std::move(init), b[Is], c[Is])), ...); + } + + template < typename A, std::size_t Size, typename F, std::size_t I, std::size_t... Is > + [[nodiscard]] constexpr A fold1_impl(F&& f, const vec& a, std::index_sequence) { + A init = a[I]; + return ((init = f(std::move(init), a[Is])), ...); + } + } + + template < typename A, std::size_t Size, typename F > + [[nodiscard]] constexpr auto map(F&& f, const vec& a) + -> vec, Size> + { + return impl::map_impl(std::forward(f), a, std::make_index_sequence{}); + } + + template < typename A, typename B, std::size_t Size, typename F > + [[nodiscard]] constexpr auto zip(F&& f, const vec& a, const vec& b) + -> vec, Size> + { + return impl::zip_impl(std::forward(f), a, b, std::make_index_sequence{}); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F > + [[nodiscard]] constexpr auto zip(F&& f, const vec& a, const vec& b, const vec& c) + -> vec, Size> + { + return impl::zip_impl(std::forward(f), a, b, c, std::make_index_sequence{}); + } + + template < typename A, typename B, std::size_t Size, typename F > + [[nodiscard]] constexpr A fold(F&& f, A init, const vec& b) { + return impl::fold_impl(std::forward(f), std::move(init), b, std::make_index_sequence{}); + } + + template < typename A, typename B, typename C, std::size_t Size, typename F > + [[nodiscard]] constexpr A fold(F&& f, A init, const vec& b, const vec& c) { + return impl::fold_impl(std::forward(f), std::move(init), b, c, std::make_index_sequence{}); + } + + template < typename A, std::size_t Size, typename F > + [[nodiscard]] constexpr A fold1(F&& f, const vec& a) { + return impl::fold1_impl(std::forward(f), a, std::make_index_sequence{}); + } +} + +// +// Operators +// + +namespace vmath_hpp +{ + // -operator + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator-(const vec& xs) { + return map(std::negate<>(), xs); + } + + // operator+ + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator+(const vec& xs, T y) { + return map([y](T x){ return x + y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator+(T x, const vec& ys) { + return map([x](T y){ return x + y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator+(const vec& xs, const vec& ys) { + return zip(std::plus<>(), xs, ys); + } + + // operator+= + + template < typename T, std::size_t Size > + constexpr vec& operator+=(vec& xs, T y) { + return (xs = xs + y); + } + + template < typename T, std::size_t Size > + constexpr vec& operator+=(vec& xs, const vec& ys) { + return (xs = xs + ys); + } + + // operator- + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator-(const vec& xs, T y) { + return map([y](T x){ return x - y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator-(T x, const vec& ys) { + return map([x](T y){ return x - y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator-(const vec& xs, const vec& ys) { + return zip(std::minus<>(), xs, ys); + } + + // operator-= + + template < typename T, std::size_t Size > + constexpr vec& operator-=(vec& xs, T y) { + return (xs = xs - y); + } + + template < typename T, std::size_t Size > + constexpr vec& operator-=(vec& xs, const vec& ys) { + return (xs = xs - ys); + } + + // operator* + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator*(const vec& xs, T y) { + return map([y](T x){ return x * y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator*(T x, const vec& ys) { + return map([x](T y){ return x * y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator*(const vec& xs, const vec& ys) { + return zip(std::multiplies<>(), xs, ys); + } + + // operator*= + + template < typename T, std::size_t Size > + constexpr vec& operator*=(vec& xs, T y) { + return (xs = xs * y); + } + + template < typename T, std::size_t Size > + constexpr vec& operator*=(vec& xs, const vec& ys) { + return (xs = xs * ys); + } + + // operator/ + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator/(const vec& xs, T y) { + return map([y](T x){ return x / y; }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator/(T x, const vec& ys) { + return map([x](T y){ return x / y; }, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec operator/(const vec& xs, const vec& ys) { + return zip(std::divides<>(), xs, ys); + } + + // operator/= + + template < typename T, std::size_t Size > + constexpr vec& operator/=(vec& xs, T y) { + return (xs = xs / y); + } + + template < typename T, std::size_t Size > + constexpr vec& operator/=(vec& xs, const vec& ys) { + return (xs = xs / ys); + } + + // operator== + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator==(const vec& xs, const vec& ys) { + return fold([](bool acc, T x, T y){ + return acc && (x == y); + }, true, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator!=(const vec& xs, const vec& ys) { + return fold([](bool acc, T x, T y){ + return acc || (x != y); + }, false, xs, ys); + } + + // operator< + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool operator<(const vec& xs, const vec& ys) { + for ( std::size_t i = 0; i < Size; ++i ) { + if ( xs[i] < ys[i] ) { + return true; + } + if ( ys[i] < xs[i] ) { + return false; + } + } + return false; + } +} + +// +// Angle and Trigonometry Functions +// + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec radians(const vec& degrees) { + return map([](T x) { return radians(x); }, degrees); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec degrees(const vec& radians) { + return map([](T x) { return degrees(x); }, radians); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec sin(const vec& xs) { + return map([](T x) { return sin(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec cos(const vec& xs) { + return map([](T x) { return cos(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec tan(const vec& xs) { + return map([](T x) { return tan(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec asin(const vec& xs) { + return map([](T x) { return asin(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec acos(const vec& xs) { + return map([](T x) { return acos(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec atan(const vec& xs) { + return map([](T x) { return atan(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec atan2(const vec& ys, const vec& xs) { + return zip([](T y, T x) { return atan2(y, x); }, ys, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec sinh(const vec& xs) { + return map([](T x) { return sinh(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec cosh(const vec& xs) { + return map([](T x) { return cosh(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec tanh(const vec& xs) { + return map([](T x) { return tanh(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec asinh(const vec& xs) { + return map([](T x) { return asinh(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec acosh(const vec& xs) { + return map([](T x) { return acosh(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec atanh(const vec& xs) { + return map([](T x) { return atanh(x); }, xs); + } +} + +// +// Exponential Functions +// + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + [[nodiscard]] vec pow(const vec& xs, const vec& ys) { + return zip([](T x, T y) { return pow(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec exp(const vec& xs) { + return map([](T x) { return exp(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec log(const vec& xs) { + return map([](T x) { return log(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec exp2(const vec& xs) { + return map([](T x) { return exp2(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec log2(const vec& xs) { + return map([](T x) { return log2(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec sqrt(const vec& xs) { + return map([](T x) { return sqrt(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec rsqrt(const vec& xs) { + return map([](T x) { return rsqrt(x); }, xs); + } +} + +// +// Common Functions +// + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec abs(const vec& xs) { + return map([](T x) { return abs(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec sign(const vec& xs) { + return map([](T x) { return sign(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec reciprocal(const vec& xs) { + return map([](T x) { return reciprocal(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec floor(const vec& xs) { + return map([](T x) { return floor(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec trunc(const vec& xs) { + return map([](T x) { return trunc(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec round(const vec& xs) { + return map([](T x) { return round(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec ceil(const vec& xs) { + return map([](T x) { return ceil(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec fract(const vec& xs) { + return map([](T x) { return fract(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec fmod(const vec& xs, T y) { + return map([y](T x) { return fmod(x, y); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec fmod(const vec& xs, const vec& ys) { + return zip([](T x, T y) { return fmod(x, y); }, xs, ys); + } + + namespace impl + { + template < typename T, std::size_t Size, std::size_t... Is > + vec modf_impl(const vec& xs, vec* is, std::index_sequence) { + return { modf(xs[Is], &(*is)[Is])... }; + } + } + + template < typename T, std::size_t Size > + vec modf(const vec& xs, vec* is) { + return impl::modf_impl(xs, is, std::make_index_sequence{}); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T min(const vec& xs) { + return fold1([](T acc, T x){ return min(acc, x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec min(const vec& xs, T y) { + return map([y](T x) { return min(x, y); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec min(const vec& xs, const vec& ys) { + return zip([](T x, T y) { return min(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T max(const vec& xs) { + return fold1([](T acc, T x){ return max(acc, x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec max(const vec& xs, T y) { + return map([y](T x) { return max(x, y); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec max(const vec& xs, const vec& ys) { + return zip([](T x, T y) { return max(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec clamp(const vec& xs, T min_x, T max_x) { + return map([min_x, max_x](T x) { return clamp(x, min_x, max_x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec clamp(const vec& xs, const vec& min_xs, const vec& max_xs) { + return zip([](T x, T min_x, T max_x) { return clamp(x, min_x, max_x); }, xs, min_xs, max_xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec saturate(const vec& xs) { + return map([](T x) { return saturate(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec lerp(const vec& xs, const vec& ys, T a) { + return zip([a](T x, T y) { return lerp(x, y, a); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec lerp(const vec& xs, const vec& ys, const vec& as) { + return zip([](T x, T y, T a) { return lerp(x, y, a); }, xs, ys, as); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec step(T edge, const vec& xs) { + return map([edge](T x) { return step(edge, x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec step(const vec& edges, const vec& xs) { + return zip([](T edge, T x) { return step(edge, x); }, edges, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec smoothstep(T edge0, T edge1, const vec& xs) { + return map([edge0, edge1](T x) { return smoothstep(edge0, edge1, x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec smoothstep(const vec& edges0, const vec& edges1, const vec& xs) { + return zip([](T edge0, T edge1, T x) { return smoothstep(edge0, edge1, x); }, edges0, edges1, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec isnan(const vec& xs) { + return map([](T x) { return isnan(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec isinf(const vec& xs) { + return map([](T x) { return isinf(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec isfinite(const vec& xs) { + return map([](T x) { return isfinite(x); }, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec fma(const vec& as, const vec& bs, const vec& cs) { + return zip([](T a, T b, T c) { return fma(a, b, c); }, as, bs, cs); + } + + namespace impl + { + template < typename T, std::size_t Size, std::size_t... Is > + vec frexp_impl(const vec& xs, vec* exps, std::index_sequence) { + return { frexp(xs[Is], &(*exps)[Is])... }; + } + } + + template < typename T, std::size_t Size > + vec frexp(const vec& xs, vec* exps) { + return impl::frexp_impl(xs, exps, std::make_index_sequence{}); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec ldexp(const vec& xs, const vec& exps) { + return zip([](T x, int exp) { return ldexp(x, exp); }, xs, exps); + } +} + +// +// Geometric Functions +// + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T dot(const vec& xs, const vec& ys) { + return fold([](T acc, T x, T y){ + return acc + (x * y); + }, T(0), xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] T length(const vec& xs) { + return sqrt(dot(xs, xs)); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T length2(const vec& xs) { + return dot(xs, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] T distance(const vec& xs, const vec& ys) { + return length(ys - xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr T distance2(const vec& xs, const vec& ys) { + return length2(ys - xs); + } + + template < typename T > + [[nodiscard]] constexpr T cross(const vec& xs, const vec& ys) { + return xs.x * ys.y - xs.y * ys.x; + } + + template < typename T > + [[nodiscard]] constexpr vec cross(const vec& xs, const vec& ys) { + return { + xs.y * ys.z - xs.z * ys.y, + xs.z * ys.x - xs.x * ys.z, + xs.x * ys.y - xs.y * ys.x}; + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec normalize(const vec& xs) { + return xs * rsqrt(dot(xs, xs)); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec faceforward(const vec& n, const vec& i, const vec& nref) { + return dot(nref, i) < T(0) ? n : -n; + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec reflect(const vec& i, const vec& n) { + return i - n * dot(n, i) * T(2); + } + + template < typename T, std::size_t Size > + [[nodiscard]] vec refract(const vec& i, const vec& n, T eta) { + 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); + } +} + +// +// Vector Relational Functions +// + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec less(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return less(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec less_equal(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return less_equal(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec greater(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return greater(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec greater_equal(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return greater_equal(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec equal_to(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return equal_to(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec equal_to(const vec& xs, const vec& ys, T epsilon) { + return zip([epsilon](T x, T y){ return equal_to(x, y, epsilon); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec not_equal_to(const vec& xs, const vec& ys) { + return zip([](T x, T y){ return not_equal_to(x, y); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr vec not_equal_to(const vec& xs, const vec& ys, T epsilon) { + return zip([epsilon](T x, T y){ return not_equal_to(x, y, epsilon); }, xs, ys); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool any(const vec& xs) { + return fold([](bool acc, T x){ return acc || any(x); }, false, xs); + } + + template < typename T, std::size_t Size > + [[nodiscard]] constexpr bool all(const vec& xs) { + return fold([](bool acc, T x){ return acc && all(x); }, true, xs); + } +} diff --git a/untests/vmath_ext_tests.cpp b/untests/vmath_ext_tests.cpp new file mode 100644 index 0000000..10e56b0 --- /dev/null +++ b/untests/vmath_ext_tests.cpp @@ -0,0 +1,219 @@ +/******************************************************************************* + * 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 + +#include "vmath_tests.hpp" + +#include +#include +#include +#include + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/ext") { + SECTION("units") { + STATIC_REQUIRE(zero2 == int2(0,0)); + STATIC_REQUIRE(zero3 == int3(0,0,0)); + STATIC_REQUIRE(zero4 == int4(0,0,0,0)); + + STATIC_REQUIRE(unit2 == int2(1,1)); + STATIC_REQUIRE(unit2_x == int2(1,0)); + STATIC_REQUIRE(unit2_y == int2(0,1)); + + STATIC_REQUIRE(unit3 == int3(1,1,1)); + STATIC_REQUIRE(unit3_x == int3(1,0,0)); + STATIC_REQUIRE(unit3_y == int3(0,1,0)); + STATIC_REQUIRE(unit3_z == int3(0,0,1)); + + STATIC_REQUIRE(unit4 == int4(1,1,1,1)); + STATIC_REQUIRE(unit4_x == int4(1,0,0,0)); + STATIC_REQUIRE(unit4_y == int4(0,1,0,0)); + STATIC_REQUIRE(unit4_z == int4(0,0,1,0)); + STATIC_REQUIRE(unit4_w == int4(0,0,0,1)); + + STATIC_REQUIRE(zero2x2 == int2x2(0,0,0,0)); + STATIC_REQUIRE(zero3x3 == int3x3(0,0,0,0,0,0,0,0,0)); + STATIC_REQUIRE(zero4x4 == int4x4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)); + + STATIC_REQUIRE(unit2x2 == int2x2(1,1,1,1)); + STATIC_REQUIRE(unit3x3 == int3x3(1,1,1,1,1,1,1,1,1)); + STATIC_REQUIRE(unit4x4 == int4x4(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)); + + STATIC_REQUIRE(identity2x2 == int2x2()); + STATIC_REQUIRE(identity3x3 == int3x3()); + STATIC_REQUIRE(identity4x4 == int4x4()); + } + + SECTION("hash") { + REQUIRE(std::hash{}({1,2}) == std::hash{}({1,2})); + REQUIRE_FALSE(std::hash{}({1,2}) == std::hash{}({2,1})); + + REQUIRE(std::hash{}({1,2,3}) == std::hash{}({1,2,3})); + REQUIRE_FALSE(std::hash{}({1,2,3}) == std::hash{}({3,2,1})); + + REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); + REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({3,2,1,4})); + + REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); + REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,4,3})); + { + std::set s; + s.insert(int2(1,2)); + REQUIRE(s.count(int2(1,2)) > 0); + REQUIRE_FALSE(s.count(int2(1,1)) > 0); + } + { + std::map s; + s.emplace(int2(1,2),3); + s.emplace(int2(2,3),5); + REQUIRE(s[int2(1,2)] == 3); + REQUIRE(s[int2(2,3)] == 5); + } + { + std::unordered_set s; + s.insert(int2(1,2)); + REQUIRE(s.count(int2(1,2)) > 0); + REQUIRE_FALSE(s.count(int2(1,1)) > 0); + } + { + std::unordered_map s; + s.emplace(int2(1,2),3); + s.emplace(int2(2,3),5); + REQUIRE(s[int2(1,2)] == 3); + REQUIRE(s[int2(2,3)] == 5); + } + } + + SECTION("cast_to") { + { + constexpr auto i = cast_to(1.5f); + STATIC_REQUIRE(i == 1); + STATIC_REQUIRE(std::is_same_v); + } + { + constexpr auto v = cast_to(float2{1.5f}); + STATIC_REQUIRE(v == int2(1)); + STATIC_REQUIRE(std::is_same_v); + } + { + constexpr auto m = cast_to(float2x2{1.5f}); + STATIC_REQUIRE(m == int2x2(1)); + STATIC_REQUIRE(std::is_same_v); + } + } + + SECTION("component") { + STATIC_REQUIRE(component(int2{1,2}, 0) == 1); + STATIC_REQUIRE(component(int2{1,2}, 1) == 2); + + STATIC_REQUIRE(component(int2{0,0}, 0, 1) == int2{1,0}); + STATIC_REQUIRE(component(int2{0,0}, 1, 2) == int2{0,2}); + } + + SECTION("row") { + STATIC_REQUIRE(row(int2x2(1,2,3,4), 0) == int2(1,2)); + STATIC_REQUIRE(row(int2x2(1,2,3,4), 1) == int2(3,4)); + + STATIC_REQUIRE(row(int2x2(), 0, {1,2}) == int2x2(1,2,0,1)); + STATIC_REQUIRE(row(int2x2(), 1, {3,4}) == int2x2(1,0,3,4)); + } + + SECTION("column") { + STATIC_REQUIRE(column(int2x2(1,2,3,4), 0) == int2(1,3)); + STATIC_REQUIRE(column(int2x2(1,2,3,4), 1) == int2(2,4)); + + STATIC_REQUIRE(column(int2x2(), 0, {2,3}) == int2x2(2,0,3,1)); + STATIC_REQUIRE(column(int2x2(), 1, {3,4}) == int2x2(1,3,0,4)); + } + + SECTION("matrix translate") { + STATIC_REQUIRE(float3(2.f,3.f,1.f) * translate(float2{1.f,2.f}) == approx3(3.f,5.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * translate(translate(float2{1.f,2.f}), float2{1.f,2.f}) == approx3(4.f,7.f,1.f)); + + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * translate(float3{1.f,2.f,3.f}) == approx4(3.f,5.f,7.f,1.f)); + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * translate(translate(float3{1.f,2.f,3.f}), float3{1.f,2.f,3.f}) == approx4(4.f,7.f,10.f,1.f)); + } + + SECTION("matrix rotate") { + constexpr float pi = radians(180.f); + constexpr float pi_2 = radians(90.f); + + REQUIRE(float3(2.f,3.f,1.f) * rotate(pi) == approx3(-2.f,-3.f,1.f)); + REQUIRE(float4(2.f,3.f,4.f,1.f) * rotate(pi,{0.f,0.f,1.f}) == approx4(-2.f,-3.f,4.f,1.f)); + REQUIRE(float4(2.f,3.f,4.f,1.f) * rotate(pi,float3{0.f,0.f,1.f}) == approx4(-2.f,-3.f,4.f,1.f)); + + REQUIRE(float3(2.f,3.f,1.f) * rotate(rotate(pi_2),pi_2) == approx3(-2.f,-3.f,1.f)); + REQUIRE(float4(2.f,3.f,4.f,1.f) * rotate(rotate(pi_2,{0.f,0.f,1.f}),pi_2,{0.f,0.f,1.f}) == approx4(-2.f,-3.f,4.f,1.f)); + REQUIRE(float4(2.f,3.f,4.f,1.f) * rotate(rotate(pi_2,float3{0.f,0.f,1.f}),pi_2,float3{0.f,0.f,1.f}) == approx4(-2.f,-3.f,4.f,1.f)); + } + + SECTION("matrix scale") { + STATIC_REQUIRE(float3(2.f,3.f,1.f) * scale(float2{2.f,3.f}) == approx3(4.f,9.f,1.f)); + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * scale(float3{2.f,3.f,4.f}) == approx4(4.f,9.f,16.f,1.f)); + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * scale(float3{2.f,3.f,4.f}) == approx4(4.f,9.f,16.f,1.f)); + + STATIC_REQUIRE(float3(2.f,3.f,1.f) * scale(scale(float2{2.f,2.f}), {2.f,3.f}) == approx3(8.f,18.f,1.f)); + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * scale(scale(float3{2.f,2.f,2.f}), {2.f,3.f,4.f}) == approx4(8.f,18.f,32.f,1.f)); + STATIC_REQUIRE(float4(2.f,3.f,4.f,1.f) * scale(scale(float3{2.f,2.f,2.f}), float3{2.f,3.f,4.f}) == approx4(8.f,18.f,32.f,1.f)); + } + + SECTION("matrix shear") { + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_x(0.f) == approx3(2.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_x(1.f) == approx3(5.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_x(shear_x(1.f),1.f) == approx3(8.f,3.f,1.f)); + + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_y(0.f) == approx3(2.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_y(1.f) == approx3(2.f,5.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear_y(shear_y(1.f),1.f) == approx3(2.f,7.f,1.f)); + + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear(float2(0.f,0.f)) == approx3(2.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear(float2(1.f,0.f)) == approx3(5.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear(float2(0.f,1.f)) == approx3(2.f,5.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear(shear(float2(1.f,0.f)),float2(1.f,0.f)) == approx3(8.f,3.f,1.f)); + STATIC_REQUIRE(float3(2.f,3.f,1.f) * shear(shear(float2(0.f,1.f)),float2(0.f,1.f)) == approx3(2.f,7.f,1.f)); + } + + SECTION("matrix look_at") { + (void)look_at_lh(float3(-10.f), float3(0.f), float3(0,-1,0)); + (void)look_at_rh(float3(-10.f), float3(0.f), float3(0,-1,0)); + + (void)orthographic_lh_zo(0.f, 800.f, 0.f, 640.f, 0.f, 10.f); + (void)orthographic_lh_no(0.f, 800.f, 0.f, 640.f, 0.f, 10.f); + (void)orthographic_rh_zo(0.f, 800.f, 0.f, 640.f, 0.f, 10.f); + (void)orthographic_rh_no(0.f, 800.f, 0.f, 640.f, 0.f, 10.f); + + (void)perspective_lh_zo(1.f, 1.3f, 0.f, 10.f); + (void)perspective_lh_no(1.f, 1.3f, 0.f, 10.f); + (void)perspective_rh_zo(1.f, 1.3f, 0.f, 10.f); + (void)perspective_rh_no(1.f, 1.3f, 0.f, 10.f); + } + + SECTION("vector angle") { + REQUIRE(angle(float2(2.f,0.f), float2(0.f,1.f)) == Approx(radians(90.f))); + REQUIRE(angle(float2(0.f,3.f), float2(1.f,0.f)) == Approx(radians(90.f))); + REQUIRE(angle(float2(0.5f,0.f), float2(-1.f,0.f)) == Approx(radians(180.f))); + REQUIRE(angle(float2(-0.2f,0.f), float2(1.f,0.f)) == Approx(radians(180.f))); + REQUIRE(angle(float3(0.f,2.f,0.f), float3(0.f,0.f,1.f)) == Approx(radians(90.f))); + REQUIRE(angle(float3(0.f,0.f,3.f), float3(0.f,1.f,0.f)) == Approx(radians(90.f))); + } + + SECTION("vector rotate") { + REQUIRE(rotate(float2(2.f,0.f), radians(90.f)) == approx2(0.f,2.f)); + REQUIRE(rotate(float2(1.5f,0.f), radians(-90.f)) == approx2(0.f,-1.5f)); + + REQUIRE(rotate(float3(1.5f,0.f,0.f), radians(90.f), float3(0,0,1)) == approx3(0.f,1.5f,0.f)); + REQUIRE(rotate(float4(1.5f,0.f,0.f,1.f), radians(90.f), float3(0,0,1)) == approx4(0.f,1.5f,0.f,1.f)); + } +} diff --git a/untests/vmath_fun_tests.cpp b/untests/vmath_fun_tests.cpp new file mode 100644 index 0000000..81288a5 --- /dev/null +++ b/untests/vmath_fun_tests.cpp @@ -0,0 +1,170 @@ +/******************************************************************************* + * 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 + +#include "vmath_tests.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/fun") { + SECTION("Angle and Trigonometry Functions") { + STATIC_REQUIRE(radians(degrees(12.13f)) == approx(12.13f)); + STATIC_REQUIRE(degrees(radians(12.13f)) == approx(12.13f)); + + (void)sin(0.f); + (void)cos(0.f); + (void)tan(0.f); + + (void)asin(0.f); + (void)acos(0.f); + (void)atan(0.f); + (void)atan2(0.f, 0.f); + + (void)sinh(0.f); + (void)cosh(0.f); + (void)tanh(0.f); + + (void)asinh(0.f); + (void)acosh(0.f); + (void)atanh(0.f); + } + + SECTION("Exponential Functions") { + (void)pow(2.f, 3.f); + (void)exp(2.f); + (void)log(2.f); + (void)exp2(2.f); + (void)log2(2.f); + (void)sqrt(2.f); + (void)rsqrt(2.f); + } + + SECTION("Common Functions") { + STATIC_REQUIRE(vmath_hpp::abs(1) == 1); + STATIC_REQUIRE(vmath_hpp::abs(-1) == 1); + STATIC_REQUIRE(vmath_hpp::abs(1.f) == approx(1.f)); + STATIC_REQUIRE(vmath_hpp::abs(-1.f) == approx(1.f)); + + STATIC_REQUIRE(sign(2) == 1); + STATIC_REQUIRE(sign(-2) == -1); + STATIC_REQUIRE(sign(0) == 0); + STATIC_REQUIRE(sign(2.f) == approx(1.f)); + STATIC_REQUIRE(sign(-2.f) == approx(-1.f)); + STATIC_REQUIRE(sign(0.f) == approx(0.f)); + + STATIC_REQUIRE(reciprocal(2.f) == approx(0.5f)); + STATIC_REQUIRE(reciprocal(4.f) == approx(0.25f)); + + 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(saturate(-0.5f) == approx(0.f)); + STATIC_REQUIRE(saturate(0.5f) == approx(0.5f)); + STATIC_REQUIRE(saturate(1.5f) == approx(1.f)); + + STATIC_REQUIRE(lerp(0.f, 10.f, 0.5f) == approx(5.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(vmath_hpp::isnan(1.f)); + REQUIRE_FALSE(vmath_hpp::isinf(1.f)); + REQUIRE(vmath_hpp::isfinite(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") { + STATIC_REQUIRE(length(10.f) == approx(10.f)); + STATIC_REQUIRE(length(-10.f) == approx(10.f)); + + STATIC_REQUIRE(length2(10.f) == approx(100.f)); + STATIC_REQUIRE(length2(-10.f) == approx(100.f)); + + STATIC_REQUIRE(distance(5.f, 10.f) == approx(5.f)); + STATIC_REQUIRE(distance(-5.f, -10.f) == approx(5.f)); + + STATIC_REQUIRE(distance2(5.f, 10.f) == approx(25.f)); + STATIC_REQUIRE(distance2(-5.f, -10.f) == approx(25.f)); + + STATIC_REQUIRE(dot(2.f, 5.f) == approx(10.f)); + REQUIRE(normalize(0.5f) == approx(1.f)); + + STATIC_REQUIRE(faceforward(1.f, 2.f, 3.f) == approx(-1.f)); + STATIC_REQUIRE(reflect(1.f, 2.f) == approx(-7.f)); + REQUIRE(refract(1.f, 2.f, 1.f) == approx(-7.f)); + } + + SECTION("Scalar Relational Functions") { + STATIC_REQUIRE(less(0, 1)); + STATIC_REQUIRE(less_equal(0, 1)); + STATIC_REQUIRE_FALSE(less(1, 1)); + STATIC_REQUIRE(less_equal(1, 1)); + + STATIC_REQUIRE(greater(1, 0)); + STATIC_REQUIRE(greater_equal(1, 0)); + STATIC_REQUIRE_FALSE(greater(1, 1)); + STATIC_REQUIRE(greater_equal(1, 1)); + + STATIC_REQUIRE(equal_to(1, 1)); + STATIC_REQUIRE_FALSE(equal_to(0, 1)); + STATIC_REQUIRE_FALSE(equal_to(0, 1, 0)); + STATIC_REQUIRE(equal_to(0, 1, 1)); + + STATIC_REQUIRE(not_equal_to(0, 1)); + STATIC_REQUIRE(not_equal_to(0, 1, 0)); + STATIC_REQUIRE_FALSE(not_equal_to(0, 1, 1)); + STATIC_REQUIRE_FALSE(not_equal_to(1, 1)); + STATIC_REQUIRE_FALSE(not_equal_to(1, 1, 0)); + STATIC_REQUIRE_FALSE(not_equal_to(1, 1, 1)); + + STATIC_REQUIRE_FALSE(any(false)); + STATIC_REQUIRE_FALSE(any(0)); + STATIC_REQUIRE(any(true)); + STATIC_REQUIRE(any(1)); + + STATIC_REQUIRE_FALSE(all(false)); + STATIC_REQUIRE_FALSE(all(0)); + STATIC_REQUIRE(all(true)); + STATIC_REQUIRE(all(1)); + } +} diff --git a/untests/vmath_mat_fun_tests.cpp b/untests/vmath_mat_fun_tests.cpp new file mode 100644 index 0000000..4fcab3b --- /dev/null +++ b/untests/vmath_mat_fun_tests.cpp @@ -0,0 +1,201 @@ +/******************************************************************************* + * 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 + +#include "vmath_tests.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; + + template < typename T, int Size > + constexpr mat generate_frank_matrix() { + mat m; + for ( int i = 1; i <= Size; ++i ) { + for ( int j = 1; j <= Size; ++j ) { + if ( j < i - Size ) { + m[i - 1][j - 1] = 0; + } else if ( j == (i - 1) ) { + m[i - 1][j - 1] = Size + 1 - i; + } else { + m[i - 1][j - 1] = Size + 1 - j; + } + } + } + return m; + } +} + +TEST_CASE("vmath/mat_fun") { + SECTION("Detail") { + STATIC_REQUIRE(map([](const int2& x){ + return x * 2; + }, int2x2{}) == int2x2(2,0,0,2)); + + STATIC_REQUIRE(zip([](const int2& x, const int2& y){ + return x + y; + }, int2x2{}, int2x2{}) == int2x2(2,0,0,2)); + + STATIC_REQUIRE(zip([](const int2& x, const int2& y, const int2& z){ + return x + y + z; + }, int2x2{}, int2x2{}, int2x2{}) == int2x2(3,0,0,3)); + + STATIC_REQUIRE(fold([](int acc, const int2& x){ + return acc + x.x; + }, 0, int2x2{}) == 1); + + STATIC_REQUIRE(fold([](int acc, const int2& x, const int2& y){ + return acc + x.x + y.x; + }, 0, int2x2{}, int2x2{}) == 2); + + STATIC_REQUIRE(fold1([](const int2& acc, const int2& x){ + return acc + x; + }, int2x2{}) == int2(1,1)); + } + + SECTION("Operators") { + STATIC_REQUIRE(-int2x2(1,2,3,4) == int2x2(-1,-2,-3,-4)); + + STATIC_REQUIRE(int2x2(1,2,3,4) + 2 == int2x2(3,4,5,6)); + STATIC_REQUIRE(int2x2(1,2,3,4) - 2 == int2x2(-1,0,1,2)); + STATIC_REQUIRE(int2x2(1,2,3,4) * 2 == int2x2(2,4,6,8)); + STATIC_REQUIRE(int2x2(1,2,3,4) / 2 == int2x2(0,1,1,2)); + + STATIC_REQUIRE(4 + int2x2(1,2,3,4) == int2x2(5,6,7,8)); + STATIC_REQUIRE(4 - int2x2(1,2,3,4) == int2x2(3,2,1,0)); + STATIC_REQUIRE(4 * int2x2(1,2,3,4) == int2x2(4,8,12,16)); + STATIC_REQUIRE(4 / int2x2(1,2,3,4) == int2x2(4,2,1,1)); + + STATIC_REQUIRE(int2x2(1,2,3,4) + int2x2(5,6,7,8) == int2x2(6,8,10,12)); + STATIC_REQUIRE(int2x2(1,2,3,4) - int2x2(5,6,7,8) == int2x2(-4,-4,-4,-4)); + STATIC_REQUIRE(int2x2(5,6,7,8) / int2x2(1,2,3,4) == int2x2(5,3,2,2)); + + STATIC_REQUIRE(int2x2() * int2x2() == int2x2()); + STATIC_REQUIRE(int3x3() * int3x3() == int3x3()); + + STATIC_REQUIRE(int2(1,2) * int2x2() == int2(1,2)); + STATIC_REQUIRE(int3(1,2,3) * int3x3() == int3(1,2,3)); + STATIC_REQUIRE(int4(1,2,3,4) * int4x4() == int4(1,2,3,4)); + + { + int2x2 v{1,2,3,4}; + REQUIRE(&v == &(v += 3)); + REQUIRE(v == int2x2{4,5,6,7}); + REQUIRE(&v == &(v += int2x2{1,2,3,4})); + REQUIRE(v == int2x2{5,7,9,11}); + } + { + int2x2 v{4,5,6,7}; + REQUIRE(&v == &(v -= 3)); + REQUIRE(v == int2x2{1,2,3,4}); + REQUIRE(&v == &(v -= int2x2{2,4,6,8})); + REQUIRE(v == int2x2{-1,-2,-3,-4}); + } + { + int2x2 v{1,2,3,4}; + REQUIRE(&v == &(v *= 3)); + REQUIRE(v == int2x2{3,6,9,12}); + } + { + int2x2 v{6,18,36,60}; + REQUIRE(&v == &(v /= 2)); + REQUIRE(v == int2x2{3,9,18,30}); + REQUIRE(&v == &(v /= int2x2{3,4,3,10})); + REQUIRE(v == int2x2{1,2,6,3}); + } + + { + int4 v{0, 0, 0, 1}; + REQUIRE(&v == &(v *= translate(int3{1,2,3}))); + REQUIRE(v == approx4(1,2,3,1)); + } + { + int3 v{1, 2, 3}; + REQUIRE(&v == &(v *= int3x3(scale(int3{2,3,4})))); + REQUIRE(v == int3(2,6,12)); + } + { + int4x4 v = translate(int3{1, 2, 3}); + REQUIRE(&v == &(v *= translate(int3{1,2,3}))); + REQUIRE(v == translate(int3{2,4,6})); + } + { + int3x3 v = int3x3(scale(int3{1, 2, 3})); + REQUIRE(&v == &(v *= int3x3(scale(int3{2,3,4})))); + REQUIRE(v == int3x3(scale(int3{2,6,12}))); + } + } + + SECTION("Matrix Functions") { + { + STATIC_REQUIRE(transpose(int2x2( + 1, 2, + 3, 4 + )) == int2x2( + 1, 3, + 2, 4 + )); + + STATIC_REQUIRE(transpose(int3x3( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9 + )) == int3x3( + 1, 4, 7, + 2, 5, 8, + 3, 6, 9 + )); + + STATIC_REQUIRE(transpose(int4x4( + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + )) == int4x4( + 1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 4, 8, 12, 16 + )); + } + { + constexpr int2x2 m2{1,2,3,4}; + constexpr int3x3 m3{1,2,3,4,5,6,7,8,9}; + constexpr int4x4 m4{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + STATIC_REQUIRE(determinant(m2) == determinant(transpose(m2))); + STATIC_REQUIRE(determinant(m3) == determinant(transpose(m3))); + STATIC_REQUIRE(determinant(m4) == determinant(transpose(m4))); + + STATIC_REQUIRE(determinant(generate_frank_matrix()) == 1); + STATIC_REQUIRE(determinant(generate_frank_matrix()) == 1); + STATIC_REQUIRE(determinant(generate_frank_matrix()) == 1); + + STATIC_REQUIRE(determinant(transpose(generate_frank_matrix())) == 1); + STATIC_REQUIRE(determinant(transpose(generate_frank_matrix())) == 1); + STATIC_REQUIRE(determinant(transpose(generate_frank_matrix())) == 1); + } + { + STATIC_REQUIRE(inverse(float2x2()) == float2x2()); + STATIC_REQUIRE(inverse(float3x3()) == float3x3()); + STATIC_REQUIRE(inverse(float4x4()) == float4x4()); + + STATIC_REQUIRE(inverse(float2x2(0.5)) == float2x2(2.f)); + STATIC_REQUIRE(inverse(float3x3(0.5)) == float3x3(2.f)); + STATIC_REQUIRE(inverse(float4x4(0.5)) == float4x4(2.f)); + + STATIC_REQUIRE(inverse(translate(float3{1.f,2.f,3.f})) == approx4x4(translate(float3{-1.f,-2.f,-3.f}))); + + REQUIRE(inverse(rotate(0.5f,normalize(float3{1.f,2.f,3.f}))) == approx4x4(rotate(-0.5f,normalize(float3{1.f,2.f,3.f})))); + REQUIRE(inverse(float3x3(rotate(0.5f,normalize(float3{1.f,2.f,3.f})))) == approx3x3(float3x3(rotate(-0.5f,normalize(float3{1.f,2.f,3.f}))))); + REQUIRE(inverse(float2x2(rotate(0.5f,float3{0,0,1}))) == approx2x2(float2x2(rotate(-0.5f,float3{0,0,1})))); + } + } +} diff --git a/untests/vmath_mat_tests.cpp b/untests/vmath_mat_tests.cpp new file mode 100644 index 0000000..b48fde8 --- /dev/null +++ b/untests/vmath_mat_tests.cpp @@ -0,0 +1,174 @@ +/******************************************************************************* + * 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 +#include + +#define CATCH_CONFIG_FAST_COMPILE +#include + +#include "vmath_tests.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/mat") { + SECTION("size/sizeof") { + STATIC_REQUIRE(int2x2{}.size == 2); + STATIC_REQUIRE(int3x3{}.size == 3); + STATIC_REQUIRE(int4x4{}.size == 4); + + STATIC_REQUIRE(sizeof(int2x2{}) == sizeof(int) * 2 * 2); + STATIC_REQUIRE(sizeof(int3x3{}) == sizeof(int) * 3 * 3); + STATIC_REQUIRE(sizeof(int4x4{}) == sizeof(int) * 4 * 4); + } + + SECTION("ctors") { + { + STATIC_REQUIRE(int2x2()[0] == int2(1,0)); + STATIC_REQUIRE(int2x2()[1] == int2(0,1)); + + STATIC_REQUIRE(int2x2(1,2,3,4)[0] == int2(1,2)); + STATIC_REQUIRE(int2x2(1,2,3,4)[1] == int2(3,4)); + + STATIC_REQUIRE(int2x2({1,2},{3,4})[0] == int2(1,2)); + STATIC_REQUIRE(int2x2({1,2},{3,4})[1] == int2(3,4)); + } + { + constexpr int2x2 v(1,2,3,4); + constexpr int2x2 v2 = v; + STATIC_REQUIRE(v2 == int2x2(1,2,3,4)); + } + { + constexpr int2x2 v(1,2,3,4); + constexpr int2x2 v2 = std::move(v); + STATIC_REQUIRE(v2 == int2x2(1,2,3,4)); + } + { + STATIC_REQUIRE(int2x2() == int2x2(1,0,0,1)); + STATIC_REQUIRE(int2x2(2) == int2x2(2,0,0,2)); + STATIC_REQUIRE(int2x2(1,2,3,4) == int2x2(1,2,3,4)); + STATIC_REQUIRE(int2x2({1,2},{3,4}) == int2x2(1,2,3,4)); + STATIC_REQUIRE(int2x2(int2x2({1,2},{3,4})) == int2x2(1,2,3,4)); + STATIC_REQUIRE(int2x2(int3x3({1,2,3},{4,5,6},{7,8,9})) == int2x2(1,2,4,5)); + STATIC_REQUIRE(int2x2(int4x4({1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16})) == int2x2(1,2,5,6)); + + STATIC_REQUIRE(int3x3() == int3x3(1,0,0,0,1,0,0,0,1)); + STATIC_REQUIRE(int3x3(2) == int3x3(2,0,0,0,2,0,0,0,2)); + STATIC_REQUIRE(int3x3(1,2,3,4,5,6,7,8,9) == int3x3(1,2,3,4,5,6,7,8,9)); + STATIC_REQUIRE(int3x3({1,2,3},{4,5,6},{7,8,9}) == int3x3(1,2,3,4,5,6,7,8,9)); + STATIC_REQUIRE(int3x3(int3x3({1,2,3},{4,5,6},{7,8,9})) == int3x3(1,2,3,4,5,6,7,8,9)); + STATIC_REQUIRE(int3x3(int2x2({1,2},{3,4})) == int3x3(1,2,0,3,4,0,0,0,1)); + STATIC_REQUIRE(int3x3(int4x4({1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16})) == int3x3(1,2,3,5,6,7,9,10,11)); + + STATIC_REQUIRE(int4x4() == int4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)); + STATIC_REQUIRE(int4x4(2) == int4x4(2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2)); + STATIC_REQUIRE(int4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16) == int4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + STATIC_REQUIRE(int4x4({1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}) == int4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + STATIC_REQUIRE(int4x4(int4x4({1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16})) == int4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + STATIC_REQUIRE(int4x4(int2x2({1,2},{3,4})) == int4x4(1,2,0,0,3,4,0,0,0,0,1,0,0,0,0,1)); + STATIC_REQUIRE(int4x4(int3x3({1,2,3},{4,5,6},{7,8,9})) == int4x4(1,2,3,0,4,5,6,0,7,8,9,0,0,0,0,1)); + } + } + + SECTION("operator=") { + { + int2x2 v(1,2,3,4); + int2x2 v2; + v2 = v; + REQUIRE(v2 == int2x2(1,2,3,4)); + } + { + int2x2 v(1,2,3,4); + int2x2 v2; + v2 = std::move(v); + REQUIRE(v2 == int2x2(1,2,3,4)); + } + } + + SECTION("swap") { + { + int2x2 v1(1,2,3,4); + int2x2 v2(4,5,6,7); + v1.swap(v2); + REQUIRE(v1 == int2x2(4,5,6,7)); + REQUIRE(v2 == int2x2(1,2,3,4)); + } + { + int2x2 v1(1,2,3,4); + int2x2 v2(4,5,6,7); + swap(v1, v2); + REQUIRE(v1 == int2x2(4,5,6,7)); + REQUIRE(v2 == int2x2(1,2,3,4)); + } + } + + SECTION("operator[]") { + { + STATIC_REQUIRE(int2x2()[0] == int2(1,0)); + STATIC_REQUIRE(int2x2()[1] == int2(0,1)); + + STATIC_REQUIRE(int3x3()[0] == int3(1,0,0)); + STATIC_REQUIRE(int3x3()[1] == int3(0,1,0)); + STATIC_REQUIRE(int3x3()[2] == int3(0,0,1)); + + STATIC_REQUIRE(int4x4()[0] == int4(1,0,0,0)); + STATIC_REQUIRE(int4x4()[1] == int4(0,1,0,0)); + STATIC_REQUIRE(int4x4()[2] == int4(0,0,1,0)); + STATIC_REQUIRE(int4x4()[3] == int4(0,0,0,1)); + } + { + int2x2 v; + v[0] = int2(1,2); + v[1] = int2(3,4); + REQUIRE(v == int2x2(1,2,3,4)); + } + { + int3x3 v; + v[0] = int3(1,2,3); + v[1] = int3(4,5,6); + v[2] = int3(7,8,9); + REQUIRE(v == int3x3(1,2,3,4,5,6,7,8,9)); + } + { + int4x4 v; + v[0] = int4(1,2,3,4); + v[1] = int4(5,6,7,8); + v[2] = int4(9,10,11,12); + v[3] = int4(13,14,15,16); + REQUIRE(v == int4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + } + } + + SECTION("at") { + STATIC_REQUIRE(int2x2(1,2,3,4).at(0) == int2(1,2)); + STATIC_REQUIRE(int2x2(1,2,3,4).at(1) == int2(3,4)); + REQUIRE_THROWS_AS(int2x2(1,2,3,4).at(2), std::out_of_range); + } + + SECTION("operator==/operator!=") { + STATIC_REQUIRE(int2x2(1,2,3,4) == int2x2(1,2,3,4)); + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) == int2x2(2,2,3,4)); + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) == int2x2(1,3,3,4)); + + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) != int2x2(1,2,3,4)); + STATIC_REQUIRE(int2x2(1,2,3,4) != int2x2(2,2,3,4)); + STATIC_REQUIRE(int2x2(1,2,3,4) != int2x2(1,3,3,4)); + } + + SECTION("operator<") { + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) < int2x2(1,2,3,4)); + + STATIC_REQUIRE(int2x2(1,1,3,4) < int2x2(1,2,3,4)); + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) < int2x2(1,1,3,4)); + + STATIC_REQUIRE(int2x2(0,3,3,4) < int2x2(1,2,3,4)); + STATIC_REQUIRE_FALSE(int2x2(1,2,3,4) < int2x2(0,3,3,4)); + } +} diff --git a/untests/vmath_tests.cpp b/untests/vmath_tests.cpp deleted file mode 100644 index 2407089..0000000 --- a/untests/vmath_tests.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/******************************************************************************* - * 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) - ******************************************************************************/ - -#define CATCH_CONFIG_FAST_COMPILE -#include - -#include - -namespace -{ -} - -TEST_CASE("vmath") { - REQUIRE(true); -} diff --git a/untests/vmath_tests.hpp b/untests/vmath_tests.hpp new file mode 100644 index 0000000..c57ce5f --- /dev/null +++ b/untests/vmath_tests.hpp @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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 +#include + +#include + +namespace vmath_tests +{ + using namespace vmath_hpp; + + template < typename T > + inline constexpr T epsilon = std::numeric_limits::epsilon() * 100; + + template < typename T > + struct approx { + T value; + explicit constexpr approx(T v) : value(v) {} + + friend constexpr bool operator==(const T& l, const approx& r) { + return equal_to(l, r.value, epsilon); + } + }; + + template < typename T > + struct approx2 { + vec value; + constexpr explicit approx2(T v) : value(v) {} + constexpr explicit approx2(T x, T y) : value(x, y) {} + constexpr explicit approx2(const vec& v) : value(v) {} + + friend constexpr bool operator==(const vec& l, const approx2& r) { + return all(equal_to(l, r.value, epsilon)); + } + }; + + template < typename T > + struct approx3 { + vec value; + constexpr explicit approx3(T v) : value(v) {} + constexpr explicit approx3(T x, T y, T z) : value(x, y, z) {} + constexpr explicit approx3(const vec& v) : value(v) {} + + friend constexpr bool operator==(const vec& l, const approx3& r) { + return all(equal_to(l, r.value, epsilon)); + } + }; + + template < typename T > + struct approx4 { + vec value; + constexpr explicit approx4(T v) : value(v) {} + constexpr explicit approx4(T x, T y, T z, T w) : value(x, y, z, w) {} + constexpr explicit approx4(const vec& v) : value(v) {} + + friend constexpr bool operator==(const vec& l, const approx4& r) { + return all(equal_to(l, r.value, epsilon)); + } + }; + + template < typename T > + struct approx2x2 { + mat value; + constexpr explicit approx2x2(const mat& v) : value(v) {} + + friend constexpr bool operator==(const mat& l, const approx2x2& r) { + return l[0] == approx2(r.value[0]) + && l[1] == approx2(r.value[1]); + } + }; + + template < typename T > + struct approx3x3 { + mat value; + constexpr explicit approx3x3(const mat& v) : value(v) {} + + friend constexpr bool operator==(const mat& l, const approx3x3& r) { + return l[0] == approx3(r.value[0]) + && l[1] == approx3(r.value[1]) + && l[2] == approx3(r.value[2]); + } + }; + + template < typename T > + struct approx4x4 { + mat value; + constexpr explicit approx4x4(const mat& v) : value(v) {} + + friend constexpr bool operator==(const mat& l, const approx4x4& r) { + return l[0] == approx4(r.value[0]) + && l[1] == approx4(r.value[1]) + && l[2] == approx4(r.value[2]) + && l[3] == approx4(r.value[3]); + } + }; +} diff --git a/untests/vmath_vec_fun_tests.cpp b/untests/vmath_vec_fun_tests.cpp new file mode 100644 index 0000000..c1ce6dd --- /dev/null +++ b/untests/vmath_vec_fun_tests.cpp @@ -0,0 +1,246 @@ +/******************************************************************************* + * 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 + +#include "vmath_tests.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/vec_fun") { + SECTION("Detail") { + STATIC_REQUIRE(map([](const int& x){ + return x * 2; + }, int2{1}) == int2{2}); + + STATIC_REQUIRE(zip([](const int& x, const int& y){ + return x + y; + }, int2{1}, int2{1}) == int2{2}); + + STATIC_REQUIRE(zip([](const int& x, const int& y, const int& z){ + return x + y + z; + }, int2{1}, int2{1}, int2{1}) == int2(3)); + + STATIC_REQUIRE(fold([](int acc, const int& x){ + return acc + x; + }, 0, int2{1}) == 2); + + STATIC_REQUIRE(fold([](int acc, const int& x, const int& y){ + return acc + x + y; + }, 0, int2{1}, int2{1}) == 4); + + STATIC_REQUIRE(fold1([](const int& acc, const int& x){ + return acc + x; + }, int2{1}) == 2); + } + + SECTION("Operators") { + STATIC_REQUIRE(-int2(1,-2) == int2(-1,2)); + + STATIC_REQUIRE(int2(1,2) + 3 == int2(4,5)); + STATIC_REQUIRE(int2(1,2) - 3 == int2(-2,-1)); + STATIC_REQUIRE(int2(1,2) * 3 == int2(3,6)); + STATIC_REQUIRE(int2(2,4) / 2 == int2(1,2)); + + STATIC_REQUIRE(3 + int2(1,2) == int2(4,5)); + STATIC_REQUIRE(3 - int2(1,2) == int2(2,1)); + STATIC_REQUIRE(3 * int2(1,2) == int2(3,6)); + STATIC_REQUIRE(4 / int2(2,4) == int2(2,1)); + + STATIC_REQUIRE(int2(1,2) + int2(3,4) == int2(4,6)); + STATIC_REQUIRE(int2(1,2) - int2(3,4) == int2(-2,-2)); + STATIC_REQUIRE(int2(1,2) * int2(3,4) == int2(3,8)); + STATIC_REQUIRE(int2(3,4) / int2(1,2) == int2(3,2)); + + { + int2 v{1,2}; + REQUIRE(&v == &(v += 3)); + REQUIRE(v == int2{4,5}); + REQUIRE(&v == &(v += int2{1,2})); + REQUIRE(v == int2{5,7}); + } + { + int2 v{4,5}; + REQUIRE(&v == &(v -= 3)); + REQUIRE(v == int2{1,2}); + REQUIRE(&v == &(v -= int2{2,4})); + REQUIRE(v == int2{-1,-2}); + } + { + int2 v{1,2}; + REQUIRE(&v == &(v *= 3)); + REQUIRE(v == int2{3,6}); + REQUIRE(&v == &(v *= int2{2,3})); + REQUIRE(v == int2{6,18}); + } + { + int2 v{6,18}; + REQUIRE(&v == &(v /= 2)); + REQUIRE(v == int2{3,9}); + REQUIRE(&v == &(v /= int2{3,4})); + REQUIRE(v == int2{1,2}); + } + } + + SECTION("Angle and Trigonometry Functions") { + STATIC_REQUIRE(radians(degrees(float2(12.13f))) == approx2(12.13f)); + STATIC_REQUIRE(degrees(radians(float2(12.13f))) == approx2(12.13f)); + + (void)sin(float2(1.f)); + (void)cos(float2(1.f)); + (void)tan(float2(1.f)); + + (void)asin(float2(1.f)); + (void)acos(float2(1.f)); + (void)atan(float2(1.f)); + (void)atan2(float2(1.f), float2(1.f)); + + (void)sinh(float2(1.f)); + (void)cosh(float2(1.f)); + (void)tanh(float2(1.f)); + + (void)asinh(float2(1.f)); + (void)acosh(float2(1.f)); + (void)atanh(float2(1.f)); + } + + SECTION("Exponential Functions") { + (void)pow(float2(1.f), float2(2.f)); + (void)exp(float2(1.f)); + (void)log(float2(1.f)); + (void)exp2(float2(1.f)); + (void)log2(float2(1.f)); + (void)sqrt(float2(1.f)); + (void)rsqrt(float2(1.f)); + } + + SECTION("Common Functions") { + STATIC_REQUIRE(abs(float2(1.f, -1.f)) == approx2(1.f,1.f)); + STATIC_REQUIRE(sign(float3(1.f, -1.f, 0.f)) == approx3(1.f,-1.f,0.f)); + STATIC_REQUIRE(reciprocal(float2(2.f, 4.f)) == approx2(0.5f,0.25f)); + + (void)floor(float2(1.f, -1.f)); + (void)trunc(float2(1.f, -1.f)); + (void)round(float2(1.f, -1.f)); + (void)ceil(float2(1.f, -1.f)); + (void)fract(float2(1.f, -1.f)); + + REQUIRE(fmod(float2(1.7f), 1.2f) == approx2(0.5f)); + REQUIRE(fmod(float2(1.7f), float2(1.2f)) == approx2(0.5f)); + + { + float2 out_i{}; + REQUIRE(modf(float2(1.7f), &out_i) == approx2(0.7f)); + REQUIRE(out_i.x == Approx(1.f)); + } + + STATIC_REQUIRE(min(int2(1,2)) == 1); + STATIC_REQUIRE(min(int2(1,2), 1) == int2(1,1)); + STATIC_REQUIRE(min(int2(1,1), int2(0,2)) == int2(0,1)); + + STATIC_REQUIRE(max(int2(1,2)) == 2); + STATIC_REQUIRE(max(int2(1,2), 1) == int2(1,2)); + STATIC_REQUIRE(max(int2(1,1), int2(0,2)) == int2(1,2)); + + STATIC_REQUIRE(clamp(int2(1,2), 0, 1) == int2(1,1)); + STATIC_REQUIRE(clamp(int2(1,2), int2(0), int2(1)) == int2(1,1)); + + STATIC_REQUIRE(saturate(float3(-1.f,0.5,1.5f)) == approx3(0.f,0.5f,1.f)); + + STATIC_REQUIRE(lerp(float2(0.f), float2(10.f), 0.5f) == approx2(5.f)); + STATIC_REQUIRE(lerp(float2(0.f), float2(10.f), float2(0.5f)) == approx2(5.f)); + + STATIC_REQUIRE(step(0.5f, float2(0.4f)) == approx2(0.f)); + STATIC_REQUIRE(step(0.5f, float2(0.6f)) == approx2(1.f)); + STATIC_REQUIRE(step(float2(0.5f), float2(0.4f)) == approx2(0.f)); + STATIC_REQUIRE(step(float2(0.5f), float2(0.6f)) == approx2(1.f)); + + STATIC_REQUIRE(smoothstep(0.f, 1.f, float2(0.1f)) == approx2(0.028f)); + STATIC_REQUIRE(smoothstep(float2(0.f), float2(1.f), float2(0.1f)) == approx2(0.028f)); + + REQUIRE_FALSE(isnan(float2(1.f)).x); + REQUIRE_FALSE(isinf(float2(1.f)).x); + REQUIRE(isfinite(float2(1.f)).x); + + REQUIRE_FALSE(fma(float2(2.f), float2(3.f), float2(4.f)).x == Approx(12.f)); + + { + int2 out_exp{}; + REQUIRE(frexp(float2(1.7f), &out_exp).x == Approx(0.85f)); + REQUIRE(out_exp == int2(1)); + } + + REQUIRE(ldexp(float2(0.85f), int2(1)).x == Approx(1.7f)); + } + + SECTION("Geometric Functions") { + REQUIRE(length(float2(10.f,0.f)) == Approx(10.f)); + REQUIRE(length(float2(-10.f,0.f)) == Approx(10.f)); + + STATIC_REQUIRE(length2(float2(10.f,0.f)) == approx(100.f)); + STATIC_REQUIRE(length2(float2(-10.f,0.f)) == approx(100.f)); + + REQUIRE(distance(float2(5.f,0.f), float2(10.f,0.f)) == Approx(5.f)); + REQUIRE(distance(float2(-5.f,0.f), float2(-10.f,0.f)) == Approx(5.f)); + + STATIC_REQUIRE(distance2(float2(5.f,0.f), float2(10.f,0.f)) == approx(25.f)); + STATIC_REQUIRE(distance2(float2(-5.f,0.f), float2(-10.f,0.f)) == approx(25.f)); + + STATIC_REQUIRE(dot(int2(1,2),int2(3,4)) == 11); + STATIC_REQUIRE(cross(int2(1,0),int2(0,1)) == 1); + STATIC_REQUIRE(cross(int3(1,0,0),int3(0,1,0)) == int3(0,0,1)); + REQUIRE(normalize(float2(0.5f,0.f)).x == Approx(1.f)); + + STATIC_REQUIRE(faceforward(float2(1.f), float2(2.f), float2(3.f)).x == approx(-1.f)); + STATIC_REQUIRE(reflect(float2(1.f), float2(2.f)).x == approx(-15.f)); + REQUIRE(refract(float2(1.f), float2(2.f), 1.f).x == Approx(-15.f)); + } + + SECTION("Vector Relational Functions") { + STATIC_REQUIRE(less(int3(1,1,1), int3(0,1,2)) == bool3(false, false, true)); + STATIC_REQUIRE(less_equal(int3(1,1,1), int3(0,1,2)) == bool3(false, true, true)); + + STATIC_REQUIRE(greater(int3(1,1,1), int3(0,1,2)) == bool3(true, false, false)); + STATIC_REQUIRE(greater_equal(int3(1,1,1), int3(0,1,2)) == bool3(true, true, false)); + + STATIC_REQUIRE(equal_to(int3(1,1,1), int3(0,1,2)) == bool3(false, true, false)); + STATIC_REQUIRE(equal_to(int4(1,1,1,1), int4(0,1,2,3), 0) == bool4(false, true, false, false)); + STATIC_REQUIRE(equal_to(int4(1,1,1,1), int4(0,1,2,3), 1) == bool4(true, true, true, false)); + STATIC_REQUIRE(equal_to(int4(1,1,1,1), int4(0,1,2,3), 2) == bool4(true, true, true, true)); + + STATIC_REQUIRE(not_equal_to(int3(1,1,1), int3(0,1,2)) == bool3(true, false, true)); + STATIC_REQUIRE(not_equal_to(int4(1,1,1,1), int4(0,1,2,3), 0) == bool4(true, false, true, true)); + STATIC_REQUIRE(not_equal_to(int4(1,1,1,1), int4(0,1,2,3), 1) == bool4(false, false, false, true)); + STATIC_REQUIRE(not_equal_to(int4(1,1,1,1), int4(0,1,2,3), 2) == bool4(false, false, false, false)); + + STATIC_REQUIRE_FALSE(any(bool2(false, false))); + STATIC_REQUIRE(any(bool2(true, false))); + STATIC_REQUIRE(any(bool2(false, true))); + STATIC_REQUIRE(any(bool2(true, true))); + + STATIC_REQUIRE_FALSE(any(int2(0, 0))); + STATIC_REQUIRE(any(int2(1, 0))); + STATIC_REQUIRE(any(int2(0, 1))); + STATIC_REQUIRE(any(int2(1, 1))); + + STATIC_REQUIRE_FALSE(all(bool2(false, false))); + STATIC_REQUIRE_FALSE(all(bool2(true, false))); + STATIC_REQUIRE_FALSE(all(bool2(false, true))); + STATIC_REQUIRE(all(bool2(true, true))); + + STATIC_REQUIRE_FALSE(all(int2(0, 0))); + STATIC_REQUIRE_FALSE(all(int2(1, 0))); + STATIC_REQUIRE_FALSE(all(int2(0, 1))); + STATIC_REQUIRE(all(int2(1, 1))); + } +} diff --git a/untests/vmath_vec_tests.cpp b/untests/vmath_vec_tests.cpp new file mode 100644 index 0000000..9d75355 --- /dev/null +++ b/untests/vmath_vec_tests.cpp @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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 +#include + +#define CATCH_CONFIG_FAST_COMPILE +#include + +#include "vmath_tests.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/vec") { + SECTION("size/sizeof") { + STATIC_REQUIRE(int2{}.size == 2); + STATIC_REQUIRE(int3{}.size == 3); + STATIC_REQUIRE(int4{}.size == 4); + + STATIC_REQUIRE(sizeof(int2{}) == sizeof(int) * 2); + STATIC_REQUIRE(sizeof(int3{}) == sizeof(int) * 3); + STATIC_REQUIRE(sizeof(int4{}) == sizeof(int) * 4); + } + + SECTION("ctors") { + { + STATIC_REQUIRE(int2().x == 0); + STATIC_REQUIRE(int2().y == 0); + + STATIC_REQUIRE(int2(1).x == 1); + STATIC_REQUIRE(int2(1).y == 1); + + STATIC_REQUIRE(int2(1,2).x == 1); + STATIC_REQUIRE(int2(1,2).y == 2); + } + { + constexpr int2 v(1,2); + constexpr int2 v2 = v; + STATIC_REQUIRE(v2 == int2(1,2)); + } + { + constexpr int2 v(1,2); + constexpr int2 v2 = std::move(v); + STATIC_REQUIRE(v2 == int2(1,2)); + } + { + STATIC_REQUIRE(int2(1) == int2(1,1)); + STATIC_REQUIRE(int2(1,2) == int2(1,2)); + STATIC_REQUIRE(int2(int2(1,2)) == int2(1,2)); + STATIC_REQUIRE(int2(int3(1,2,3)) == int2(1,2)); + STATIC_REQUIRE(int2(int4(1,2,3,4)) == int2(1,2)); + + STATIC_REQUIRE(int3(1) == int3(1,1,1)); + STATIC_REQUIRE(int3(1,2,3) == int3(1,2,3)); + STATIC_REQUIRE(int3(int2(1,2),3) == int3(1,2,3)); + STATIC_REQUIRE(int3(1,int2(2,3)) == int3(1,2,3)); + STATIC_REQUIRE(int3(int3(1,2,3)) == int3(1,2,3)); + STATIC_REQUIRE(int3(int4(1,2,3,4)) == int3(1,2,3)); + + STATIC_REQUIRE(int4(1) == int4(1,1,1,1)); + STATIC_REQUIRE(int4(1,2,3,4) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(int2(1,2),3,4) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(1,int2(2,3),4) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(1,2,int2(3,4)) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(int2(1,2),int2(3,4)) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(int3(1,2,3),4) == int4(1,2,3,4)); + STATIC_REQUIRE(int4(1,int3(2,3,4)) == int4(1,2,3,4)); + } + } + + SECTION("operator=") { + { + int2 v(1,2); + int2 v2; + v2 = v; + REQUIRE(v2 == int2(1,2)); + } + { + int2 v(1,2); + int2 v2; + v2 = std::move(v); + REQUIRE(v2 == int2(1,2)); + } + } + + SECTION("swap") { + { + int2 v1(1,2); + int2 v2(4,5); + v1.swap(v2); + REQUIRE(v1 == int2(4,5)); + REQUIRE(v2 == int2(1,2)); + } + { + int2 v1(1,2); + int2 v2(4,5); + swap(v1, v2); + REQUIRE(v1 == int2(4,5)); + REQUIRE(v2 == int2(1,2)); + } + } + + SECTION("operator[]") { + { + STATIC_REQUIRE(int2(1,2).x == 1); + STATIC_REQUIRE(int2(1,2).y == 2); + + STATIC_REQUIRE(int3(1,2,3).x == 1); + STATIC_REQUIRE(int3(1,2,3).y == 2); + STATIC_REQUIRE(int3(1,2,3).z == 3); + + STATIC_REQUIRE(int4(1,2,3,4).x == 1); + STATIC_REQUIRE(int4(1,2,3,4).y == 2); + STATIC_REQUIRE(int4(1,2,3,4).z == 3); + STATIC_REQUIRE(int4(1,2,3,4).w == 4); + } + { + STATIC_REQUIRE(int2(1,2)[0] == 1); + STATIC_REQUIRE(int2(1,2)[1] == 2); + } + { + int2 v; + v.x = 1; + v.y = 2; + REQUIRE(v == int2(1,2)); + } + { + int3 v; + v.x = 1; + v.y = 2; + v.z = 3; + REQUIRE(v == int3(1,2,3)); + } + { + int4 v; + v.x = 1; + v.y = 2; + v.z = 3; + v.w = 4; + REQUIRE(v == int4(1,2,3,4)); + } + } + + SECTION("at") { + STATIC_REQUIRE(int2(1,2).at(0) == 1); + STATIC_REQUIRE(int2(1,2).at(1) == 2); + REQUIRE_THROWS_AS(int2(1,2).at(2), std::out_of_range); + } + + SECTION("operator==/operator!=") { + STATIC_REQUIRE(int2(1,2) == int2(1,2)); + STATIC_REQUIRE_FALSE(int2(1,2) == int2(2,2)); + STATIC_REQUIRE_FALSE(int2(1,2) == int2(1,3)); + + STATIC_REQUIRE_FALSE(int2(1,2) != int2(1,2)); + STATIC_REQUIRE(int2(1,2) != int2(2,2)); + STATIC_REQUIRE(int2(1,2) != int2(1,3)); + } + + SECTION("operator<") { + STATIC_REQUIRE_FALSE(int2(1,2) < int2(1,2)); + + STATIC_REQUIRE(int2(1,1) < int2(1,2)); + STATIC_REQUIRE_FALSE(int2(1,2) < int2(1,1)); + + STATIC_REQUIRE(int2(0,3) < int2(1,2)); + STATIC_REQUIRE_FALSE(int2(1,2) < int2(0,3)); + } +}