Home > Programming > N-Dimensional Vector Template Class

N-Dimensional Vector Template Class

Hey, whipping together some of the math classes for one of my projects and just ‘finished’ an N-dimensional vector class. Only tested lightly. Should be fully portable, but that has not been tested.

Still needs some extra features (cross product, linear interpolation, reflection, etc), and documentation. If and when I finish that I’ll post an update.

You’re free to use this for whatever. All I ask is that if you find any bugs you please report them. (Oh, acknowledging that Kynox’s sister is a nympho is a big plus too.)

EDIT:

Updated version. Now much more const-safe (whoops!), several minor things fixed, a couple of new features added, portability tested some more (compiles under MSVC and Comeau C++), implemented the suggested generic constructors in case they’re of use to someone.

// Preprocessor header guard to stop multiple includes

#ifndef HADES__VECN_H

#define HADES__VECN_H

// C++ Standard Library

#include <cmath>

#include <cassert>

#include <stdexcept>

#include <algorithm>

#include <functional>

#include <numeric>

#include <vector>

#include <iterator>

// Hades namespace

namespace Hades

{

// N-Dimensional Vector

template <class ElemT, unsigned int Num>

class VecN

{

public:

// ElemT used to store elements

typedef std::vector<ElemT> DataT;

// Element placeholders. For convenience only.

enum Elems

{

XElem,

YElem,

ZElem

};

// Default constructor.

// All dimensions default initialized.

VecN() : m_Size(Num), m_Data(Num) {}

// Vec1 constructor

VecN(ElemT X) : m_Size(Num), m_Data(Num)

{

assert(m_Size == 1);

m_Data[XElem] = X;

}

// Vec2 constructor

VecN(ElemT X, ElemT Y) : m_Size(Num), m_Data(Num)

{

assert(m_Size == 2);

m_Data[XElem] = X; m_Data[YElem] = Y;

}

// Vec3 constructor

VecN(ElemT X, ElemT Y, ElemT Z) : m_Size(Num), m_Data(Num)

{

assert(m_Size == 3);

m_Data[XElem] = X; m_Data[YElem] = Y; m_Data[ZElem] = Z;

}

// Vec4 constructor

VecN(ElemT X, ElemT Y, ElemT Z, ElemT N) : m_Size(Num), m_Data(Num)

{

assert(m_Size == 4);

m_Data[XElem] = X; m_Data[YElem] = Y; m_Data[ZElem] = Z; m_Data[3] = N;

}

// Get reference to vector element

// Implemented by calling const version of function and const_cast’ing

// the returned reference. Safe in this scenario. For more information

// see [Myers].

ElemT& operator[] (unsigned int Index)

{

assert(Index < Num);

return const_cast<ElemT&>(static_cast<const VecN<ElemT, Num>& >(*this).

operator[](Index));

}

// Get const reference to vector element

const ElemT& operator[] (unsigned int Index) const

{

assert(Index < Num);

return m_Data[Index];

}

// Get negated vector

VecN<ElemT, Num> operator- () const

{

VecN<ElemT, Num> Result;

std::transform(m_Data.begin(), m_Data.end(), Result.m_Data.begin(),

std::negate<ElemT>());

return Result;

}

// Vector subtraction

VecN<ElemT, Num> operator- (const VecN<ElemT, Num>& Rhs) const

{

VecN<ElemT, Num> Result;

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

Result.m_Data.begin(), std::minus<ElemT>());

return Result;

}

// Vector subtraction

VecN<ElemT, Num> operator-= (const VecN<ElemT, Num>& Rhs)

{

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

m_Data.begin(), std::minus<ElemT>());

return *this;

}

// Vector addition

VecN<ElemT, Num> operator+ (const VecN<ElemT, Num>& Rhs) const

{

VecN<ElemT, Num> Result;

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

Result.m_Data.begin(), std::plus<ElemT>());

return Result;

}

// Vector addition

VecN<ElemT, Num> operator+= (const VecN<ElemT, Num>& Rhs)

{

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

m_Data.begin(), std::plus<ElemT>());

return *this;

}

// Vector multiplication (scales elements)

VecN<ElemT, Num> operator* (ElemT Rhs) const

{

VecN<ElemT, Num> Result;

std::transform(m_Data.begin(), m_Data.end(), Result.m_Data.begin(),

std::bind2nd(std::multiplies<ElemT>(), Rhs));

return Result;

}

// Vector multiplication (scales elements)

// Mixed mode version

friend inline VecN<ElemT, Num> operator* (ElemT Rhs,

const VecN<ElemT, Num>& v)

{

VecN<ElemT, Num> Result;

std::transform(v.m_Data.begin(), v.m_Data.end(), Result.m_Data.begin(),

std::bind2nd(std::multiplies<ElemT>(), Rhs));

return Result;

}

// Vector multiplication (scalar/dot product)

ElemT operator* (const VecN<ElemT, Num>& Rhs) const

{

DataT Result(Num);

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

Result.begin(), std::multiplies<ElemT>());

return std::accumulate(Result.begin(), Result.end(),

static_cast<ElemT>(0));

}

// Vector multiplication (scales elements)

VecN<ElemT, Num> operator*= (const VecN<ElemT, Num>& Rhs)

{

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

m_Data.begin(), std::multiplies<ElemT>());

return *this;

}

// Vector division (scalar/dot division)

ElemT operator/ (const VecN<ElemT, Num>& Rhs) const

{

DataT Result(Num);

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

Result.begin(), std::divides<ElemT>());

return std::accumulate(Result.begin(), Result.end(),

static_cast<ElemT>(0));

}

// Vector division (scalar/dot division)

VecN<ElemT, Num> operator/= (const VecN<ElemT, Num>& Rhs)

{

std::transform(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin(),

m_Data.begin(), std::divides<ElemT>());

return *this;

}

// Equality test

bool operator == (const VecN<ElemT, Num>& Rhs) const

{

return std::equal(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin());

}

// Inequality check

bool operator != (const VecN<ElemT, Num>& Rhs) const

{

return !std::equal(m_Data.begin(), m_Data.end(), Rhs.m_Data.begin());

}

// Output stream overload

// Prints in format:

// E1 E2 … EN

friend inline std::ostream& operator << (std::ostream& Lhs,

const VecN<ElemT, Num>& Rhs)

{

std::copy(Rhs.m_Data.begin(), Rhs.m_Data.end(),

std::ostream_iterator<ElemT>(Lhs, ” “));

return Lhs;

}

// Gets the squared length of a vector

friend inline ElemT LengthSquared(const VecN<ElemT, Num>& v)

{

VecN<ElemT, Num> Temp;

std::transform(v.m_Data.begin(), v.m_Data.end(), v.m_Data.begin(),

Temp.m_Data.begin(), std::multiplies<ElemT>());

return std::accumulate(Temp.m_Data.begin(), Temp.m_Data.end(),

static_cast<ElemT>(0));

}

// Gets the length of a vector

friend inline ElemT Length(const VecN<ElemT, Num>& v)

{

return static_cast<ElemT>(sqrt(static_cast<long double>(

LengthSquared(v))));

}

// Normalizes a vector

friend inline ElemT Normalize(VecN<ElemT, Num>& v)

{

ElemT Len = Length(v);

if (Len != static_cast<ElemT>(0))

{

std::transform(v.m_Data.begin(), v.m_Data.end(), v.m_Data.begin(),

std::bind2nd(std::divides<ElemT>(), Len));

}

return Len;

}

// Tests if a vector is normalized

friend inline bool IsNormalized(const VecN<ElemT, Num>& v,

ElemT Eps = static_cast<ElemT>(1.0001f))

{

return static_cast<ElemT>(fabs(static_cast<long double>(

LengthSquared(v) - static_cast<ElemT>(1))))

<= static_cast<ElemT>(Eps);

}

// Cross product

friend inline VecN<ElemT, 3> Cross(const VecN<ElemT, 3>& v1,

const VecN<ElemT, 3>& v2)

{

return VecN<ElemT, 3>(

(v1[YElem] * v2[ZElem]) - (v1[ZElem] * v2[YElem]),

(v1[ZElem] * v2[XElem]) - (v1[XElem] * v2[ZElem]),

(v1[XElem] * v2[YElem]) - (v1[YElem] * v2[XElem])

);

}

// Reflection

friend inline VecN<ElemT, Num> Reflect(const VecN<ElemT, Num>& v,

const VecN<ElemT, Num>& Normal)

{

return v - (static_cast<ElemT>(2.0) * ((v * Normal) * Normal));

}

private:

// Size of vector

unsigned int m_Size;

// Vector elements

std::vector<ElemT> m_Data;

};

// Ease of use typedefs

typedef VecN<int, 2> Vec2i;

typedef VecN<float, 2> Vec2f;

typedef VecN<double, 2> Vec2d;

typedef VecN<int, 3> Vec3i;

typedef VecN<float, 3> Vec3f;

typedef VecN<double, 3> Vec3d;

}

#endif // HADES__VECN_H

Hey, whipping together some of the math classes for one of my projects and just ‘finished’ an N-dimensional vector class. Only tested lightly.
Should be fully portable, but that has not been tested.
Still needs some extra features (cross product, linear interpolation, reflection, etc), and documentation. If and when I finish that I’ll post an update.
You’re free to use this for whatever. All I ask is that if you find any bugs you please report them.
Author: Cypherjb Categories: Programming Tags: , , , , ,
  1. poire
    July 9th, 2009 at 18:55 | #1

    Kynox’s sister is a nympho.

  2. Raindog
    July 11th, 2009 at 10:40 | #2

    I thought she was a hooker too?

  3. Raindog
    July 11th, 2009 at 10:40 | #3

    Didn’t he catch her blowin dudes in the backyard last week?

  4. July 11th, 2009 at 11:58 | #4

    @Raindog
    Yep, that sounds about right.

  1. No trackbacks yet.