mvaganov

Vector_.h

Jan 17th, 2015
322
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #pragma once
  2. /*
  3. math vector classes for 2, 3, and 4 dimensions, using some clever inheritance and C++11 features
  4. author: mvaganov@hotmail.com
  5. license: MIT (http://opensource.org/licenses/MIT). TL;DR - this is free software, I won't fix it for you!
  6. */
  7.  
  8. #define _USE_MATH_DEFINES
  9. #include <math.h>
  10.  
  11. // if compiling with a C++11 compiler, enable vardic templates and initializer lists
  12. #if _MSC_VER >= 1800 || __cplusplus >= 199711L
  13.     #define __INITIALIZER_LIST_SUPPORTED
  14. //  #define __VARDIC_ARGUMENTS_SUPPORTED
  15.     #include <initializer_list>
  16. #endif
  17.  
  18. /** multi-dimensional position structures */
  19. template<typename Scalar_t>
  20. struct _Dimensional {
  21.     typedef Scalar_t Scalar;
  22.     Scalar * v() { return (Scalar*)this; }
  23.     const Scalar * v() const { return (Scalar*)this; }
  24. };
  25.  
  26. template<typename Scalar_t>
  27. struct _2D : public _Dimensional<Scalar_t> {
  28.     Scalar x, y;
  29.     static const int DIMENSIONS = 2;
  30.  
  31.     _2D<Scalar>() : x(0), y(0) {}
  32.     _2D<Scalar>(const Scalar x, const Scalar y) : x(x), y(y) {}
  33.  
  34.     /** construct a vector that is orthogonal (perpendicular) to the given vector. Replaced by CrossProduct in 3 Dimensions. */
  35.    
  36.     /** @return a perpendicular vector */
  37.     _2D<Scalar> Orthogonal() const { return _2D(-y, x); }
  38.     _2D<Scalar> Perp() const { return Orthogonal(); }
  39.     /** @return turns this vector into it's perpendicular vector */
  40.     _2D<Scalar> SetPerp() { Scalar temp = x; x = -y; y = temp; return *this; }
  41. };
  42.  
  43. template<typename Scalar_t>
  44. struct _3D : public  _Dimensional<Scalar_t> {
  45.     Scalar x, y, z;
  46.     static const int DIMENSIONS = 3;
  47.  
  48.     _3D() : x(0), y(0), z(0) {}
  49.     _3D(const Scalar x, const Scalar y, const Scalar z) : x(x), y(y), z(z) {}
  50.  
  51.     /** Cross Product doesn't make sense in 2D space. In 4D+ space, many vectors can be perpendicular to a pair of Vectors. */
  52.     _3D Cross(const _3D& other) const {
  53.         return _3D(
  54.             y * other.z - other.y * z,
  55.             z * other.x - other.z * x,
  56.             x * other.y - other.x * y);
  57.     }
  58.     //_3D operator % (const _3D& other) const { return Cross(other); }
  59.  
  60.     _2D & AsVector2D()             { return *((_2D*)this); }
  61.     const _2D & AsVector2D() const { return *((_2D*)this); }
  62. };
  63.  
  64. template<typename Scalar_t>
  65. struct _4D : public  _Dimensional<Scalar_t> {
  66.     Scalar x, y, z, w;
  67.     static const int DIMENSIONS = 4;
  68.  
  69.     _4D() : x(0), y(0), z(0),w(0) {}
  70.     _4D(const Scalar x, const Scalar y, const Scalar z, const Scalar w) : x(x), y(y), z(z), w(w) {}
  71.  
  72.     _3D & AsVector3D()             { return *((_3D*)this); }
  73.     const _3D & AsVector3D() const { return *((_3D*)this); }
  74.     _2D & AsVector2D()             { return *((_2D*)this); }
  75.     const _2D & AsVector2D() const { return *((_2D*)this); }
  76. };
  77.  
  78. #define VEC Vector_<D>
  79.  
  80. /**
  81. * describes a general Vector, with DIMENSIONS number of dimensions.
  82. * @template D determines what kind of Vector this is (2D, 3D, or 4D), including the scalar data type. Must inherit _Dimensional
  83. */
  84. template <typename D>
  85. struct Vector_ : public D
  86. {
  87.     /** reference the scalar type from the base dimensions structure. without this, Scalar cannot be used in method signatures. */
  88.     typedef typename D::Scalar Scalar;
  89.  
  90.     void SetZero(const int startIndex) { for (int i = startIndex; i < DIMENSIONS; i++) v()[i] = 0; }
  91.     void SetZero() { SetZero(0); }
  92.  
  93.     /** templated copy function. will work on any type that inherits from _Dimensional */
  94.     template<typename OtherVectorType> void Copy(const OtherVectorType& other) {
  95.         static_assert(OtherVectorType::DIMENSIONS, "explicit copy for vector being done with non-vector, somewhere");
  96.         for (int i = 0; i < DIMENSIONS; ++i) {
  97.             v()[i] = (D::Scalar)(other.v()[i]);
  98.         }
  99.         SetZero(DIMENSIONS); // set unset values to zero
  100.     }
  101.     /** assignment operator */
  102.     template<typename OtherVectorType> VEC& operator=(const OtherVectorType& other) {
  103.         static_assert(OtherVectorType::DIMENSIONS, "vector being assigned to non-vector, somewhere");
  104.         Copy(other); return *this;
  105.     }
  106.     /** single-parameter (copy) constructor. Made explicit to reduce confusion, especially for automatic type conversion for functions */
  107.     template<typename OtherVectorType> explicit Vector_(const OtherVectorType& other) {
  108.         static_assert(OtherVectorType::DIMENSIONS, "non-vector passed into single-parameter constructor, somewhere");
  109.         Copy<OtherVectorType>(other);
  110.     }
  111.  
  112.     /** initialize with an array */
  113.     Vector_(Scalar * element) {
  114.         for (int i = 0; i < DIMENSIONS; i++) v()[i] = element[i];
  115.     }
  116.  
  117.     Vector_(const D& _d) { *this = _d; }
  118. #ifndef __VARDIC_ARGUMENTS_SUPPORTED
  119.     Vector_() { SetZero(); } // explicit default constructor, allows static
  120.  
  121.     Vector_(Scalar x, Scalar y) { Set(x, y); }
  122.     Vector_(Scalar x, Scalar y, Scalar z) { Set(x, y, z); }
  123.     Vector_(Scalar x, Scalar y, Scalar z, Scalar w) { Set(x, y, z, w); }
  124.  
  125.     void Set(const Scalar x, const Scalar y)                    { SetZero(2); v()[0] = x; v()[1] = y; }
  126.     void Set(const Scalar x, const Scalar y, const Scalar z)    { SetZero(3); v()[0] = x; v()[1] = y; v()[2] = z; }
  127.     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; }
  128. #endif
  129.  
  130.     /** use to both read and write elements, just like a static array */
  131.     Scalar& operator [] (const int index) { return v()[index]; }
  132.  
  133.     /** use to read elements from const vectors */
  134.     Scalar operator [] (int index) const { return v()[index]; }
  135.  
  136.     /** same as Magnitude() * Magnitude(), but calculated more quickly by avoiding sqrt */
  137.     Scalar MagnitudeSq() const {
  138.         Scalar length = 0.0;
  139.         for (int i = 0; i < DIMENSIONS; i++) length += v()[i] * v()[i];
  140.         return length;
  141.     }
  142.     Scalar Magnitude() const { return sqrt(MagnitudeSq()); }
  143.     /** for people who prefer Length to Magnitude. C++ should optimize this away anyway. */
  144.     Scalar Length() const { return Magnitude(); }
  145.  
  146.     /** @return the distance between the two given points */
  147.     static Scalar Distance(const VEC& a, const VEC& b) { return (b - a).Magnitude(); }
  148.  
  149.     /** @return the distance this point and the given point */
  150.     Scalar Distance(const VEC& other) const { return Distance(*this, other); }
  151.    
  152.     /** rescale the vector to have a smaller magnitude (but the same ratio in it's members), but only if the magnitude is greater */
  153.     void Truncate(const Scalar maximumMagnitude) {
  154.         Scalar m = Magnitude();
  155.         if (m > maximumMagnitude) {
  156.             for (int i = 0; i < DIMENSIONS; i++)
  157.                 // "x = (x * a_maxLength) / m" is more precise than "x *= (a_maxLength / m)"
  158.                 v()[i] = (v()[i] * maximumMagnitude) / m;
  159.         }
  160.     }
  161.  
  162.     /** modifies the vector to be unit length */
  163.     void Normalize() { Scalar m; Normalize(m); }
  164.  
  165.     /** @param out_magnitude [out] the magnitude of the vector before it was normalized */
  166.     void Normalize(Scalar & out_magnitude) {
  167.         if (!IsZero()) {
  168.             out_magnitude = Magnitude();
  169.             // do not divide is length is really small
  170.             for (int i = 0; i < DIMENSIONS; i++)
  171.                 v()[i] /= out_magnitude;
  172.         } else out_magnitude = 0;
  173.     }
  174.  
  175.     /** @return a new vector that is this vector, but normalized */
  176.     VEC Normalized() const {
  177.         VEC result(*this);
  178.         result.Normalize();
  179.         return result;
  180.     }
  181.     VEC Normalized(Scalar & out_magnitude) const {
  182.         VEC result = *this;
  183.         result.Normalize(out_magnitude);
  184.         return result;
  185.     }
  186.  
  187.     /** @return negative of a vector */
  188.     VEC operator - ( void ) const { return *this * -1; }
  189.  
  190.     void operator *= (Scalar scalar) { for (int i = 0; i < DIMENSIONS; i++) v()[i] *= scalar; }
  191.     void operator /= (Scalar scalar) { for (int i = 0; i < DIMENSIONS; i++) v()[i] /= scalar; }
  192.  
  193.     /** @return scales a vector (multiplying). for the case when the operand order is Vector * Scalar */
  194.     VEC operator * (Scalar scalar) const {
  195.         VEC result;
  196.         for (int i = 0; i < DIMENSIONS; i++)
  197.             result.v()[i] = v()[i] * scalar;
  198.         return result;
  199.     }
  200.     /** @return scales a vector (dividing). for the case when the operand order is Vector * Scalar */
  201.     VEC operator / (const Scalar scalar) const {
  202.         VEC result;
  203.         for (int i = 0; i < DIMENSIONS; i++)
  204.             result.v()[i] = v()[i] / scalar;
  205.         return result;
  206.     }
  207.  
  208.     /** @return the sum of this vector and the given vector */
  209.     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; }
  210.     /** @return the difference between this vector and the given vector */
  211.     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; }
  212.     /** @return the product of this vector and the given vector */
  213.     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; }
  214.     /** @return the quotient of this vector and the given vector */
  215.     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; }
  216.  
  217.     VEC& operator += (const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] += other.v()[i]; return *this; }
  218.     VEC& operator -= (const VEC& other) { for (int i = 0; i < DIMENSIONS; i++) v()[i] -= other.v()[i]; return *this; }
  219.     VEC& multiply(const VEC& other)     { for (int i = 0; i < DIMENSIONS; i++) v()[i] *= other.v()[i]; return *this; }
  220.     VEC& divide(const VEC& other)       { for (int i = 0; i < DIMENSIONS; i++) v()[i] /= other.v()[i]; return *this; }
  221.  
  222.     /** @return Dot-Product of the given vectors */
  223.     static Scalar Dot(const VEC& a, const VEC& b) {
  224.         Scalar dot_product = 0.0;
  225.         for (int i = 0; i < DIMENSIONS; i++)
  226.             dot_product += a.v()[i] * b.v()[i];
  227.         return dot_product;
  228.     }
  229.  
  230.     //Scalar operator *  (const VEC& other) const{ return Dot(*this, other); }
  231.  
  232.     /** @return true if the given vector has the same values as this vector */
  233.     bool operator == (const VEC& other) const {
  234.         for (int i = 0; i < DIMENSIONS; i++)
  235.             if (v()[i] != other.v()[i])
  236.                 return false;
  237.         return true;
  238.     }
  239.     /** @return !operator==(other) */
  240.     bool operator != (const VEC& other) const { return !operator==(other); }
  241.  
  242.     /** it returns a reference to a constant static vector full of 0's, presumably at the origin of a system */
  243.     static const VEC& ZERO() { static VEC zero_vector; return zero_vector; }
  244.  
  245.     /** @return true if this vector is the ZERO vector */
  246.     bool IsZero() const {
  247.         for (int i = 0; i < DIMENSIONS; i++) {
  248.             if (v()[i] != 0)
  249.                 return false;
  250.         }
  251.         return true;
  252.     }
  253.  
  254.     /** clamps this vector's values to be integral (disgarding decimal), in case this vector is being used for very digital row/col-type purposes */
  255.     void ClampToInt() {
  256.         for (int i = 0; i < DIMENSIONS; i++) {
  257.             v()[i] = (Scalar)((int)v()[i]);
  258.         }
  259.     }
  260.  
  261.     /** rounds this vector's values to the nearest integer, in case this vector is being used for very digital row/col-type purposes */
  262.     void RoundToInt() {
  263.         for (int i = 0; i < DIMENSIONS; i++) {
  264.             v()[i] = (Scalar)((int)(v()[i]+ 0.5));
  265.         }
  266.     }
  267.  
  268.     /**
  269.     * @param percentage 0 is a, 1 is b, .5 is between(a,b). any numeric value should work.
  270.     * @return an interpolated point between points a and b.
  271.     */
  272.     static VEC Lerp(const VEC& a, const VEC& b, const Scalar percentage) {
  273.         VEC delta = b - a;
  274.         delta *= percentage;
  275.         return a + delta;
  276.     }
  277.  
  278.     /**
  279.     * @param p the point you are looking for neighbors to
  280.     * @param list multiple points
  281.     * @param listSize how many points are in list
  282.     * @return the index (from list) of the point that is closest to given point p
  283.     */
  284.     static int IndexOfClosest(const VEC& p, const VEC * const & list, const int listSize) {
  285.         if (listSize < 0) return -1;
  286.         int closestIndex = 0;
  287.         VEC delta = list[0] - p;
  288.         Scalar mag, closestSoFar = delta.Magnitude();
  289.         for (int i = 1; i < listSize; ++i) {
  290.             delta = list[i] - p;
  291.             mag = delta.Magnitude();
  292.             if (mag < closestSoFar) {
  293.                 closestSoFar = mag;
  294.                 closestIndex = i;
  295.             }
  296.         }
  297.         return closestIndex;
  298.     }
  299.  
  300. #ifdef __INITIALIZER_LIST_SUPPORTED
  301.     /** initializer list constructor */
  302.     Vector_(const std::initializer_list <Scalar> & ilist) {
  303.         auto it = ilist.begin();
  304.         int index = 0;
  305.         while (it != ilist.end()) {
  306.             v()[index++] = *it;
  307.             it++;
  308.         }
  309.     }
  310. #endif
  311.  
  312. #ifdef __VARDIC_ARGUMENTS_SUPPORTED
  313.     template<int INDEX> void Set(const Scalar& a_value){ v()[INDEX] = (Scalar)a_value; }
  314.  
  315. private:
  316.     /** terminus for vardic template recursion */
  317.     template<int INDEX, typename... ARGS>
  318.     void SetVardic(){ /*static_assert((INDEX != DIMENSIONS - 1), "not enough arguments supplied");*/ }
  319.  
  320.     /** vardic template recursion */
  321.     template<int INDEX, typename ARGTYPE, typename... ARGS>
  322.     void SetVardic(const ARGTYPE a_value, const ARGS ... args) {
  323.         static_assert((INDEX < DIMENSIONS), "too many arguments supplied");
  324.         Set<INDEX>((const Scalar)a_value);
  325.         // tail recursion, hopefully inline-able by the compiler
  326.         SetVardic<INDEX + 1, ARGS...>(args...);
  327.     }
  328.  
  329. public:
  330.     /** sets the value of this vector using vardic arguments */
  331.     template<typename... ARGS>
  332.     void Set(const ARGS... args) {
  333.         const int NUMARGS = sizeof...(ARGS)-1;
  334.         static_assert((NUMARGS != DIMENSIONS), "wrong number of arguments supplied");
  335.         SetVardic<0, ARGS...>(args...);
  336.     }
  337.  
  338.     /** explicit default constructor, allows static array allocation. otherwise, Microsoft Visual Studio 2013 has undocumented problems. */
  339.     Vector_() { SetZero(); }
  340.  
  341.     template<typename... ARGS>
  342.     /** complete constructor using vardic arguments. this is implicitly also a default constructor (when zero arguments are supplied) */
  343.     explicit Vector_(const ARGS... args) {
  344.         const int NUMARGS = sizeof...(ARGS);
  345.         if (NUMARGS < DIMENSIONS) { // set undefined scalar members to zero
  346.             SetZero(NUMARGS);
  347.         }
  348.         if (NUMARGS > 0) {
  349.             Set(args...);
  350.         }
  351.     }
  352.  
  353. #endif
  354. };
  355.  
  356. #undef __INITIALIZER_LIST_SUPPORTED
  357. #undef __VARDIC_ARGUMENTS_SUPPORTED
  358.  
  359. #undef VEC
  360.  
  361. typedef Vector_<_2D<float>> V2f;
  362. typedef Vector_<_3D<float>> V3f;
  363. typedef Vector_<_4D<float>> V4f;
  364.  
  365. typedef Vector_<_2D<int>> V2i;
RAW Paste Data