// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include #include namespace WwiseGTE { template class Vector { public: // The tuple is uninitialized. Vector() = default; // The tuple is fully initialized by the inputs. Vector(std::array const& values) : mTuple(values) { } // At most N elements are copied from the initializer list, setting // any remaining elements to zero. Create the zero vector using the // syntax // Vector zero{(Real)0}; // WARNING: The C++ 11 specification states that // Vector zero{}; // will lead to a call of the default constructor, not the initializer // constructor! Vector(std::initializer_list values) { int const numValues = static_cast(values.size()); if (N == numValues) { std::copy(values.begin(), values.end(), mTuple.begin()); } else if (N > numValues) { std::copy(values.begin(), values.end(), mTuple.begin()); std::fill(mTuple.begin() + numValues, mTuple.end(), (Real)0); } else // N < numValues { std::copy(values.begin(), values.begin() + N, mTuple.begin()); } } // For 0 <= d < N, element d is 1 and all others are 0. If d is // invalid, the zero vector is created. This is a convenience for // creating the standard Euclidean basis vectors; see also // MakeUnit(int) and Unit(int). Vector(int d) { MakeUnit(d); } // The copy constructor, destructor, and assignment operator are // generated by the compiler. // Member access. The first operator[] returns a const reference // rather than a Real value. This supports writing via standard file // operations that require a const pointer to data. inline int GetSize() const { return N; } inline Real const& operator[](int i) const { return mTuple[i]; } inline Real& operator[](int i) { return mTuple[i]; } // Comparisons for sorted containers and geometric ordering. inline bool operator==(Vector const& vec) const { return mTuple == vec.mTuple; } inline bool operator!=(Vector const& vec) const { return mTuple != vec.mTuple; } inline bool operator< (Vector const& vec) const { return mTuple < vec.mTuple; } inline bool operator<=(Vector const& vec) const { return mTuple <= vec.mTuple; } inline bool operator> (Vector const& vec) const { return mTuple > vec.mTuple; } inline bool operator>=(Vector const& vec) const { return mTuple >= vec.mTuple; } // Special vectors. // All components are 0. void MakeZero() { std::fill(mTuple.begin(), mTuple.end(), (Real)0); } // All components are 1. void MakeOnes() { std::fill(mTuple.begin(), mTuple.end(), (Real)1); } // Component d is 1, all others are zero. void MakeUnit(int d) { std::fill(mTuple.begin(), mTuple.end(), (Real)0); if (0 <= d && d < N) { mTuple[d] = (Real)1; } } static Vector Zero() { Vector v; v.MakeZero(); return v; } static Vector Ones() { Vector v; v.MakeOnes(); return v; } static Vector Unit(int d) { Vector v; v.MakeUnit(d); return v; } protected: // This data structure takes advantage of the built-in operator[], // range checking, and visualizers in MSVS. std::array mTuple; }; // Unary operations. template Vector operator+(Vector const& v) { return v; } template Vector operator-(Vector const& v) { Vector result; for (int i = 0; i < N; ++i) { result[i] = -v[i]; } return result; } // Linear-algebraic operations. template Vector operator+(Vector const& v0, Vector const& v1) { Vector result = v0; return result += v1; } template Vector operator-(Vector const& v0, Vector const& v1) { Vector result = v0; return result -= v1; } template Vector operator*(Vector const& v, Real scalar) { Vector result = v; return result *= scalar; } template Vector operator*(Real scalar, Vector const& v) { Vector result = v; return result *= scalar; } template Vector operator/(Vector const& v, Real scalar) { Vector result = v; return result /= scalar; } template Vector& operator+=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] += v1[i]; } return v0; } template Vector& operator-=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] -= v1[i]; } return v0; } template Vector& operator*=(Vector& v, Real scalar) { for (int i = 0; i < N; ++i) { v[i] *= scalar; } return v; } template Vector& operator/=(Vector& v, Real scalar) { if (scalar != (Real)0) { Real invScalar = (Real)1 / scalar; for (int i = 0; i < N; ++i) { v[i] *= invScalar; } } else { for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return v; } // Componentwise algebraic operations. template Vector operator*(Vector const& v0, Vector const& v1) { Vector result = v0; return result *= v1; } template Vector operator/(Vector const& v0, Vector const& v1) { Vector result = v0; return result /= v1; } template Vector& operator*=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] *= v1[i]; } return v0; } template Vector& operator/=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] /= v1[i]; } return v0; } // Geometric operations. The functions with 'robust' set to 'false' use // the standard algorithm for normalizing a vector by computing the length // as a square root of the squared length and dividing by it. The results // can be infinite (or NaN) if the length is zero. When 'robust' is set // to 'true', the algorithm is designed to avoid floating-point overflow // and sets the normalized vector to zero when the length is zero. template Real Dot(Vector const& v0, Vector const& v1) { Real dot = v0[0] * v1[0]; for (int i = 1; i < N; ++i) { dot += v0[i] * v1[i]; } return dot; } template Real Length(Vector const& v, bool robust = false) { if (robust) { Real maxAbsComp = std::fabs(v[0]); for (int i = 1; i < N; ++i) { Real absComp = std::fabs(v[i]); if (absComp > maxAbsComp) { maxAbsComp = absComp; } } Real length; if (maxAbsComp > (Real)0) { Vector scaled = v / maxAbsComp; length = maxAbsComp * std::sqrt(Dot(scaled, scaled)); } else { length = (Real)0; } return length; } else { return std::sqrt(Dot(v, v)); } } template Real Normalize(Vector& v, bool robust = false) { if (robust) { Real maxAbsComp = std::fabs(v[0]); for (int i = 1; i < N; ++i) { Real absComp = std::fabs(v[i]); if (absComp > maxAbsComp) { maxAbsComp = absComp; } } Real length; if (maxAbsComp > (Real)0) { v /= maxAbsComp; length = std::sqrt(Dot(v, v)); v /= length; length *= maxAbsComp; } else { length = (Real)0; for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return length; } else { Real length = std::sqrt(Dot(v, v)); if (length > (Real)0) { v /= length; } else { for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return length; } } // Gram-Schmidt orthonormalization to generate orthonormal vectors from // the linearly independent inputs. The function returns the smallest // length of the unnormalized vectors computed during the process. If // this value is nearly zero, it is possible that the inputs are linearly // dependent (within numerical round-off errors). On input, // 1 <= numElements <= N and v[0] through v[numElements-1] must be // initialized. On output, the vectors v[0] through v[numElements-1] // form an orthonormal set. template Real Orthonormalize(int numInputs, Vector* v, bool robust = false) { if (v && 1 <= numInputs && numInputs <= N) { Real minLength = Normalize(v[0], robust); for (int i = 1; i < numInputs; ++i) { for (int j = 0; j < i; ++j) { Real dot = Dot(v[i], v[j]); v[i] -= v[j] * dot; } Real length = Normalize(v[i], robust); if (length < minLength) { minLength = length; } } return minLength; } return (Real)0; } // Construct a single vector orthogonal to the nonzero input vector. If // the maximum absolute component occurs at index i, then the orthogonal // vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components // zero. The index addition i+1 is computed modulo N. template Vector GetOrthogonal(Vector const& v, bool unitLength) { Real cmax = std::fabs(v[0]); int imax = 0; for (int i = 1; i < N; ++i) { Real c = std::fabs(v[i]); if (c > cmax) { cmax = c; imax = i; } } Vector result; result.MakeZero(); int inext = imax + 1; if (inext == N) { inext = 0; } result[imax] = v[inext]; result[inext] = -v[imax]; if (unitLength) { Real sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; Real invLength = ((Real)1) / std::sqrt(sqrDistance); result[imax] *= invLength; result[inext] *= invLength; } return result; } // Compute the axis-aligned bounding box of the vectors. The return value // is 'true' iff the inputs are valid, in which case vmin and vmax have // valid values. template bool ComputeExtremes(int numVectors, Vector const* v, Vector& vmin, Vector& vmax) { if (v && numVectors > 0) { vmin = v[0]; vmax = vmin; for (int j = 1; j < numVectors; ++j) { Vector const& vec = v[j]; for (int i = 0; i < N; ++i) { if (vec[i] < vmin[i]) { vmin[i] = vec[i]; } else if (vec[i] > vmax[i]) { vmax[i] = vec[i]; } } } return true; } return false; } // Lift n-tuple v to homogeneous (n+1)-tuple (v,last). template Vector HLift(Vector const& v, Real last) { Vector result; for (int i = 0; i < N; ++i) { result[i] = v[i]; } result[N] = last; return result; } // Project homogeneous n-tuple v = (u,v[n-1]) to (n-1)-tuple u. template Vector HProject(Vector const& v) { static_assert(N >= 2, "Invalid dimension."); Vector result; for (int i = 0; i < N - 1; ++i) { result[i] = v[i]; } return result; } // Lift n-tuple v = (w0,w1) to (n+1)-tuple u = (w0,u[inject],w1). By // inference, w0 is a (inject)-tuple [nonexistent when inject=0] and w1 is // a (n-inject)-tuple [nonexistent when inject=n]. template Vector Lift(Vector const& v, int inject, Real value) { Vector result; int i; for (i = 0; i < inject; ++i) { result[i] = v[i]; } result[i] = value; int j = i; for (++j; i < N; ++i, ++j) { result[j] = v[i]; } return result; } // Project n-tuple v = (w0,v[reject],w1) to (n-1)-tuple u = (w0,w1). By // inference, w0 is a (reject)-tuple [nonexistent when reject=0] and w1 is // a (n-1-reject)-tuple [nonexistent when reject=n-1]. template Vector Project(Vector const& v, int reject) { static_assert(N >= 2, "Invalid dimension."); Vector result; for (int i = 0, j = 0; i < N - 1; ++i, ++j) { if (j == reject) { ++j; } result[i] = v[j]; } return result; } }