// // vector.hpp // vector // // Created by Sam Jaffe on 8/15/16. // #pragma once #include #include #include #include #include #include #include namespace math { namespace vector { #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) \ vector::value,T>::type, N> #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(var) for (std::size_t var = 0; var < N; ++var) struct {} fill; using fill_t = decltype(fill); template struct 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; vector(std::initializer_list && init) { std::size_t idx = 0; for ( auto it = init.begin(), end = init.end(); it != end && idx < N; ++it, ++idx ) { _data[idx] = *it; } } vector(std::array const & init) { for (std::size_t i = 0; i < N && i < init.size(); ++i) { _data[i] = init[i]; } } vector(vector const & other) { *this = other; } vector(vector && other) { *this = std::move(other); } // Conversion template explicit vector(vector const & other) { for (std::size_t i = 0; i < N && i < N2; ++i) { _data[i] = static_cast(other[i]); } } vector(T const & v, fill_t) { VECTOR_FOR_EACH(i) { _data[i] = v; } } // Assignment vector& operator=(vector const & other) { VECTOR_FOR_EACH(i) { _data[i] = other[i]; } return *this; } vector& operator=(vector && other) { VECTOR_FOR_EACH(i) { _data[i] = std::move(other._data[i]); } return *this; } // 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 value_type const & operator[](std::size_t idx) const { return _data[idx]; } value_type & operator[](std::size_t idx) { return _data[idx]; } value_type const & at(std::size_t idx) const { if (idx >= N) throw std::out_of_range{"index out of range"}; assert(idx < N); return operator[](idx); } value_type & at(std::size_t idx) { if (idx >= N) throw std::out_of_range{"index out of range"}; assert(idx < N); return operator[](idx); } // Mathematical Operations vector& operator+=(vector const & other) { VECTOR_FOR_EACH(i) { _data[i] += other[i]; } return *this; } vector& operator-=(vector const & other) { VECTOR_FOR_EACH(i) { _data[i] -= other[i]; } return *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_ENABLE_IF_EQ_T(div_t, T, N)& operator/=(M c) { VECTOR_FOR_EACH(i) { _data[i] /= c; } return *this; } 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_ENABLE_IF_EQ_T(div_t, T, N)& operator/=(vector c) { VECTOR_FOR_EACH(i) { _data[i] /= c[i]; } return *this; } vector operator+(vector const & other) const { return vector{*this} += other; } vector operator-(vector const & other) const { return vector{*this} -= other; } template vector, N> scaled(vector const & other) const { return vector, N>{*this} *= other; } template vector, N> operator*(M c) const { return vector, N>{*this} *= c; } template friend vector, N> operator*(M c, vector const & v) { return v * c; } template vector, N> invscaled(vector const & other) const { return vector, N>{*this} /= other; } template vector, N> operator/(M c) const { return vector, N>{*this} /= c; } vector operator-() const { return vector{} -= *this; } // 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(); } friend vector abs(vector const & self) { vector tmp(self); using std::abs; for (std::size_t i = 0; i < N; ++i) { tmp._data[i] = abs(tmp._data[i]); } return tmp; } friend void swap(vector & lhs, vector & rhs) { vector tmp(lhs); lhs = rhs; rhs = tmp; } 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); } friend bool operator==(vector const & lhs, vector const & rhs) { return lhs.compare(rhs) == 0; } friend bool operator!=(vector const & lhs, vector const & rhs) { return lhs.compare(rhs) != 0; } friend bool operator< (vector const & lhs, vector const & rhs) { return lhs.compare(rhs) < 0; } friend bool operator<=(vector const & lhs, vector const & rhs) { return lhs.compare(rhs) <= 0; } friend bool operator> (vector const & lhs, vector const & rhs) { return lhs.compare(rhs) > 0; } friend bool operator>=(vector const & lhs, vector const & rhs) { return lhs.compare(rhs) >= 0; } private: int compare(vector const & other) const { int rv = 0; for (std::size_t i = 0; i < N && rv == 0; ++i) { if (_data[i] < other[i]) rv = -1; else if (_data[i] > other[i]) rv = 1; } return rv; } value_type _data[N] = {value_type()}; }; } }