// // vector.hpp // vector // // Created by Sam Jaffe on 8/15/16. // #pragma once #include #include #include #include #include #include #include #include #include "math/vector/forward.h" #include "math/vector/traits.hpp" #define VECTOR_ENABLE_IF_LT_N(index, expr) \ template \ typename std::enable_if::type #define VECTOR_ENABLE_IF_EQ_N(index, t, n) \ template \ typename std::enable_if>::type #define VECTOR_ENABLE_IF_EQ_T(_type, t, n) \ typename std::enable_if::value, vector>::type #define VECTOR_DISABLE_IF_VECTOR(_type, t, n) \ typename std::enable_if::value, vector>::type #define VECTOR_ACCESS_FN(name, i) \ VECTOR_ENABLE_IF_LT_N(i, value_type const &) name() const { \ return _data[i]; \ } \ VECTOR_ENABLE_IF_LT_N(i, value_type &) name() { return _data[i]; } #define VECTOR_FOR_EACH_RANGE(var, end) \ for (std::size_t var = 0; var < end; ++var) #define VECTOR_FOR_EACH(var) VECTOR_FOR_EACH_RANGE(var, N) namespace math::vector { template class vector { public: using value_type = T; private: using mag_t = decltype(std::sqrt(std::declval())); template using mul_t = decltype(std::declval() * std::declval()); template using div_t = decltype(std::declval() / std::declval()); public: // Constructors vector() = default; template vector(T arg0, Us... args) { static_assert(std::is_same_v, T>, "must be type compatible"); static_assert(sizeof...(Us) + 1 == N, "size mismatch"); *this = vector(std::array{arg0, static_cast(args)...}); } vector(std::array const & init) { VECTOR_FOR_EACH(i) { _data[i] = init[i]; } } // Conversion template explicit vector(vector const & other) { VECTOR_FOR_EACH_RANGE(i, std::min(N, N2)) { _data[i] = static_cast(other[i]); } } vector(T const & v, fill_t) { VECTOR_FOR_EACH(i) { _data[i] = v; } } // Named Accessors // - Numeric Vector Accessors VECTOR_ACCESS_FN(x, 0) VECTOR_ACCESS_FN(y, 1) VECTOR_ACCESS_FN(z, 2) VECTOR_ACCESS_FN(w, 3) // - Color Vector Accessors VECTOR_ACCESS_FN(r, 0) VECTOR_ACCESS_FN(g, 1) VECTOR_ACCESS_FN(b, 2) VECTOR_ACCESS_FN(a, 3) // Unnamed Accessors constexpr value_type const & operator[](std::size_t idx) const { return _data[idx]; } constexpr value_type & operator[](std::size_t idx) { return _data[idx]; } value_type const & at(std::size_t idx) const { expects(idx < N, std::out_of_range, "index out of range"); return _data[idx]; } value_type & at(std::size_t idx) { expects(idx < N, std::out_of_range, "index out of range"); return _data[idx]; } // Mathematical Operations vector & operator+=(vector const & other) { VECTOR_FOR_EACH(i) { _data[i] += other[i]; } return *this; } vector & operator+=(T const & other) { return operator+=(vector(other, fill)); } vector operator+(vector const & other) const { return vector{*this} += other; } vector operator+(T const & other) const { return operator+(vector(other, fill)); } friend vector operator+(T const & lhs, vector const & rhs) { return rhs + lhs; } vector & operator-=(vector const & other) { VECTOR_FOR_EACH(i) { _data[i] -= other[i]; } return *this; } vector & operator-=(T const & other) { return operator-=(vector(other, fill)); } vector operator-(vector const & other) const { return vector{*this} -= other; } vector operator-(T const & other) const { return operator-(vector(other, fill)); } friend vector operator-(T const & lhs, vector const & rhs) { return vector(lhs, fill) - rhs; } vector operator-() const { return vector{} -= *this; } template VECTOR_ENABLE_IF_EQ_T(mul_t, T, N) & operator*=(M c) { VECTOR_FOR_EACH(i) { _data[i] *= c; } return *this; } template VECTOR_DISABLE_IF_VECTOR(M, mul_t, N) operator*(M c) const { return vector, N>{*this} *= c; } template friend VECTOR_DISABLE_IF_VECTOR(M, mul_t, N) operator*(M c, vector const & v) { return v * c; } template VECTOR_ENABLE_IF_EQ_T(mul_t, T, N) & operator*=(vector c) { VECTOR_FOR_EACH(i) { _data[i] *= c[i]; } return *this; } template vector, N> operator*(vector const & other) const { return vector, N>{*this} *= other; } template VECTOR_ENABLE_IF_EQ_T(div_t, T, N) & operator/=(M c) { expects(c != 0, std::domain_error, "divide by zero"); VECTOR_FOR_EACH(i) { _data[i] /= c; } return *this; } template VECTOR_DISABLE_IF_VECTOR(M, div_t, N) operator/(M c) const { return vector, N>{*this} /= c; } template VECTOR_ENABLE_IF_EQ_T(div_t, T, N) & operator/=(vector c) { VECTOR_FOR_EACH(i) { expects(c[i] != 0, std::domain_error, "divide by zero"); } VECTOR_FOR_EACH(i) { _data[i] /= c[i]; } return *this; } template vector, N> operator/(vector const & other) const { return vector, N>{*this} /= other; } // Vector Operations value_type dot(vector const & other) const { value_type accum{}; VECTOR_FOR_EACH(i) { accum += at(i) * other.at(i); } return accum; } mag_t magnitude() const { return std::sqrt(dot(*this)); } vector unit() const { return *this / magnitude(); } VECTOR_ENABLE_IF_EQ_N(3, T, N) cross(vector const & other) const { return {{y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(), x() * other.y() - y() * other.x()}}; } VECTOR_ENABLE_IF_EQ_N(2, T, 3) cross(vector const & other) const { return {{0, 0, x() * other.y() - y() * other.x()}}; } vector projection(vector const & other) const { vector b_p = other.unit(); return b_p * vector{*this}.dot(b_p); } private: value_type _data[N] = {value_type()}; }; template vector(T, Us...) -> vector; template vector abs(vector const & self) { vector tmp(self); using std::abs; VECTOR_FOR_EACH(i) { tmp[i] = abs(tmp[i]); } return tmp; } template int compare(vector const & lhs, vector const & rhs) { VECTOR_FOR_EACH(i) { if (lhs[i] < rhs[i]) return -1; else if (lhs[i] > rhs[i]) return 1; } return 0; } template bool operator==(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) == 0; } template bool operator!=(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) != 0; } template bool operator<(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) < 0; } template bool operator<=(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) <= 0; } template bool operator>(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) > 0; } template bool operator>=(vector const & lhs, vector const & rhs) { return compare(lhs, rhs) >= 0; } } using math::vector::abs;