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
Kynox’s sister is a nympho.
I thought she was a hooker too?
Didn’t he catch her blowin dudes in the backyard last week?
@Raindog
Yep, that sounds about right.