I started to write a library for linear algebra for personal use, but also for revitilization of my C++.
Below is the first class of this library, a templated vector class, which is templated over the type of the elements as well as the size. It supports all common operations of vectors as member functions and also as static functions, to support different coding preferences. I also overloaded the operators, that make sense in my opinion. I templated over the size because I need the byte-size of a float vector to be 4*n.
Generally all suggestions and comments are more than welcome, but I also have a few points I specifically want to ask about:
- Interface-Design: Choice of providing both non-static and static versions of most operations, and implementing operators in terms of the static functions
- Inlining: Did I miss functions, that can be inlined without further thought? Should I remove inlining from some functions
- Functionality: Did I miss functions/operators that the class should have?
- Function naming: I'm not completely satisfied with some namen, but can't think of better ones, that are still exact. Maybe someone has suggestions?
Thanks for your time!
template<typename T, uint32_t size> class Vector { private: std::array<T, size> data; public: Vector() : Vector<T, size>(T(0)) {} Vector(T value) { for (int i = 0; i < size; ++i) { data[i] = value; } } Vector(std::array<T, size> data) { this->data = data; } public: Vector<T, size>& addTo(const Vector<T, size>& summand) { for (int i = 0; i < size; ++i) { this->data[i] += summand[i]; } return *this; } Vector<T, size>& subtractFrom(const Vector<T, size>& subtrahend) { for (int i = 0; i < size; ++i) { this->data[i] -= subtrahend[i]; } return *this; } template<typename scalar> Vector<T, size>& multiplyBy(scalar factor) { for (int i = 0; i < size; ++i) { data[i] *= factor; } return *this; } Vector<T, size>& multiplyBy(const Vector<T, size>& factor) { for (int i = 0; i < size; ++i) { data[i] *= factor[i]; } return *this; } template<typename scalar> Vector<T, size>& divideBy(scalar divisor) { this->multiplyBy(1 / divisor); return *this; } template<typename otherT> Vector<T, size>& divideBy(const Vector<otherT, size>& divisor) { for (int i = 0; i < size; ++i) { data[i] /= divisor[i]; } return *this; } template<typename dotResult> dotResult dotWith(const Vector<T, size>& other) const { dotResult help = dotResult(0); for (int i = 0; i < size; ++i) { help += data[i] * other[i]; } return help; } inline T dotWith(const Vector<T, size>& other) const { return this->dotWith<T>(other); } inline T length() const { return sqrt(this->lengthSquared()); } T lengthSquared() const { T help = 0; for (int i = 0; i < size; ++i) { help += data[i] * data[i]; } return help; } Vector<T, size>& normalize() { this->divide(this->length()); return *this; } T angle(const Vector<T, size>& other) const { //Using normalized vectors minimizes rounding problems return acos(normalize(*this).dot(normalize(other))); } Vector<T, size> cross(const Vector<T, size>& other) { static_assert(size == 3, "Crossproduct is only defined for Vectors of size 3!"); Vector<T, size> result; result[0] = data[1] * other[2] - data[2] * other[1]; result[1] = data[2] * other[0] - data[0] * other[2]; result[2] = data[0] * other[1] - data[1] * other[0]; return result; } public: template<typename scalar> static inline Vector<T, size> multiply(Vector<T, size> vector, scalar scalar) { return vector.multiplyBy(scalar); } template<typename scalar> static inline Vector<T, size> divide(Vector<T, size> vector, scalar scalar) { return vector.divideBy(scalar); } static inline Vector<T, size> multiply(Vector<T, size> first, const Vector<T, size>& second) { return first.multiplyBy(second); } static inline Vector<T, size> divide(Vector<T, size> first, const Vector<T, size>& second) { return first.divideBy(second); } static inline Vector<T, size> add(Vector<T, size> first, const Vector<T, size>& second) { return first.addTo(second); } static inline Vector<T, size> subtract(Vector<T, size> first, const Vector<T, size>& second) { return first.subtractFrom(second); } template<typename dotResult> static inline dotResult dot(Vector<T, size> first, const Vector<T, size>& second) { return first.dotWith(second); } static inline T dot(const Vector<T, size>& first, const Vector<T, size>& second) { return dot<T>(first, second); } static inline Vector<T, size> normalize(Vector<T, size> vector) { return vector.normalize(); } static inline Vector<T, size> angle(const Vector<T, size>& first, const Vector<T, size>& second) { return first.angle(second); } static inline Vector<T, size> cross(const Vector<T, size>& first, const Vector<T, size>& second) { return first.cross(second); } public: inline T& operator[](uint32_t i) { assert(i < size); return data[i]; } inline T operator[](uint32_t i) const { assert(i < size); return data[i]; } /*Vector addition*/ inline Vector<T, size> operator+(const Vector<T, size> summand) const { return Vector<T, size>::add(*this, summand); } /*Vector subtraction*/ inline Vector<T, size> operator-(const Vector<T, size> subtrahend) const { return Vector<T, size>::subtract(*this, subtrahend); } /*Dot product*/ inline T operator*(const Vector<T, size> other) const { return Vector<T, size>::dot(*this, other); } /*Scalar multiplication*/ template <typename scalar> inline Vector<T, size> operator*(const scalar& scalar) const { return Vector<T, size>::multiply(*this, scalar); } template <typename scalar> inline friend Vector<T, size> operator*(const scalar& scalar, const Vector<T, size>& vector) { return Vector<T, size>::multiply(*this, scalar); } /*Scalar division*/ template <typename scalar> inline Vector<T, size> operator/(const scalar& scalar) const { return Vector<T, size>::divideBy(scalar); } template <typename scalar> inline friend Vector<scalar, size> operator/(const scalar& scalar, const Vector<T, size>& vector) { return Vector<scalar, size>(scalar).divideBy(vector); } inline Vector<T, size> operator-() const { return Vector<T, size>::multiply(*this, -1); } inline Vector<T, size> operator+() const { return *this; } inline Vector<T, size>& operator+=(const Vector<T, size>& summand) { this->addTo(summand); } inline Vector<T, size>& operator-=(const Vector<T, size>& subtrahend) { this->subtractFrom(subtrahend); } template<typename scalar> inline Vector<T, size>& operator*=(const scalar& factor) { this->multiplyBy(factor); } template<typename scalar> inline Vector<T, size>& operator/=(const scalar& divisor) { this->divideBy(divisor); } template <int newSize> operator Vector<T, newSize>() const { Vector<T, newSize> result; for (int i = 0; i < newSize; ++i) { result[i] = (i < size) ? data[i] : T(0); } return result; } inline bool operator==(const Vector<T, size>& other) { for (int i = 0; i < size; ++i) { if (data[i] != other[i]) return false; } return true; } inline bool operator!=(const Vector<T, size>& other) { return !operator==(other); } }; typedef Vector<float, 2> Vector2; typedef Vector<float, 3> Vector3; typedef Vector<float, 4> Vector4;