Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #pragma once
- /*
- math vector classes for 2, 3, and 4 dimensions, using some clever inheritance and C++11 features
- author: mvaganov@hotmail.com
- license: MIT (http://opensource.org/licenses/MIT). TL;DR - this is free software, I won't fix it for you!
- */
- #define _USE_MATH_DEFINES
- #include <math.h>
- // if compiling with a C++11 compiler, enable vardic templates and initializer lists
- #if _MSC_VER >= 1800 || __cplusplus >= 199711L
- #define __INITIALIZER_LIST_SUPPORTED
- // #define __VARDIC_ARGUMENTS_SUPPORTED
- #include <initializer_list>
- #endif
- /** multi-dimensional position structures */
- template<typename Scalar_t>
- struct _Dimensional {
- typedef Scalar_t Scalar;
- Scalar * v() { return (Scalar*)this; }
- const Scalar * v() const { return (Scalar*)this; }
- };
- template<typename Scalar_t>
- struct _2D : public _Dimensional<Scalar_t> {
- Scalar x, y;
- static const int DIMENSIONS = 2;
- _2D<Scalar>() : x(0), y(0) {}
- _2D<Scalar>(const Scalar x, const Scalar y) : x(x), y(y) {}
- /** construct a vector that is orthogonal (perpendicular) to the given vector. Replaced by CrossProduct in 3 Dimensions. */
- /** @return a perpendicular vector */
- _2D<Scalar> Orthogonal() const { return _2D(-y, x); }
- _2D<Scalar> Perp() const { return Orthogonal(); }
- /** @return turns this vector into it's perpendicular vector */
- _2D<Scalar> SetPerp() { Scalar temp = x; x = -y; y = temp; return *this; }
- };
- template<typename Scalar_t>
- struct _3D : public _Dimensional<Scalar_t> {
- Scalar x, y, z;
- static const int DIMENSIONS = 3;
- _3D() : x(0), y(0), z(0) {}
- _3D(const Scalar x, const Scalar y, const Scalar z) : x(x), y(y), z(z) {}
- /** Cross Product doesn't make sense in 2D space. In 4D+ space, many vectors can be perpendicular to a pair of Vectors. */
- _3D Cross(const _3D& other) const {
- return _3D(
- y * other.z - other.y * z,
- z * other.x - other.z * x,
- x * other.y - other.x * y);
- }
- //_3D operator % (const _3D& other) const { return Cross(other); }
- _2D & AsVector2D() { return *((_2D*)this); }
- const _2D & AsVector2D() const { return *((_2D*)this); }
- };
- template<typename Scalar_t>
- struct _4D : public _Dimensional<Scalar_t> {
- Scalar x, y, z, w;
- static const int DIMENSIONS = 4;
- _4D() : x(0), y(0), z(0),w(0) {}
- _4D(const Scalar x, const Scalar y, const Scalar z, const Scalar w) : x(x), y(y), z(z), w(w) {}
- _3D & AsVector3D() { return *((_3D*)this); }
- const _3D & AsVector3D() const { return *((_3D*)this); }
- _2D & AsVector2D() { return *((_2D*)this); }
- const _2D & AsVector2D() const { return *((_2D*)this); }
- };
- #define VEC Vector_<D>
- /**
- * describes a general Vector, with DIMENSIONS number of dimensions.
- * @template D determines what kind of Vector this is (2D, 3D, or 4D), including the scalar data type. Must inherit _Dimensional
- */
- template <typename D>
- struct Vector_ : public D
- {
- /** reference the scalar type from the base dimensions structure. without this, Scalar cannot be used in method signatures. */
- typedef typename D::Scalar Scalar;
- void SetZero(const int startIndex) { for (int i = startIndex; i < DIMENSIONS; i++) v()[i] = 0; }
- void SetZero() { SetZero(0); }
- /** templated copy function. will work on any type that inherits from _Dimensional */
- template<typename OtherVectorType> void Copy(const OtherVectorType& other) {
- static_assert(OtherVectorType::DIMENSIONS, "explicit copy for vector being done with non-vector, somewhere");
- for (int i = 0; i < DIMENSIONS; ++i) {
- v()[i] = (D::Scalar)(other.v()[i]);
- }
- SetZero(DIMENSIONS); // set unset values to zero
- }
- /** assignment operator */
- template<typename OtherVectorType> VEC& operator=(const OtherVectorType& other) {
- static_assert(OtherVectorType::DIMENSIONS, "vector being assigned to non-vector, somewhere");
- Copy(other); return *this;
- }
- /** single-parameter (copy) constructor. Made explicit to reduce confusion, especially for automatic type conversion for functions */
- template<typename OtherVectorType> explicit Vector_(const OtherVectorType& other) {
- static_assert(OtherVectorType::DIMENSIONS, "non-vector passed into single-parameter constructor, somewhere");
- Copy<OtherVectorType>(other);
- }
- /** initialize with an array */
- Vector_(Scalar * element) {
- for (int i = 0; i < DIMENSIONS; i++) v()[i] = element[i];
- }
- Vector_(const D& _d) { *this = _d; }
- #ifndef __VARDIC_ARGUMENTS_SUPPORTED
- Vector_() { SetZero(); } // explicit default constructor, allows static
- Vector_(Scalar x, Scalar y) { Set(x, y); }
- Vector_(Scalar x, Scalar y, Scalar z) { Set(x, y, z); }
- Vector_(Scalar x, Scalar y, Scalar z, Scalar w) { Set(x, y, z, w); }
- void Set(const Scalar x, const Scalar y) { SetZero(2); v()[0] = x; v()[1] = y; }
- void Set(const Scalar x, const Scalar y, const Scalar z) { SetZero(3); v()[0] = x; v()[1] = y; v()[2] = z; }
- void Set(const Scalar x, const Scalar y, const Scalar z, const Scalar w) { SetZero(4); v()[0] = x; v()[1] = y; v()[2] = z; v()[3] = w; }
- #endif
- /** use to both read and write elements, just like a static array */
- Scalar& operator [] (const int index) { return v()[index]; }
- /** use to read elements from const vectors */
- Scalar operator [] (int index) const { return v()[index]; }
- /** same as Magnitude() * Magnitude(), but calculated more quickly by avoiding sqrt */
- Scalar MagnitudeSq() const {
- Scalar length = 0.0;
- for (int i = 0; i < DIMENSIONS; i++) length += v()[i] * v()[i];
- return length;
- }
- Scalar Magnitude() const { return sqrt(MagnitudeSq()); }
- /** for people who prefer Length to Magnitude. C++ should optimize this away anyway. */
- Scalar Length() const { return Magnitude(); }
- /** @return the distance between the two given points */
- static Scalar Distance(const VEC& a, const VEC& b) { return (b - a).Magnitude(); }
- /** @return the distance this point and the given point */
- Scalar Distance(const VEC& other) const { return Distance(*this, other); }
- /** rescale the vector to have a smaller magnitude (but the same ratio in it's members), but only if the magnitude is greater */
- void Truncate(const Scalar maximumMagnitude) {
- Scalar m = Magnitude();
- if (m > maximumMagnitude) {
- for (int i = 0; i < DIMENSIONS; i++)
- // "x = (x * a_maxLength) / m" is more precise than "x *= (a_maxLength / m)"
- v()[i] = (v()[i] * maximumMagnitude) / m;
- }
- }
- /** modifies the vector to be unit length */
- void Normalize() { Scalar m; Normalize(m); }
- /** @param out_magnitude [out] the magnitude of the vector before it was normalized */
- void Normalize(Scalar & out_magnitude) {
- if (!IsZero()) {
- out_magnitude = Magnitude();
- // do not divide is length is really small
- for (int i = 0; i < DIMENSIONS; i++)
- v()[i] /= out_magnitude;
- } else out_magnitude = 0;
- }
- /** @return a new vector that is this vector, but normalized */
- VEC Normalized() const {
- VEC result(*this);
- result.Normalize();
- return result;
- }
- VEC Normalized(Scalar & out_magnitude) const {
- VEC result = *this;
- result.Normalize(out_magnitude);
- return result;
- }
- /** @return negative of a vector */
- VEC operator - ( void ) const { return *this * -1; }
- void operator *= (Scalar scalar) { for (int i = 0; i < DIMENSIONS; i++) v()[i] *= scalar; }
- void operator /= (Scalar scalar) { for (int i = 0; i < DIMENSIONS; i++) v()[i] /= scalar; }
- /** @return scales a vector (multiplying). for the case when the operand order is Vector * Scalar */
- VEC operator * (Scalar scalar) const {
- VEC result;
- for (int i = 0; i < DIMENSIONS; i++)
- result.v()[i] = v()[i] * scalar;
- return result;
- }
- /** @return scales a vector (dividing). for the case when the operand order is Vector * Scalar */
- VEC operator / (const Scalar scalar) const {
- VEC result;
- for (int i = 0; i < DIMENSIONS; i++)
- result.v()[i] = v()[i] / scalar;
- return result;
- }
- /** @return the sum of this vector and the given vector */
- VEC operator + (const VEC& other) const { VEC result; for (int i = 0; i < DIMENSIONS; i++) { result.v()[i] = v()[i] + other.v()[i]; } return result; }
- /** @return the difference between this vector and the given vector */
- VEC operator - (const VEC& other) const { VEC result; for (int i = 0; i < DIMENSIONS; i++) { result.v()[i] = v()[i] - other.v()[i]; } return result; }
- /** @return the product of this vector and the given vector */
- VEC product (const VEC& other) const { VEC result; for (int i = 0; i < DIMENSIONS; i++) { result.v()[i] = v()[i] * other.v()[i]; } return result; }
- /** @return the quotient of this vector and the given vector */
- VEC quotient(const VEC& other) const { VEC result; for (int i = 0; i < DIMENSIONS; i++) { result.v()[i] = v()[i] / other.v()[i]; } return result; }
- VEC& operator += (const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] += other.v()[i]; return *this; }
- VEC& operator -= (const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] -= other.v()[i]; return *this; }
- VEC& multiply(const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] *= other.v()[i]; return *this; }
- VEC& divide(const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] /= other.v()[i]; return *this; }
- /** @return Dot-Product of the given vectors */
- static Scalar Dot(const VEC& a, const VEC& b) {
- Scalar dot_product = 0.0;
- for (int i = 0; i < DIMENSIONS; i++)
- dot_product += a.v()[i] * b.v()[i];
- return dot_product;
- }
- //Scalar operator * (const VEC& other) const{ return Dot(*this, other); }
- /** @return true if the given vector has the same values as this vector */
- bool operator == (const VEC& other) const {
- for (int i = 0; i < DIMENSIONS; i++)
- if (v()[i] != other.v()[i])
- return false;
- return true;
- }
- /** @return !operator==(other) */
- bool operator != (const VEC& other) const { return !operator==(other); }
- /** it returns a reference to a constant static vector full of 0's, presumably at the origin of a system */
- static const VEC& ZERO() { static VEC zero_vector; return zero_vector; }
- /** @return true if this vector is the ZERO vector */
- bool IsZero() const {
- for (int i = 0; i < DIMENSIONS; i++) {
- if (v()[i] != 0)
- return false;
- }
- return true;
- }
- /** clamps this vector's values to be integral (disgarding decimal), in case this vector is being used for very digital row/col-type purposes */
- void ClampToInt() {
- for (int i = 0; i < DIMENSIONS; i++) {
- v()[i] = (Scalar)((int)v()[i]);
- }
- }
- /** rounds this vector's values to the nearest integer, in case this vector is being used for very digital row/col-type purposes */
- void RoundToInt() {
- for (int i = 0; i < DIMENSIONS; i++) {
- v()[i] = (Scalar)((int)(v()[i]+ 0.5));
- }
- }
- /**
- * @param percentage 0 is a, 1 is b, .5 is between(a,b). any numeric value should work.
- * @return an interpolated point between points a and b.
- */
- static VEC Lerp(const VEC& a, const VEC& b, const Scalar percentage) {
- VEC delta = b - a;
- delta *= percentage;
- return a + delta;
- }
- /**
- * @param p the point you are looking for neighbors to
- * @param list multiple points
- * @param listSize how many points are in list
- * @return the index (from list) of the point that is closest to given point p
- */
- static int IndexOfClosest(const VEC& p, const VEC * const & list, const int listSize) {
- if (listSize < 0) return -1;
- int closestIndex = 0;
- VEC delta = list[0] - p;
- Scalar mag, closestSoFar = delta.Magnitude();
- for (int i = 1; i < listSize; ++i) {
- delta = list[i] - p;
- mag = delta.Magnitude();
- if (mag < closestSoFar) {
- closestSoFar = mag;
- closestIndex = i;
- }
- }
- return closestIndex;
- }
- #ifdef __INITIALIZER_LIST_SUPPORTED
- /** initializer list constructor */
- Vector_(const std::initializer_list <Scalar> & ilist) {
- auto it = ilist.begin();
- int index = 0;
- while (it != ilist.end()) {
- v()[index++] = *it;
- it++;
- }
- }
- #endif
- #ifdef __VARDIC_ARGUMENTS_SUPPORTED
- template<int INDEX> void Set(const Scalar& a_value){ v()[INDEX] = (Scalar)a_value; }
- private:
- /** terminus for vardic template recursion */
- template<int INDEX, typename... ARGS>
- void SetVardic(){ /*static_assert((INDEX != DIMENSIONS - 1), "not enough arguments supplied");*/ }
- /** vardic template recursion */
- template<int INDEX, typename ARGTYPE, typename... ARGS>
- void SetVardic(const ARGTYPE a_value, const ARGS ... args) {
- static_assert((INDEX < DIMENSIONS), "too many arguments supplied");
- Set<INDEX>((const Scalar)a_value);
- // tail recursion, hopefully inline-able by the compiler
- SetVardic<INDEX + 1, ARGS...>(args...);
- }
- public:
- /** sets the value of this vector using vardic arguments */
- template<typename... ARGS>
- void Set(const ARGS... args) {
- const int NUMARGS = sizeof...(ARGS)-1;
- static_assert((NUMARGS != DIMENSIONS), "wrong number of arguments supplied");
- SetVardic<0, ARGS...>(args...);
- }
- /** explicit default constructor, allows static array allocation. otherwise, Microsoft Visual Studio 2013 has undocumented problems. */
- Vector_() { SetZero(); }
- template<typename... ARGS>
- /** complete constructor using vardic arguments. this is implicitly also a default constructor (when zero arguments are supplied) */
- explicit Vector_(const ARGS... args) {
- const int NUMARGS = sizeof...(ARGS);
- if (NUMARGS < DIMENSIONS) { // set undefined scalar members to zero
- SetZero(NUMARGS);
- }
- if (NUMARGS > 0) {
- Set(args...);
- }
- }
- #endif
- };
- #undef __INITIALIZER_LIST_SUPPORTED
- #undef __VARDIC_ARGUMENTS_SUPPORTED
- #undef VEC
- typedef Vector_<_2D<float>> V2f;
- typedef Vector_<_3D<float>> V3f;
- typedef Vector_<_4D<float>> V4f;
- typedef Vector_<_2D<int>> V2i;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement