/******************************************************************************* * 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::impl { template < typename A, std::size_t Size, typename F, std::size_t... Is > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto map_join_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 VMATH_HPP_FORCE_INLINE auto map_join_impl( F&& f, const vec& a, const vec& b, std::index_sequence ) -> vec(), std::declval())), 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 VMATH_HPP_FORCE_INLINE auto map_join_impl( F&& f, const vec& a, const vec& b, const vec& c, std::index_sequence ) -> vec(), std::declval(), std::declval())), 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 VMATH_HPP_FORCE_INLINE auto fold_join_impl( F&& f, A init, const vec& b, std::index_sequence ) -> A { 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 VMATH_HPP_FORCE_INLINE auto fold_join_impl( F&& f, A init, const vec& b, const vec& c, std::index_sequence ) -> A { 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 VMATH_HPP_FORCE_INLINE auto fold1_join_impl( F&& f, const vec& a, std::index_sequence ) -> A { A init = a[I]; return ((init = f(std::move(init), a[Is])), ...); } } namespace vmath_hpp::detail { template < typename A, std::size_t Size, typename F > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto map_join( F&& f, const vec& a ) { return impl::map_join_impl( std::forward(f), a, std::make_index_sequence{}); } template < typename A, typename B, std::size_t Size, typename F > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto map_join( F&& f, const vec& a, const vec& b ) { return impl::map_join_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 VMATH_HPP_FORCE_INLINE auto map_join( F&& f, const vec& a, const vec& b, const vec& c ) { return impl::map_join_impl( std::forward(f), a, b, c, std::make_index_sequence{}); } template < typename A, typename B, std::size_t Size, typename F > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto fold_join( F&& f, A init, const vec& b ) { return impl::fold_join_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 VMATH_HPP_FORCE_INLINE auto fold_join( F&& f, A init, const vec& b, const vec& c ) { return impl::fold_join_impl( std::forward(f), std::move(init), b, c, std::make_index_sequence{}); } template < typename A, std::size_t Size, typename F > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto fold1_join( F&& f, const vec& a ) { return impl::fold1_join_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_join([](T x){ return -x; }, xs); } // ~operator template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator~(const vec& xs) { return map_join([](T x){ return ~x; }, xs); } // !operator template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator!(const vec& xs) { return map_join([](T x){ return !x; }, xs); } // operator+ template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator+(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x + y; }, 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_join([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_join([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 map_join([](T x, T y){ return x - y; }, 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_join([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_join([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 map_join([](T x, T y){ return x * y; }, 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_join([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_join([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 map_join([](T x, T y){ return x / y; }, 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_join([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_join([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 map_join([](T x, T y){ return x & y; }, 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_join([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_join([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 map_join([](T x, T y){ return x | y; }, 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_join([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_join([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 map_join([](T x, T y){ return x ^ y; }, 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_join([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_join([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 map_join([](T x, T y){ return x && y; }, xs, ys); } // operator|| template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator||(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x || y; }, xs, ys); } // operator== template < typename T, std::size_t Size > [[nodiscard]] constexpr bool operator==(const vec& xs, const vec& ys) { return fold_join([](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 !(xs == ys); } } // // Relational Operators // namespace vmath_hpp { // operator< template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator<(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x < y; }, xs, ys); } // operator<= template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator<=(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x <= y; }, xs, ys); } // operator> template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator>(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x > y; }, xs, ys); } // operator>= template < typename T, std::size_t Size > [[nodiscard]] constexpr vec operator>=(const vec& xs, T y) { return map_join([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_join([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 map_join([](T x, T y){ return x >= y; }, xs, ys); } } // // Angle and Trigonometry Functions // namespace vmath_hpp { template < typename T, std::size_t Size > [[nodiscard]] constexpr vec radians(const vec& degrees) { return map_join([](T x) { return radians(x); }, degrees); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec degrees(const vec& radians) { return map_join([](T x) { return degrees(x); }, radians); } template < typename T, std::size_t Size > [[nodiscard]] vec sin(const vec& xs) { return map_join([](T x) { return sin(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec cos(const vec& xs) { return map_join([](T x) { return cos(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec tan(const vec& xs) { return map_join([](T x) { return tan(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec asin(const vec& xs) { return map_join([](T x) { return asin(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec acos(const vec& xs) { return map_join([](T x) { return acos(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec atan(const vec& xs) { return map_join([](T x) { return atan(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec atan2(const vec& ys, const vec& xs) { return map_join([](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_join([](T x) { return sinh(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec cosh(const vec& xs) { return map_join([](T x) { return cosh(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec tanh(const vec& xs) { return map_join([](T x) { return tanh(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec asinh(const vec& xs) { return map_join([](T x) { return asinh(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec acosh(const vec& xs) { return map_join([](T x) { return acosh(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec atanh(const vec& xs) { return map_join([](T x) { return atanh(x); }, xs); } template < typename T, size_t Size > void sincos(const vec& xs, vec* ss, vec* cs) { *ss = map_join([](T x){ return sin(x); }, xs); *cs = map_join([](T x){ return cos(x); }, xs); } } // // Exponential Functions // namespace vmath_hpp { template < typename T, std::size_t Size > [[nodiscard]] vec pow(const vec& xs, const vec& ys) { return map_join([](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_join([](T x) { return exp(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec log(const vec& xs) { return map_join([](T x) { return log(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec exp2(const vec& xs) { return map_join([](T x) { return exp2(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec log2(const vec& xs) { return map_join([](T x) { return log2(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec sqrt(const vec& xs) { return map_join([](T x) { return sqrt(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec rsqrt(const vec& xs) { return map_join([](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_join([](T x) { return abs(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec sign(const vec& xs) { return map_join([](T x) { return sign(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec reciprocal(const vec& xs) { return map_join([](T x) { return reciprocal(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec floor(const vec& xs) { return map_join([](T x) { return floor(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec trunc(const vec& xs) { return map_join([](T x) { return trunc(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec round(const vec& xs) { return map_join([](T x) { return round(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec ceil(const vec& xs) { return map_join([](T x) { return ceil(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec fract(const vec& xs) { return map_join([](T x) { return fract(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec fmod(const vec& xs, T y) { return map_join([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 map_join([](T x, T y) { return fmod(x, y); }, xs, ys); } namespace impl { template < typename T, std::size_t Size, std::size_t... Is > VMATH_HPP_FORCE_INLINE 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_join([](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_join([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 map_join([](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_join([](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_join([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 map_join([](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_join([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 map_join([](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_join([](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 map_join([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 map_join([](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_join([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 map_join([](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_join([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 map_join([](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_join([](T x) { return isnan(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec isinf(const vec& xs) { return map_join([](T x) { return isinf(x); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] vec isfinite(const vec& xs) { return map_join([](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 map_join([](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 > VMATH_HPP_FORCE_INLINE 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 map_join([](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_join([](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); } } // // Relational Functions // namespace vmath_hpp { template < typename T, std::size_t Size > [[nodiscard]] constexpr bool any(const vec& xs) { return fold_join([](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_join([](bool acc, T x){ return acc && all(x); }, true, xs); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(const vec& xs, T y) { return map_join([y](T x){ return approximately(x, y); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(T x, const vec& ys) { return map_join([x](T y){ return approximately(x, y); }, ys); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(const vec& xs, const vec& ys) { return map_join([](T x, T y){ return approximately(x, y); }, xs, ys); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(const vec& xs, T y, T epsilon) { return map_join([y, epsilon](T x){ return approximately(x, y, epsilon); }, xs); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(T x, const vec& ys, T epsilon) { return map_join([x, epsilon](T y){ return approximately(x, y, epsilon); }, ys); } template < typename T, std::size_t Size > [[nodiscard]] constexpr vec approximately(const vec& xs, const vec& ys, T epsilon) { return map_join([epsilon](T x, T y){ return approximately(x, y, epsilon); }, xs, ys); } }