diff options
author | Nao Pross <naopross@thearcway.org> | 2019-01-20 23:21:53 +0100 |
---|---|---|
committer | Nao Pross <naopross@thearcway.org> | 2019-01-20 23:24:36 +0100 |
commit | 7f4277381b1c50f1a183702ac24812872e803adb (patch) | |
tree | 26aa0eca0bda10f64fdf828c5dbef44628e6454a | |
download | libmm-7f4277381b1c50f1a183702ac24812872e803adb.tar.gz libmm-7f4277381b1c50f1a183702ac24812872e803adb.zip |
Initial commit, add mmvec.hpp with example, makefile and .gitignore
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | example.cpp | 56 | ||||
-rw-r--r-- | makefile | 8 | ||||
-rw-r--r-- | mmvec.hpp | 252 |
4 files changed, 317 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/example.cpp b/example.cpp new file mode 100644 index 0000000..e72d65b --- /dev/null +++ b/example.cpp @@ -0,0 +1,56 @@ +#include "mmvec.hpp" + +#include <iostream> +#include <complex> + +int main(int argc, char *argv[]) { + // N dimensional vectors + std::cout << "N dimensional (int) vectors" << std::endl; + mm::vec<int, 5> u {3, 2, 1, 0, 1}; + mm::vec<int, 5> v {1, 2, 3, 4, 5}; + + std::cout << "u = " << u << std::endl; + std::cout << "v = " << v << std::endl; + std::cout << std::endl; + + // basic operations + std::cout << "u + v = " << u + v << std::endl; + std::cout << "u - v = " << u - v << std::endl; + std::cout << "2 * v = " << 2 * v << std::endl; + std::cout << "v * 2 = " << v * 2 << std::endl; + std::cout << "u * v = " << u * v << std::endl; + std::cout << std::endl; + + // three dimensional vectors + std::cout << "three dimensional (double) vectors" << std::endl; + + mm::vec3<double> a {1, 2, 3}; + mm::vec3<double> b {3, 2, 1}; + + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << std::endl; + + std::cout << "a x b = " << mm::vec3<double>::cross(a, b) << std::endl; + std::cout << "zenith(a) = " << a.zenith() << std::endl; + std::cout << "azimuth(a) = " << a.azimuth() << std::endl; + std::cout << "spherical(a) = " << a.spherical() << std::endl; + std::cout << std::endl; + + // two dimensional vector + std::cout << "two dimensional (complex) vectors" << std::endl; + + mm::vec2<std::complex<float>> j {{1, 2}, {3, -1}}; + mm::vec2<std::complex<float>> k { 5, {-2, 1}}; + + std::cout << "j = " << j << std::endl; + std::cout << "k = " << k << std::endl; + std::cout << std::endl; + + std::cout << "j x k = " << mm::vec2<std::complex<float>>::cross(j, k) << std::endl; + std::cout << "angle(j) = " << j.angle() << std::endl; + std::cout << "polar(j) = " << j.polar() << std::endl; + std::cout << std::endl; + + return 0; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..c18edc5 --- /dev/null +++ b/makefile @@ -0,0 +1,8 @@ +CPPC := c++ +CPPARGS := -Wall -Werror -I. -std=c++17 -pedantic + +all: build/example + +build/%: %.cpp mmvec.hpp + mkdir -p build + $(CPPC) $(CPPARGS) $< -o $@ diff --git a/mmvec.hpp b/mmvec.hpp new file mode 100644 index 0000000..5173e85 --- /dev/null +++ b/mmvec.hpp @@ -0,0 +1,252 @@ +/* vector.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 <naopross@thearcway.org> + * 2018 ~ 2019 + */ +#include <iostream> + +#include <cassert> +#include <cmath> + +#include <array> +#include <algorithm> +#include <numeric> +#include <complex> +#include <initializer_list> + +namespace mm { + // generic implementation + template<typename T, std::size_t d> + struct basic_vec; + + // usable specializations + template<typename T, std::size_t d> + struct vec; + template<typename T> + struct vec3; + template<typename T> + struct vec2; +} + +template<typename T, std::size_t d> +struct mm::basic_vec : public std::array<T, d> { + using type = T; + static constexpr std::size_t dimensions = d; + + // TODO: template away these + static constexpr T null_element = static_cast<T>(0); + static constexpr T unit_element = static_cast<T>(1); + static constexpr T unit_additive_inverse_element = static_cast<T>(-1); + + basic_vec(); + basic_vec(const std::initializer_list<T> l); + template<std::size_t n> basic_vec(const basic_vec<T, n>& other); + + T length() const; +}; + + +template<typename T, std::size_t d> +mm::basic_vec<T, d>::basic_vec() : std::array<T, d>() { + this->fill(basic_vec<T, d>::null_element); +} + +template<typename T, std::size_t d> +mm::basic_vec<T, d>::basic_vec(const std::initializer_list<T> 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<typename T, std::size_t d> +template<std::size_t n> +mm::basic_vec<T, d>::basic_vec(const mm::basic_vec<T, n>& other) { + // construct with empty values + basic_vec(); + + static_assert( + d >= n, + "cannot copy higher dimensional vector into a smaller one" + ); + + std::copy(other.begin(), other.end(), this->begin()); +} + +template<typename T, std::size_t d> +T mm::basic_vec<T, d>::length() const { + return std::sqrt(std::accumulate(this->begin(), this->end(), + basic_vec<T, d>::null_element, + [](const T& init, const T& val) -> T { + return init + val * val; + } + )); +} + +template<typename T, std::size_t d> +mm::basic_vec<T, d> operator+(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) { + mm::basic_vec<T, d> out; + + std::transform(rhs.begin(), rhs.end(), lhs.begin(), out.begin(), + [](const T& r, const T& l) -> T { + return r + l; + } + ); + + return out; +} + +template<typename T, std::size_t d> +mm::basic_vec<T, d> operator*(const mm::basic_vec<T, d>& rhs, const T& lhs) { + return lhs * rhs; +} + +template<typename T, std::size_t d> +mm::basic_vec<T, d> operator*(const T& rhs, const mm::basic_vec<T, d>& lhs) { + mm::basic_vec<T, d> out; + + std::transform(lhs.begin(), lhs.end(), out.begin(), + [rhs](const T& t) -> T { + return t * rhs; + }); + + return out; +} + +template<typename T, std::size_t d> +mm::basic_vec<T, d> operator-(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) { + return rhs + mm::basic_vec<T, d>::unit_additive_inverse_element * lhs; +} + +template<typename T, std::size_t d> +T operator*(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) { + return std::inner_product(rhs.begin(), rhs.end(), lhs.begin(), 0); +} + +template<typename T, std::size_t d> +std::ostream& operator<<(std::ostream& os, const mm::basic_vec<T, d>& v) { + os << "<"; + std::for_each(v.begin(), v.end() -1, [&](const T& el) { + os << el << ", "; + }); + os << v.back() << ">"; + + return os; +} + +// actual classes to use in your code +template<typename T, std::size_t d> +class mm::vec: public mm::basic_vec<T, d> { +public: + vec(std::initializer_list<T> l) : basic_vec<T, d>(l) {} + + template<std::size_t n> + vec(const basic_vec<T, n>& other) : basic_vec<T, d>(other) {} +}; + +// three dimensional specialization with a static cross product +template<typename T> +class mm::vec3 : public mm::basic_vec<T, 3> { +public: + vec3() : basic_vec<T, 3>() {} + vec3(std::initializer_list<T> l) : basic_vec<T, 3>(l) {} + + template<std::size_t n> + vec3(const basic_vec<T, n>& other) : basic_vec<T, 3>(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<T> spherical() const; + + static vec3<T> cross(const vec3<T>& rhs, const vec3<T>& lhs); +}; + +template<typename T> +T mm::vec3<T>::zenith() const { + return std::acos(this->z() / this->length()); +} + +template<typename T> +T mm::vec3<T>::azimuth() const { + return std::atan(this->y() / this->x()); +} + +template<typename T> +mm::vec3<T> mm::vec3<T>::spherical() const { + return mm::vec3<T> { + this->length(), + this->zenith(), + this->azimuth(), + }; +} + +template<typename T> +mm::vec3<T> mm::vec3<T>::cross(const vec3<T>& rhs, const vec3<T>& lhs) { + mm::vec3<T> 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 +template<typename T> +class mm::vec2: public mm::basic_vec<T, 2> { +public: + vec2() : basic_vec<T, 2>() {} + vec2(std::initializer_list<T> l) : basic_vec<T, 2>(l) {} + + template<std::size_t n> + vec2(const basic_vec<T, n>& other) : basic_vec<T, 2>(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<T> polar() const; + + static vec3<T> cross(const vec2<T>& rhs, const vec2<T>& lhs); +}; + +template<typename T> +T mm::vec2<T>::angle() const { + return std::atan(this->y() / this->x()); +} + +template<typename T> +mm::vec2<T> mm::vec2<T>::polar() const { + return mm::vec2 { + this->length(), + this->angle() + }; +} + +template<typename T> +mm::vec3<T> mm::vec2<T>::cross(const mm::vec2<T>& rhs, const mm::vec2<T>& lhs) { + return mm::vec3<T>::cross(mm::vec3<T>(rhs), mm::vec3<T>(lhs)); +} |