Source code

Revision control

Copy as Markdown

Other Tools

//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// vector_utils.h: Utility classes implementing various vector operations
#ifndef COMMON_VECTOR_UTILS_H_
#define COMMON_VECTOR_UTILS_H_
#include <cmath>
#include <cstddef>
#include <ostream>
#include <type_traits>
namespace angle
{
template <size_t Dimension, typename Type>
class Vector;
using Vector2 = Vector<2, float>;
using Vector3 = Vector<3, float>;
using Vector4 = Vector<4, float>;
using Vector2I = Vector<2, int>;
using Vector3I = Vector<3, int>;
using Vector4I = Vector<4, int>;
using Vector2U = Vector<2, unsigned int>;
using Vector3U = Vector<3, unsigned int>;
using Vector4U = Vector<4, unsigned int>;
template <size_t Dimension, typename Type>
class VectorBase
{
public:
using VectorN = Vector<Dimension, Type>;
// Constructors
VectorBase() = default;
explicit VectorBase(Type element);
template <typename Type2>
VectorBase(const VectorBase<Dimension, Type2> &other);
template <typename Arg1, typename Arg2, typename... Args>
VectorBase(const Arg1 &arg1, const Arg2 &arg2, const Args &...args);
// Access the vector backing storage directly
const Type *data() const { return mData; }
Type *data() { return mData; }
constexpr size_t size() const { return Dimension; }
// Load or store the pointer from / to raw data
static VectorN Load(const Type *source);
static void Store(const VectorN &source, Type *destination);
// Index the vector
Type &operator[](size_t i) { return mData[i]; }
const Type &operator[](size_t i) const { return mData[i]; }
// Basic arithmetic operations
VectorN operator+() const;
VectorN operator-() const;
VectorN operator+(const VectorN &other) const;
VectorN operator-(const VectorN &other) const;
VectorN operator*(const VectorN &other) const;
VectorN operator/(const VectorN &other) const;
VectorN operator*(Type other) const;
VectorN operator/(Type other) const;
friend VectorN operator*(Type a, const VectorN &b) { return b * a; }
// Compound arithmetic operations
VectorN &operator+=(const VectorN &other);
VectorN &operator-=(const VectorN &other);
VectorN &operator*=(const VectorN &other);
VectorN &operator/=(const VectorN &other);
VectorN &operator*=(Type other);
VectorN &operator/=(Type other);
// Comparison operators
bool operator==(const VectorBase<Dimension, Type> &other) const;
bool operator!=(const VectorBase<Dimension, Type> &other) const;
// Other arithmetic operations
Type length() const;
Type lengthSquared() const;
Type dot(const VectorBase<Dimension, Type> &other) const;
VectorN normalized() const;
protected:
template <size_t CurrentIndex, size_t OtherDimension, typename OtherType, typename... Args>
void initWithList(const Vector<OtherDimension, OtherType> &arg1, const Args &...args);
// Some old compilers consider this function an alternative for initWithList(Vector)
// when the variant above is more precise. Use SFINAE on the return value to hide
// this variant for non-arithmetic types. The return value is still void.
template <size_t CurrentIndex, typename OtherType, typename... Args>
typename std::enable_if<std::is_arithmetic<OtherType>::value>::type initWithList(
OtherType arg1,
const Args &...args);
template <size_t CurrentIndex>
void initWithList() const;
template <size_t Dimension2, typename Type2>
friend class VectorBase;
Type mData[Dimension];
};
template <size_t Dimension, typename Type>
std::ostream &operator<<(std::ostream &ostream, const VectorBase<Dimension, Type> &vector);
template <typename Type>
class Vector<2, Type> : public VectorBase<2, Type>
{
public:
// Import the constructors defined in VectorBase
using VectorBase<2, Type>::VectorBase;
// Element shorthands
Type &x() { return this->mData[0]; }
Type &y() { return this->mData[1]; }
const Type &x() const { return this->mData[0]; }
const Type &y() const { return this->mData[1]; }
};
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector);
template <typename Type>
class Vector<3, Type> : public VectorBase<3, Type>
{
public:
// Import the constructors defined in VectorBase
using VectorBase<3, Type>::VectorBase;
// Additional operations
Vector<3, Type> cross(const Vector<3, Type> &other) const;
// Element shorthands
Type &x() { return this->mData[0]; }
Type &y() { return this->mData[1]; }
Type &z() { return this->mData[2]; }
const Type &x() const { return this->mData[0]; }
const Type &y() const { return this->mData[1]; }
const Type &z() const { return this->mData[2]; }
};
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector);
template <typename Type>
class Vector<4, Type> : public VectorBase<4, Type>
{
public:
// Import the constructors defined in VectorBase
using VectorBase<4, Type>::VectorBase;
// Element shorthands
Type &x() { return this->mData[0]; }
Type &y() { return this->mData[1]; }
Type &z() { return this->mData[2]; }
Type &w() { return this->mData[3]; }
const Type &x() const { return this->mData[0]; }
const Type &y() const { return this->mData[1]; }
const Type &z() const { return this->mData[2]; }
const Type &w() const { return this->mData[3]; }
};
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector);
// Implementation of constructors and misc operations
template <size_t Dimension, typename Type>
VectorBase<Dimension, Type>::VectorBase(Type element)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] = element;
}
}
template <size_t Dimension, typename Type>
template <typename Type2>
VectorBase<Dimension, Type>::VectorBase(const VectorBase<Dimension, Type2> &other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] = static_cast<Type>(other.mData[i]);
}
}
// Ideally we would like to have only two constructors:
// - a scalar constructor that takes Type as a parameter
// - a compound constructor
// However if we define the compound constructor for when it has a single arguments, then calling
// Vector2(0.0) will be ambiguous. To solve this we take advantage of there being a single compound
// constructor with a single argument, which is the copy constructor. We end up with three
// constructors:
// - the scalar constructor
// - the copy constructor
// - the compound constructor for two or more arguments, hence the arg1, and arg2 here.
template <size_t Dimension, typename Type>
template <typename Arg1, typename Arg2, typename... Args>
VectorBase<Dimension, Type>::VectorBase(const Arg1 &arg1, const Arg2 &arg2, const Args &...args)
{
initWithList<0>(arg1, arg2, args...);
}
template <size_t Dimension, typename Type>
template <size_t CurrentIndex, size_t OtherDimension, typename OtherType, typename... Args>
void VectorBase<Dimension, Type>::initWithList(const Vector<OtherDimension, OtherType> &arg1,
const Args &...args)
{
static_assert(CurrentIndex + OtherDimension <= Dimension,
"Too much data in the vector constructor.");
for (size_t i = 0; i < OtherDimension; ++i)
{
mData[CurrentIndex + i] = static_cast<Type>(arg1.mData[i]);
}
initWithList<CurrentIndex + OtherDimension>(args...);
}
template <size_t Dimension, typename Type>
template <size_t CurrentIndex, typename OtherType, typename... Args>
typename std::enable_if<std::is_arithmetic<OtherType>::value>::type
VectorBase<Dimension, Type>::initWithList(OtherType arg1, const Args &...args)
{
static_assert(CurrentIndex + 1 <= Dimension, "Too much data in the vector constructor.");
mData[CurrentIndex] = static_cast<Type>(arg1);
initWithList<CurrentIndex + 1>(args...);
}
template <size_t Dimension, typename Type>
template <size_t CurrentIndex>
void VectorBase<Dimension, Type>::initWithList() const
{
static_assert(CurrentIndex == Dimension, "Not enough data in the vector constructor.");
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::Load(const Type *source)
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = source[i];
}
return result;
}
template <size_t Dimension, typename Type>
void VectorBase<Dimension, Type>::Store(const Vector<Dimension, Type> &source, Type *destination)
{
for (size_t i = 0; i < Dimension; ++i)
{
destination[i] = source.mData[i];
}
}
// Implementation of basic arithmetic operations
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator+() const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = +mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator-() const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = -mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator+(
const Vector<Dimension, Type> &other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] + other.mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator-(
const Vector<Dimension, Type> &other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] - other.mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator*(
const Vector<Dimension, Type> &other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] * other.mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator/(
const Vector<Dimension, Type> &other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] / other.mData[i];
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator*(Type other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] * other;
}
return result;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::operator/(Type other) const
{
Vector<Dimension, Type> result;
for (size_t i = 0; i < Dimension; ++i)
{
result.mData[i] = mData[i] / other;
}
return result;
}
// Implementation of compound arithmetic operations
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator+=(
const Vector<Dimension, Type> &other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] += other.mData[i];
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator-=(
const Vector<Dimension, Type> &other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] -= other.mData[i];
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator*=(
const Vector<Dimension, Type> &other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] *= other.mData[i];
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator/=(
const Vector<Dimension, Type> &other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] /= other.mData[i];
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator*=(Type other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] *= other;
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator/=(Type other)
{
for (size_t i = 0; i < Dimension; ++i)
{
mData[i] /= other;
}
return *static_cast<Vector<Dimension, Type> *>(this);
}
// Implementation of comparison operators
template <size_t Dimension, typename Type>
bool VectorBase<Dimension, Type>::operator==(const VectorBase<Dimension, Type> &other) const
{
for (size_t i = 0; i < Dimension; ++i)
{
if (mData[i] != other.mData[i])
{
return false;
}
}
return true;
}
template <size_t Dimension, typename Type>
bool VectorBase<Dimension, Type>::operator!=(const VectorBase<Dimension, Type> &other) const
{
return !(*this == other);
}
// Implementation of other arithmetic operations
template <size_t Dimension, typename Type>
Type VectorBase<Dimension, Type>::length() const
{
static_assert(std::is_floating_point<Type>::value,
"VectorN::length is only defined for floating point vectors");
return std::sqrt(lengthSquared());
}
template <size_t Dimension, typename Type>
Type VectorBase<Dimension, Type>::lengthSquared() const
{
return dot(*this);
}
template <size_t Dimension, typename Type>
Type VectorBase<Dimension, Type>::dot(const VectorBase<Dimension, Type> &other) const
{
Type sum = Type();
for (size_t i = 0; i < Dimension; ++i)
{
sum += mData[i] * other.mData[i];
}
return sum;
}
template <size_t Dimension, typename Type>
std::ostream &operator<<(std::ostream &ostream, const VectorBase<Dimension, Type> &vector)
{
ostream << "[ ";
for (size_t elementIdx = 0; elementIdx < Dimension; elementIdx++)
{
if (elementIdx > 0)
{
ostream << ", ";
}
ostream << vector.data()[elementIdx];
}
ostream << " ]";
return ostream;
}
template <size_t Dimension, typename Type>
Vector<Dimension, Type> VectorBase<Dimension, Type>::normalized() const
{
static_assert(std::is_floating_point<Type>::value,
"VectorN::normalized is only defined for floating point vectors");
return *this / length();
}
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector)
{
return ostream << static_cast<const VectorBase<2, Type> &>(vector);
}
template <typename Type>
Vector<3, Type> Vector<3, Type>::cross(const Vector<3, Type> &other) const
{
return Vector<3, Type>(y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(),
x() * other.y() - y() * other.x());
}
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector)
{
return ostream << static_cast<const VectorBase<3, Type> &>(vector);
}
template <typename Type>
std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector)
{
return ostream << static_cast<const VectorBase<4, Type> &>(vector);
}
} // namespace angle
#endif // COMMON_VECTOR_UTILS_H_