summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNao Pross <naopross@thearcway.org>2019-01-20 23:21:53 +0100
committerNao Pross <naopross@thearcway.org>2019-01-20 23:24:36 +0100
commit7f4277381b1c50f1a183702ac24812872e803adb (patch)
tree26aa0eca0bda10f64fdf828c5dbef44628e6454a
downloadlibmm-7f4277381b1c50f1a183702ac24812872e803adb.tar.gz
libmm-7f4277381b1c50f1a183702ac24812872e803adb.zip
Initial commit, add mmvec.hpp with example, makefile and .gitignore
-rw-r--r--.gitignore1
-rw-r--r--example.cpp56
-rw-r--r--makefile8
-rw-r--r--mmvec.hpp252
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));
+}