/* mmvec.hpp * Part of Mathematical library built (ab)using Modern C++ 17 abstractions. * * This library is not intended to be _performant_, it does not contain * hand written SMID / SSE / AVX optimizations. It is instead an example * of highly abstracted code, where Vectors can contain any data type. * * As a challenge, the vector data structure has been built on a container * of static capacity. But if a dynamic base container is needed, the code * should be easily modifiable to add further abstraction, by templating * the container, and by consequence the allocator. * * Naoki Pross * 2018 ~ 2019 */ #pragma once #include #include #include #include #include #include #include #include namespace mm { // generic implementation template struct basic_vec; // usable specializations template struct vec; template struct vec3; template struct vec2; } template struct mm::basic_vec : public std::array { using type = T; static constexpr std::size_t dimensions = d; // TODO: template away these static constexpr T null_element = static_cast(0); static constexpr T unit_element = static_cast(1); static constexpr T unit_additive_inverse_element = static_cast(-1); basic_vec(); basic_vec(const std::initializer_list l); template basic_vec(const basic_vec& other); T length() const; template basic_vec& operator=(const mm::basic_vec& other); template basic_vec& operator+=(const mm::basic_vec& other); template basic_vec& operator-=(const mm::basic_vec& other); basic_vec& operator*=(const T& scalar); }; // member functions for basic_vec template mm::basic_vec::basic_vec() : std::array() { this->fill(basic_vec::null_element); } template mm::basic_vec::basic_vec(const std::initializer_list l) { // construct with empty values basic_vec(); // why can't this sh*t be a constexpr with static_assert??? assert(l.size() <= d); std::copy(l.begin(), l.end(), this->begin()); } template template mm::basic_vec::basic_vec(const mm::basic_vec& other) { // construct with empty values basic_vec(); // uses operator= *this = other; } template T mm::basic_vec::length() const { return std::sqrt(std::accumulate(this->begin(), this->end(), basic_vec::null_element, [](const T& init, const T& val) -> T { return init + val * val; } )); } // memeber operator overloads for basic_vec template template mm::basic_vec& mm::basic_vec::operator=(const mm::basic_vec& other) { static_assert( d >= n, "cannot copy higher dimensional vector into a smaller one" ); std::copy(other.begin(), other.end(), this->begin()); return *this; } template template mm::basic_vec& mm::basic_vec::operator+=(const mm::basic_vec& other) { *this = *this + other; return *this; } template template mm::basic_vec& mm::basic_vec::operator-=(const mm::basic_vec& other) { *this = *this - other; return *this; } template mm::basic_vec& mm::basic_vec::operator*=(const T& scalar) { *this = *this * scalar; return *this; } // operator overloads for basic_vec template mm::basic_vec operator+(const mm::basic_vec& rhs, const mm::basic_vec& lhs) { mm::basic_vec out; std::transform(rhs.begin(), rhs.end(), lhs.begin(), out.begin(), [](const T& r, const T& l) -> T { return r + l; } ); return out; } template mm::basic_vec operator*(const mm::basic_vec& rhs, const T& lhs) { return lhs * rhs; } template mm::basic_vec operator*(const T& rhs, const mm::basic_vec& lhs) { mm::basic_vec out; std::transform(lhs.begin(), lhs.end(), out.begin(), [rhs](const T& t) -> T { return t * rhs; }); return out; } template mm::basic_vec operator-(const mm::basic_vec& rhs, const mm::basic_vec& lhs) { return rhs + mm::basic_vec::unit_additive_inverse_element * lhs; } template T operator*(const mm::basic_vec& rhs, const mm::basic_vec& lhs) { return std::inner_product(rhs.begin(), rhs.end(), lhs.begin(), 0); } template std::ostream& operator<<(std::ostream& os, const mm::basic_vec& v) { os << "<"; std::for_each(v.begin(), v.end() -1, [&](const T& el) { os << el << ", "; }); os << v.back() << ">"; return os; } // actual vectors to use in your code template class mm::vec: public mm::basic_vec { public: vec(std::initializer_list l) : basic_vec(l) {} template vec(const basic_vec& other) : basic_vec(other) {} }; // three dimensional specialization with a static cross product // TODO: specialize operator+ for spherical coordinates template class mm::vec3 : public mm::basic_vec { public: vec3() : basic_vec() {} vec3(std::initializer_list l) : basic_vec(l) {} template vec3(const basic_vec& other) : basic_vec(other) {} T& x() { return this->at(0); } T& y() { return this->at(1); } T& z() { return this->at(2); } const T& x() const { return this->at(0); } const T& y() const { return this->at(1); } const T& z() const { return this->at(2); } T zenith() const; T azimuth() const; vec3 spherical() const; static vec3 cross(const vec3& rhs, const vec3& lhs); }; template T mm::vec3::zenith() const { return std::acos(this->z() / this->length()); } template T mm::vec3::azimuth() const { return std::atan(this->y() / this->x()); } template mm::vec3 mm::vec3::spherical() const { return mm::vec3 { this->length(), this->zenith(), this->azimuth(), }; } template mm::vec3 mm::vec3::cross(const vec3& rhs, const vec3& lhs) { mm::vec3 res; res.x() = (rhs.y() * lhs.z()) - (rhs.z() * lhs.y()); res.y() = (rhs.z() * lhs.x()) - (rhs.x() * lhs.z()); res.z() = (rhs.x() * lhs.y()) - (rhs.y() * lhs.x()); return res; } // two dimensional specialization with a polar conversion // TODO: specialize operator+ for polar coordinates template class mm::vec2: public mm::basic_vec { public: vec2() : basic_vec() {} vec2(std::initializer_list l) : basic_vec(l) {} template vec2(const basic_vec& other) : basic_vec(other) {} T& x() { return this->at(0); } T& y() { return this->at(1); } const T& x() const { return this->at(0); } const T& y() const { return this->at(1); } T angle() const; vec2 polar() const; static vec3 cross(const vec2& rhs, const vec2& lhs); }; template T mm::vec2::angle() const { return std::atan(this->y() / this->x()); } template mm::vec2 mm::vec2::polar() const { return mm::vec2 { this->length(), this->angle() }; } template mm::vec3 mm::vec2::cross(const mm::vec2& rhs, const mm::vec2& lhs) { return mm::vec3::cross(mm::vec3(rhs), mm::vec3(lhs)); }