You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

578 lines
16 KiB

// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// 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 <Mathematics/Math.h>
#include <algorithm>
#include <array>
#include <initializer_list>
namespace gte
{
template <int N, typename Real>
class Vector
{
public:
// The tuple is uninitialized.
Vector() = default;
// The tuple is fully initialized by the inputs.
Vector(std::array<Real, N> 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<N,Real> zero{(Real)0};
// WARNING: The C++ 11 specification states that
// Vector<N,Real> zero{};
// will lead to a call of the default constructor, not the initializer
// constructor!
Vector(std::initializer_list<Real> values)
{
int const numValues = static_cast<int>(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<N, Real> v;
v.MakeZero();
return v;
}
static Vector Ones()
{
Vector<N, Real> v;
v.MakeOnes();
return v;
}
static Vector Unit(int d)
{
Vector<N, Real> 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<Real, N> mTuple;
};
// Unary operations.
template <int N, typename Real>
Vector<N, Real> operator+(Vector<N, Real> const& v)
{
return v;
}
template <int N, typename Real>
Vector<N, Real> operator-(Vector<N, Real> const& v)
{
Vector<N, Real> result;
for (int i = 0; i < N; ++i)
{
result[i] = -v[i];
}
return result;
}
// Linear-algebraic operations.
template <int N, typename Real>
Vector<N, Real> operator+(Vector<N, Real> const& v0, Vector<N, Real> const& v1)
{
Vector<N, Real> result = v0;
return result += v1;
}
template <int N, typename Real>
Vector<N, Real> operator-(Vector<N, Real> const& v0, Vector<N, Real> const& v1)
{
Vector<N, Real> result = v0;
return result -= v1;
}
template <int N, typename Real>
Vector<N, Real> operator*(Vector<N, Real> const& v, Real scalar)
{
Vector<N, Real> result = v;
return result *= scalar;
}
template <int N, typename Real>
Vector<N, Real> operator*(Real scalar, Vector<N, Real> const& v)
{
Vector<N, Real> result = v;
return result *= scalar;
}
template <int N, typename Real>
Vector<N, Real> operator/(Vector<N, Real> const& v, Real scalar)
{
Vector<N, Real> result = v;
return result /= scalar;
}
template <int N, typename Real>
Vector<N, Real>& operator+=(Vector<N, Real>& v0, Vector<N, Real> const& v1)
{
for (int i = 0; i < N; ++i)
{
v0[i] += v1[i];
}
return v0;
}
template <int N, typename Real>
Vector<N, Real>& operator-=(Vector<N, Real>& v0, Vector<N, Real> const& v1)
{
for (int i = 0; i < N; ++i)
{
v0[i] -= v1[i];
}
return v0;
}
template <int N, typename Real>
Vector<N, Real>& operator*=(Vector<N, Real>& v, Real scalar)
{
for (int i = 0; i < N; ++i)
{
v[i] *= scalar;
}
return v;
}
template <int N, typename Real>
Vector<N, Real>& operator/=(Vector<N, Real>& 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 <int N, typename Real>
Vector<N, Real> operator*(Vector<N, Real> const& v0, Vector<N, Real> const& v1)
{
Vector<N, Real> result = v0;
return result *= v1;
}
template <int N, typename Real>
Vector<N, Real> operator/(Vector<N, Real> const& v0, Vector<N, Real> const& v1)
{
Vector<N, Real> result = v0;
return result /= v1;
}
template <int N, typename Real>
Vector<N, Real>& operator*=(Vector<N, Real>& v0, Vector<N, Real> const& v1)
{
for (int i = 0; i < N; ++i)
{
v0[i] *= v1[i];
}
return v0;
}
template <int N, typename Real>
Vector<N, Real>& operator/=(Vector<N, Real>& v0, Vector<N, Real> 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 <int N, typename Real>
Real Dot(Vector<N, Real> const& v0, Vector<N, Real> const& v1)
{
Real dot = v0[0] * v1[0];
for (int i = 1; i < N; ++i)
{
dot += v0[i] * v1[i];
}
return dot;
}
template <int N, typename Real>
Real Length(Vector<N, Real> 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<N, Real> scaled = v / maxAbsComp;
length = maxAbsComp * std::sqrt(Dot(scaled, scaled));
}
else
{
length = (Real)0;
}
return length;
}
else
{
return std::sqrt(Dot(v, v));
}
}
template <int N, typename Real>
Real Normalize(Vector<N, Real>& 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 <int N, typename Real>
Real Orthonormalize(int numInputs, Vector<N, Real>* 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 <int N, typename Real>
Vector<N, Real> GetOrthogonal(Vector<N, Real> 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<N, Real> 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 <int N, typename Real>
bool ComputeExtremes(int numVectors, Vector<N, Real> const* v,
Vector<N, Real>& vmin, Vector<N, Real>& vmax)
{
if (v && numVectors > 0)
{
vmin = v[0];
vmax = vmin;
for (int j = 1; j < numVectors; ++j)
{
Vector<N, Real> 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 <int N, typename Real>
Vector<N + 1, Real> HLift(Vector<N, Real> const& v, Real last)
{
Vector<N + 1, Real> 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 <int N, typename Real>
Vector<N - 1, Real> HProject(Vector<N, Real> const& v)
{
static_assert(N >= 2, "Invalid dimension.");
Vector<N - 1, Real> 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 <int N, typename Real>
Vector<N + 1, Real> Lift(Vector<N, Real> const& v, int inject, Real value)
{
Vector<N + 1, Real> 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 <int N, typename Real>
Vector<N - 1, Real> Project(Vector<N, Real> const& v, int reject)
{
static_assert(N >= 2, "Invalid dimension.");
Vector<N - 1, Real> result;
for (int i = 0, j = 0; i < N - 1; ++i, ++j)
{
if (j == reject)
{
++j;
}
result[i] = v[j];
}
return result;
}
}