Go Back

Vector.hpp

/*
  Author: Jake McLeman

  This is the implementation of my personal C++ vector library. It is designed for runtime performance,
  at the cost of compile time. This is achieved by using templates for varying dimensions to allow the compiler
  to generate the most optimal code by automatically unrolling loops and allowing CPU instruction pipelining.

  It is also designed to ensure all operations are well-defined mathematically, illegal vector operations will
  not compile
*/

#include <iostream>         //For stream insertion operator
#include <initializer_list> //For initilazer list construction, (also used for swizzleing)

#ifndef VECTOR_HPP
#define VECTOR_HPP

//Simplification of index and swizzle operations by defining these values
enum {
 x = 0,
 y = 1,
 z = 2,
 w = 3,
 r = 0,
 g = 1,
 b = 2,
 a = 3,
};

template<typename T, unsigned int Dimension>
class Vector
{
public:

 //Default constructor fills with 0s
 Vector()
 {
   for(int i = 0; i < Dimension; i++)
   {
     data[i] = 0;
   }
 }

 //Initialize using an array of values of the correct size
 Vector(T (&values)[Dimension])
 {
   for(int i = 0; i < Dimension; i++) data[i] = values[i];
 }

 //Convert vectors up/down dimensions as needed
 template<typename T2, unsigned int Dimension2>
 explicit Vector(const Vector<T2, Dimension2>& vec)
 {
   int i;
   for(i = 0; i < ((Dimension > Dimension2) ? Dimension2 : Dimension); i++) data[i] = vec[i];
   for(; i < Dimension; i++) data[i] = 0;
 }

 //Create a vector from an initializer list
 Vector(std::initializer_list<T> list)
 {
   for(int i = 0; i < list.size(); i++)
   {
     data[i] = list.begin()[i];
   }
 }

 //Add two vectors of different size and type, return type is the larger vector size
 template<typename T2, unsigned int Dimension2>
 Vector<T, (Dimension > Dimension2) ? Dimension : Dimension2>
 operator+(Vector<T2, Dimension2> rhs)
 {
   //Create a vector of the larger vectors size
   Vector<T, (Dimension > Dimension2) ? Dimension : Dimension2> sum;

   //For each vector in the region where the vectors overlap, add the values
   for(unsigned int i = 0; i < ((Dimension > Dimension2) ? Dimension2 : Dimension); i++)
   {
     sum[i] = data[i] + rhs[i];
   }

   //Copy the non-overlapping values from the larger vector into the result
   for(unsigned int i = ((Dimension > Dimension2) ? Dimension2 : Dimension);
       i < ((Dimension > Dimension2) ? Dimension : Dimension2);
	     i++)
   {
     sum[i] = ((Dimension > Dimension2) ? data[i] : rhs[i]);
   }

   return sum;
 }

 //Addition/assignment operator
 template<typename T2, unsigned int Dimension2>
 Vector& operator+=(Vector<T2, Dimension2> rhs)
 {
   //Add all elements up to the size of the smaller vector into this vector
   for(unsigned int i = 0; i < ((Dimension > Dimension2) ? Dimension2 : Dimension); i++)
   {
     data[i] += rhs[i];
   }
   return *this;
 }

 //assignment operator
 Vector& operator=(Vector rhs)
 {
   //Assign all elements into this vector
   for(int i = 0; i < Dimension; i++)
   {
     data[i] = rhs[i];
   }
 }

 //Scalar multiplication, argument should be type that has operator * defined with vector type
 template<typename T2>
 Vector operator*(const T2& rhs)
 {
   Vector product = *this;
   for(int i = 0; i < Dimension; i++)
   {
     product[i] *= rhs;
   }
   return product;
 }

 //Scalar multiplication, argument should be type that has operator * defined with vector type
 template<typename T2>
 Vector& operator*=(const T2& rhs)
 {
   //Multiply each element in the veector by the scalar value
   for(int i = 0; i < Dimension; i++)
   {
     data[i] *= rhs;
   }
   return *this;
 }

 //Dot product, must be of vector of matching type and dimension
 T Dot(const Vector& rhs)
 {
   T sum = 0;
   for(unsigned int i = 0; i < Dimension; i++)
   {
     sum += data[i] * rhs[i];
   }

   return sum;
 }

 //Subscript/index operator (use with x,y,z enum above)
 const T& operator[](unsigned int index) const
 {
   return data[index];
 }

 //Subscript/index operator (use with x,y,z enum above)
 T& operator[](unsigned int index)
 {
   return data[index];
 }

 //Vector swizzleing
 template<unsigned int...I, unsigned int N = sizeof...(I)>
 Vector<T, N> swizzle()
 {
   //expand the template arguments into array indexes
   //use those as an initializer list for the new vector
   return { {data[I]...} };
 }

private:
  T data [Dimension];
};

//Stream insertion operator for output
template<typename T, unsigned int Dimension>
std::ostream& operator<<(std::ostream& out, const Vector<T, Dimension>& v)
{
  out << "<";
  for(int i = 0; i < Dimension; i++)
  {
    out << v[i];
    if(i < Dimension - 1) out << ", ";
  }

  out << ">";
  return out;
}

//Scalar multiplication but with scalar on the left
template <typename T, typename T2, unsigned int Dimension>
Vector<T, Dimension> operator*(T2 scale, Vector<T, Dimension> vec)
{
 return vec * scale;
}

//Cross product (3 dimensional only)
//TODO find a case for other dimensions where cross product works (all 3 of them)
template <typename T1, typename T2>
Vector<T1, 3> Cross(Vector<T1, 3> lhs, Vector<T2, 3> rhs)
{
 Vector<T1, 3> result;
 result[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]);
 result[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]);
 result[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]);
 return result;
}
#endif