// // 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 #include #include #include namespace angle { template 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 class VectorBase { public: using VectorN = Vector; // Constructors VectorBase() = default; explicit VectorBase(Type element); template VectorBase(const VectorBase &other); template 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 VectorN &other) const; bool operator!=(const VectorN &other) const; // Other arithmetic operations Type length() const; Type lengthSquared() const; Type dot(const VectorBase &other) const; VectorN normalized() const; protected: template void initWithList(const Vector &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 typename std::enable_if::value>::type initWithList( OtherType arg1, const Args &... args); template void initWithList() const; template friend class VectorBase; Type mData[Dimension]; }; template std::ostream &operator<<(std::ostream &ostream, const VectorBase &vector); template 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 std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector); template 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 std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector); template 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 std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector); // Implementation of constructors and misc operations template VectorBase::VectorBase(Type element) { for (size_t i = 0; i < Dimension; ++i) { mData[i] = element; } } template template VectorBase::VectorBase(const VectorBase &other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] = static_cast(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 template VectorBase::VectorBase(const Arg1 &arg1, const Arg2 &arg2, const Args &... args) { initWithList<0>(arg1, arg2, args...); } template template void VectorBase::initWithList(const Vector &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(arg1.mData[i]); } initWithList(args...); } template template typename std::enable_if::value>::type VectorBase::initWithList(OtherType arg1, const Args &... args) { static_assert(CurrentIndex + 1 <= Dimension, "Too much data in the vector constructor."); mData[CurrentIndex] = static_cast(arg1); initWithList(args...); } template template void VectorBase::initWithList() const { static_assert(CurrentIndex == Dimension, "Not enough data in the vector constructor."); } template Vector VectorBase::Load(const Type *source) { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = source[i]; } return result; } template void VectorBase::Store(const Vector &source, Type *destination) { for (size_t i = 0; i < Dimension; ++i) { destination[i] = source.mData[i]; } } // Implementation of basic arithmetic operations template Vector VectorBase::operator+() const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = +mData[i]; } return result; } template Vector VectorBase::operator-() const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = -mData[i]; } return result; } template Vector VectorBase::operator+( const Vector &other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] + other.mData[i]; } return result; } template Vector VectorBase::operator-( const Vector &other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] - other.mData[i]; } return result; } template Vector VectorBase::operator*( const Vector &other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] * other.mData[i]; } return result; } template Vector VectorBase::operator/( const Vector &other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] / other.mData[i]; } return result; } template Vector VectorBase::operator*(Type other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] * other; } return result; } template Vector VectorBase::operator/(Type other) const { Vector result; for (size_t i = 0; i < Dimension; ++i) { result.mData[i] = mData[i] / other; } return result; } // Implementation of compound arithmetic operations template Vector &VectorBase::operator+=( const Vector &other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] += other.mData[i]; } return *reinterpret_cast *>(this); } template Vector &VectorBase::operator-=( const Vector &other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] -= other.mData[i]; } return *reinterpret_cast *>(this); } template Vector &VectorBase::operator*=( const Vector &other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] *= other.mData[i]; } return *reinterpret_cast *>(this); } template Vector &VectorBase::operator/=( const Vector &other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] /= other.mData[i]; } return *reinterpret_cast *>(this); } template Vector &VectorBase::operator*=(Type other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] *= other; } return *reinterpret_cast *>(this); } template Vector &VectorBase::operator/=(Type other) { for (size_t i = 0; i < Dimension; ++i) { mData[i] /= other; } return *reinterpret_cast *>(this); } // Implementation of comparison operators template bool VectorBase::operator==(const Vector &other) const { for (size_t i = 0; i < Dimension; ++i) { if (mData[i] != other.mData[i]) { return false; } } return true; } template bool VectorBase::operator!=(const Vector &other) const { return !(*this == other); } // Implementation of other arithmetic operations template Type VectorBase::length() const { static_assert(std::is_floating_point::value, "VectorN::length is only defined for floating point vectors"); return std::sqrt(lengthSquared()); } template Type VectorBase::lengthSquared() const { return dot(*this); } template Type VectorBase::dot(const VectorBase &other) const { Type sum = Type(); for (size_t i = 0; i < Dimension; ++i) { sum += mData[i] * other.mData[i]; } return sum; } template std::ostream &operator<<(std::ostream &ostream, const VectorBase &vector) { ostream << "[ "; for (size_t elementIdx = 0; elementIdx < Dimension; elementIdx++) { if (elementIdx > 0) { ostream << ", "; } ostream << vector.data()[elementIdx]; } ostream << " ]"; return ostream; } template Vector VectorBase::normalized() const { static_assert(std::is_floating_point::value, "VectorN::normalized is only defined for floating point vectors"); return *this / length(); } template std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector) { return ostream << static_cast &>(vector); } template 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 std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector) { return ostream << static_cast &>(vector); } template std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector) { return ostream << static_cast &>(vector); } } // namespace angle #endif // COMMON_VECTOR_UTILS_H_