This source file includes following definitions.
- _correction
- get
- procrustesRotationAndTranslation
- procrustesRotationAndTranslation
- jacobiRotateRight
- jacobiRotateRight
- twoSidedJacobiRotation
- twoSidedJacobiRotation
- swapColumns
- maxOffDiag
- maxOffDiag
- twoSidedJacobiSVD
- twoSidedJacobiSVD
- jacobiSVD
- jacobiSVD
- jacobiRotateRight
- jacobiRotation
- jacobiRotation
- maxOffDiagSymm
- jacobiEigenSolver
- jacobiEigenSolver
- maxEigenVector
- minEigenVector
#include "ImathMatrixAlgo.h"
#include <cmath>
#include <algorithm>
#if defined(OPENEXR_DLL)
#define EXPORT_CONST __declspec(dllexport)
#else
#define EXPORT_CONST const
#endif
namespace Imath {
EXPORT_CONST M33f identity33f ( 1, 0, 0,
0, 1, 0,
0, 0, 1);
EXPORT_CONST M33d identity33d ( 1, 0, 0,
0, 1, 0,
0, 0, 1);
EXPORT_CONST M44f identity44f ( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
EXPORT_CONST M44d identity44d ( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
namespace
{
class KahanSum
{
public:
KahanSum() : _total(0), _correction(0) {}
void
operator+= (const double val)
{
const double y = val - _correction;
const double t = _total + y;
_correction = (t - _total) - y;
_total = t;
}
double get() const
{
return _total;
}
private:
double _total;
double _correction;
};
}
template <typename T>
M44d
procrustesRotationAndTranslation (const Vec3<T>* A, const Vec3<T>* B, const T* weights, const size_t numPoints, const bool doScale)
{
if (numPoints == 0)
return M44d();
V3d Acenter (0.0);
V3d Bcenter (0.0);
double weightsSum = 0.0;
if (weights == 0)
{
for (int i = 0; i < numPoints; ++i)
{
Acenter += (V3d) A[i];
Bcenter += (V3d) B[i];
}
weightsSum = (double) numPoints;
}
else
{
for (int i = 0; i < numPoints; ++i)
{
const double w = weights[i];
weightsSum += w;
Acenter += w * (V3d) A[i];
Bcenter += w * (V3d) B[i];
}
}
if (weightsSum == 0)
return M44d();
Acenter /= weightsSum;
Bcenter /= weightsSum;
M33d C (0.0);
if (weights == 0)
{
for (int i = 0; i < numPoints; ++i)
C += outerProduct ((V3d) B[i] - Bcenter, (V3d) A[i] - Acenter);
}
else
{
for (int i = 0; i < numPoints; ++i)
{
const double w = weights[i];
C += outerProduct (w * ((V3d) B[i] - Bcenter), (V3d) A[i] - Acenter);
}
}
M33d U, V;
V3d S;
jacobiSVD (C, U, S, V, Imath::limits<double>::epsilon(), true);
const M33d Qt = V * U.transposed();
double s = 1.0;
if (doScale && numPoints > 1)
{
KahanSum traceATA;
if (weights == 0)
{
for (int i = 0; i < numPoints; ++i)
traceATA += ((V3d) A[i] - Acenter).length2();
}
else
{
for (int i = 0; i < numPoints; ++i)
traceATA += ((double) weights[i]) * ((V3d) A[i] - Acenter).length2();
}
KahanSum traceBATQ;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
traceBATQ += Qt[j][i] * C[i][j];
s = traceBATQ.get() / traceATA.get();
}
const V3d translate = Bcenter - s*Acenter*Qt;
return M44d (s*Qt.x[0][0], s*Qt.x[0][1], s*Qt.x[0][2], T(0),
s*Qt.x[1][0], s*Qt.x[1][1], s*Qt.x[1][2], T(0),
s*Qt.x[2][0], s*Qt.x[2][1], s*Qt.x[2][2], T(0),
translate.x, translate.y, translate.z, T(1));
}
template <typename T>
M44d
procrustesRotationAndTranslation (const Vec3<T>* A, const Vec3<T>* B, const size_t numPoints, const bool doScale)
{
return procrustesRotationAndTranslation (A, B, (const T*) 0, numPoints, doScale);
}
template M44d procrustesRotationAndTranslation (const V3d* from, const V3d* to, const size_t numPoints, const bool doScale);
template M44d procrustesRotationAndTranslation (const V3f* from, const V3f* to, const size_t numPoints, const bool doScale);
template M44d procrustesRotationAndTranslation (const V3d* from, const V3d* to, const double* weights, const size_t numPoints, const bool doScale);
template M44d procrustesRotationAndTranslation (const V3f* from, const V3f* to, const float* weights, const size_t numPoints, const bool doScale);
namespace
{
template <typename T, int j, int k>
void
jacobiRotateRight (Imath::Matrix33<T>& A,
const T c,
const T s)
{
for (int i = 0; i < 3; ++i)
{
const T tau1 = A[i][j];
const T tau2 = A[i][k];
A[i][j] = c * tau1 - s * tau2;
A[i][k] = s * tau1 + c * tau2;
}
}
template <typename T>
void
jacobiRotateRight (Imath::Matrix44<T>& A,
const int j,
const int k,
const T c,
const T s)
{
for (int i = 0; i < 4; ++i)
{
const T tau1 = A[i][j];
const T tau2 = A[i][k];
A[i][j] = c * tau1 - s * tau2;
A[i][k] = s * tau1 + c * tau2;
}
}
template <typename T, int j, int k, int l>
bool
twoSidedJacobiRotation (Imath::Matrix33<T>& A,
Imath::Matrix33<T>& U,
Imath::Matrix33<T>& V,
const T tol)
{
const T w = A[j][j];
const T x = A[j][k];
const T y = A[k][j];
const T z = A[k][k];
bool changed = false;
T mu_1 = w + z;
T mu_2 = x - y;
T c, s;
if (std::abs(mu_2) <= tol*std::abs(mu_1))
{
c = T(1);
s = T(0);
const T p = w;
const T r = z;
mu_1 = r - p;
mu_2 = x + y;
}
else
{
const T rho = mu_1 / mu_2;
s = T(1) / std::sqrt (T(1) + rho*rho);
if (rho < 0)
s = -s;
c = s * rho;
mu_1 = s * (x + y) + c * (z - w);
mu_2 = T(2) * (c * x - s * z);
changed = true;
}
T c_2, s_2;
if (std::abs(mu_2) <= tol*std::abs(mu_1))
{
c_2 = T(1);
s_2 = T(0);
}
else
{
const T rho_2 = mu_1 / mu_2;
T t_2 = T(1) / (std::abs(rho_2) + std::sqrt(1 + rho_2*rho_2));
if (rho_2 < 0)
t_2 = -t_2;
c_2 = T(1) / std::sqrt (T(1) + t_2*t_2);
s_2 = c_2 * t_2;
changed = true;
}
const T c_1 = c_2 * c - s_2 * s;
const T s_1 = s_2 * c + c_2 * s;
if (!changed)
{
A[k][j] = 0;
A[j][k] = 0;
return false;
}
const T d_1 = c_1*(w*c_2 - x*s_2) - s_1*(y*c_2 - z*s_2);
const T d_2 = s_1*(w*s_2 + x*c_2) + c_1*(y*s_2 + z*c_2);
A[j][j] = d_1;
A[k][k] = d_2;
A[k][j] = 0;
A[j][k] = 0;
{
const T tau1 = A[j][l];
const T tau2 = A[k][l];
A[j][l] = c_1 * tau1 - s_1 * tau2;
A[k][l] = s_1 * tau1 + c_1 * tau2;
}
{
const T tau1 = A[l][j];
const T tau2 = A[l][k];
A[l][j] = c_2 * tau1 - s_2 * tau2;
A[l][k] = s_2 * tau1 + c_2 * tau2;
}
jacobiRotateRight<T, j, k> (U, c_1, s_1);
jacobiRotateRight<T, j, k> (V, c_2, s_2);
return true;
}
template <typename T>
bool
twoSidedJacobiRotation (Imath::Matrix44<T>& A,
int j,
int k,
Imath::Matrix44<T>& U,
Imath::Matrix44<T>& V,
const T tol)
{
const T w = A[j][j];
const T x = A[j][k];
const T y = A[k][j];
const T z = A[k][k];
bool changed = false;
T mu_1 = w + z;
T mu_2 = x - y;
T c, s;
if (std::abs(mu_2) <= tol*std::abs(mu_1))
{
c = T(1);
s = T(0);
const T p = w;
const T r = z;
mu_1 = r - p;
mu_2 = x + y;
}
else
{
const T rho = mu_1 / mu_2;
s = T(1) / std::sqrt (T(1) + rho*rho);
if (rho < 0)
s = -s;
c = s * rho;
mu_1 = s * (x + y) + c * (z - w);
mu_2 = T(2) * (c * x - s * z);
changed = true;
}
T c_2, s_2;
if (std::abs(mu_2) <= tol*std::abs(mu_1))
{
c_2 = T(1);
s_2 = T(0);
}
else
{
const T rho_2 = mu_1 / mu_2;
T t_2 = T(1) / (std::abs(rho_2) + std::sqrt(1 + rho_2*rho_2));
if (rho_2 < 0)
t_2 = -t_2;
c_2 = T(1) / std::sqrt (T(1) + t_2*t_2);
s_2 = c_2 * t_2;
changed = true;
}
const T c_1 = c_2 * c - s_2 * s;
const T s_1 = s_2 * c + c_2 * s;
if (!changed)
{
A[k][j] = 0;
A[j][k] = 0;
return false;
}
const T d_1 = c_1*(w*c_2 - x*s_2) - s_1*(y*c_2 - z*s_2);
const T d_2 = s_1*(w*s_2 + x*c_2) + c_1*(y*s_2 + z*c_2);
A[j][j] = d_1;
A[k][k] = d_2;
A[k][j] = 0;
A[j][k] = 0;
for (int l = 0; l < 4; ++l)
{
if (l == j || l == k)
continue;
const T tau1 = A[j][l];
const T tau2 = A[k][l];
A[j][l] = c_1 * tau1 - s_1 * tau2;
A[k][l] = s_1 * tau1 + c_1 * tau2;
}
for (int l = 0; l < 4; ++l)
{
if (l == j || l == k)
continue;
const T tau1 = A[l][j];
const T tau2 = A[l][k];
A[l][j] = c_2 * tau1 - s_2 * tau2;
A[l][k] = s_2 * tau1 + c_2 * tau2;
}
jacobiRotateRight (U, j, k, c_1, s_1);
jacobiRotateRight (V, j, k, c_2, s_2);
return true;
}
template <typename T>
void
swapColumns (Imath::Matrix33<T>& A, int j, int k)
{
for (int i = 0; i < 3; ++i)
std::swap (A[i][j], A[i][k]);
}
template <typename T>
T
maxOffDiag (const Imath::Matrix33<T>& A)
{
T result = 0;
result = std::max (result, std::abs (A[0][1]));
result = std::max (result, std::abs (A[0][2]));
result = std::max (result, std::abs (A[1][0]));
result = std::max (result, std::abs (A[1][2]));
result = std::max (result, std::abs (A[2][0]));
result = std::max (result, std::abs (A[2][1]));
return result;
}
template <typename T>
T
maxOffDiag (const Imath::Matrix44<T>& A)
{
T result = 0;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
if (i != j)
result = std::max (result, std::abs (A[i][j]));
}
}
return result;
}
template <typename T>
void
twoSidedJacobiSVD (Imath::Matrix33<T> A,
Imath::Matrix33<T>& U,
Imath::Vec3<T>& S,
Imath::Matrix33<T>& V,
const T tol,
const bool forcePositiveDeterminant)
{
U.makeIdentity();
V.makeIdentity();
const int maxIter = 20;
const T absTol = tol * maxOffDiag (A);
if (absTol != 0)
{
int numIter = 0;
do
{
++numIter;
bool changed = twoSidedJacobiRotation<T, 0, 1, 2> (A, U, V, tol);
changed = twoSidedJacobiRotation<T, 0, 2, 1> (A, U, V, tol) || changed;
changed = twoSidedJacobiRotation<T, 1, 2, 0> (A, U, V, tol) || changed;
if (!changed)
break;
} while (maxOffDiag(A) > absTol && numIter < maxIter);
}
S.x = A[0][0];
S.y = A[1][1];
S.z = A[2][2];
for (int i = 0; i < 3; ++i)
{
if (S[i] < 0)
{
S[i] = -S[i];
for (int j = 0; j < 3; ++j)
U[j][i] = -U[j][i];
}
}
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < (2 - i); ++j)
{
if (S[j] < S[j+1])
{
std::swap (S[j], S[j+1]);
swapColumns (U, j, j+1);
swapColumns (V, j, j+1);
}
}
}
if (forcePositiveDeterminant)
{
if (U.determinant() < 0)
{
for (int i = 0; i < 3; ++i)
U[i][2] = -U[i][2];
S.z = -S.z;
}
if (V.determinant() < 0)
{
for (int i = 0; i < 3; ++i)
V[i][2] = -V[i][2];
S.z = -S.z;
}
}
}
template <typename T>
void
twoSidedJacobiSVD (Imath::Matrix44<T> A,
Imath::Matrix44<T>& U,
Imath::Vec4<T>& S,
Imath::Matrix44<T>& V,
const T tol,
const bool forcePositiveDeterminant)
{
U.makeIdentity();
V.makeIdentity();
const int maxIter = 20;
const T absTol = tol * maxOffDiag (A);
if (absTol != 0)
{
int numIter = 0;
do
{
++numIter;
bool changed = twoSidedJacobiRotation (A, 0, 1, U, V, tol);
changed = twoSidedJacobiRotation (A, 0, 2, U, V, tol) || changed;
changed = twoSidedJacobiRotation (A, 0, 3, U, V, tol) || changed;
changed = twoSidedJacobiRotation (A, 1, 2, U, V, tol) || changed;
changed = twoSidedJacobiRotation (A, 1, 3, U, V, tol) || changed;
changed = twoSidedJacobiRotation (A, 2, 3, U, V, tol) || changed;
if (!changed)
break;
} while (maxOffDiag(A) > absTol && numIter < maxIter);
}
S[0] = A[0][0];
S[1] = A[1][1];
S[2] = A[2][2];
S[3] = A[3][3];
for (int i = 0; i < 4; ++i)
{
if (S[i] < 0)
{
S[i] = -S[i];
for (int j = 0; j < 4; ++j)
U[j][i] = -U[j][i];
}
}
for (int i = 1; i < 4; ++i)
{
const Imath::Vec4<T> uCol (U[0][i], U[1][i], U[2][i], U[3][i]);
const Imath::Vec4<T> vCol (V[0][i], V[1][i], V[2][i], V[3][i]);
const T sVal = S[i];
int j = i - 1;
while (std::abs (S[j]) < std::abs (sVal))
{
for (int k = 0; k < 4; ++k)
U[k][j+1] = U[k][j];
for (int k = 0; k < 4; ++k)
V[k][j+1] = V[k][j];
S[j+1] = S[j];
--j;
if (j < 0)
break;
}
for (int k = 0; k < 4; ++k)
U[k][j+1] = uCol[k];
for (int k = 0; k < 4; ++k)
V[k][j+1] = vCol[k];
S[j+1] = sVal;
}
if (forcePositiveDeterminant)
{
if (U.determinant() < 0)
{
for (int i = 0; i < 4; ++i)
U[i][3] = -U[i][3];
S[3] = -S[3];
}
if (V.determinant() < 0)
{
for (int i = 0; i < 4; ++i)
V[i][3] = -V[i][3];
S[3] = -S[3];
}
}
}
}
template <typename T>
void
jacobiSVD (const Imath::Matrix33<T>& A,
Imath::Matrix33<T>& U,
Imath::Vec3<T>& S,
Imath::Matrix33<T>& V,
const T tol,
const bool forcePositiveDeterminant)
{
twoSidedJacobiSVD (A, U, S, V, tol, forcePositiveDeterminant);
}
template <typename T>
void
jacobiSVD (const Imath::Matrix44<T>& A,
Imath::Matrix44<T>& U,
Imath::Vec4<T>& S,
Imath::Matrix44<T>& V,
const T tol,
const bool forcePositiveDeterminant)
{
twoSidedJacobiSVD (A, U, S, V, tol, forcePositiveDeterminant);
}
template void jacobiSVD (const Imath::Matrix33<float>& A,
Imath::Matrix33<float>& U,
Imath::Vec3<float>& S,
Imath::Matrix33<float>& V,
const float tol,
const bool forcePositiveDeterminant);
template void jacobiSVD (const Imath::Matrix33<double>& A,
Imath::Matrix33<double>& U,
Imath::Vec3<double>& S,
Imath::Matrix33<double>& V,
const double tol,
const bool forcePositiveDeterminant);
template void jacobiSVD (const Imath::Matrix44<float>& A,
Imath::Matrix44<float>& U,
Imath::Vec4<float>& S,
Imath::Matrix44<float>& V,
const float tol,
const bool forcePositiveDeterminant);
template void jacobiSVD (const Imath::Matrix44<double>& A,
Imath::Matrix44<double>& U,
Imath::Vec4<double>& S,
Imath::Matrix44<double>& V,
const double tol,
const bool forcePositiveDeterminant);
namespace
{
template <int j, int k, typename TM>
inline
void
jacobiRotateRight (TM& A,
const typename TM::BaseType s,
const typename TM::BaseType tau)
{
typedef typename TM::BaseType T;
for (unsigned int i = 0; i < TM::dimensions(); ++i)
{
const T nu1 = A[i][j];
const T nu2 = A[i][k];
A[i][j] -= s * (nu2 + tau * nu1);
A[i][k] += s * (nu1 - tau * nu2);
}
}
template <int j, int k, int l, typename T>
bool
jacobiRotation (Matrix33<T>& A,
Matrix33<T>& V,
Vec3<T>& Z,
const T tol)
{
const T x = A[j][j];
const T y = A[j][k];
const T z = A[k][k];
const T mu1 = z - x;
const T mu2 = 2 * y;
if (std::abs(mu2) <= tol*std::abs(mu1))
{
A[j][k] = 0;
return false;
}
const T rho = mu1 / mu2;
const T t = (rho < 0 ? T(-1) : T(1)) / (std::abs(rho) + std::sqrt(1 + rho*rho));
const T c = T(1) / std::sqrt (T(1) + t*t);
const T s = t * c;
const T tau = s / (T(1) + c);
const T h = t * y;
Z[j] -= h;
Z[k] += h;
A[j][j] -= h;
A[k][k] += h;
A[j][k] = 0;
T& offd1 = l < j ? A[l][j] : A[j][l];
T& offd2 = l < k ? A[l][k] : A[k][l];
const T nu1 = offd1;
const T nu2 = offd2;
offd1 = nu1 - s * (nu2 + tau * nu1);
offd2 = nu2 + s * (nu1 - tau * nu2);
jacobiRotateRight<j, k> (V, s, tau);
return true;
}
template <int j, int k, int l1, int l2, typename T>
bool
jacobiRotation (Matrix44<T>& A,
Matrix44<T>& V,
Vec4<T>& Z,
const T tol)
{
const T x = A[j][j];
const T y = A[j][k];
const T z = A[k][k];
const T mu1 = z - x;
const T mu2 = T(2) * y;
if (std::abs(mu2) <= tol*std::abs(mu1))
{
A[j][k] = 0;
return true;
}
const T rho = mu1 / mu2;
const T t = (rho < 0 ? T(-1) : T(1)) / (std::abs(rho) + std::sqrt(1 + rho*rho));
const T c = T(1) / std::sqrt (T(1) + t*t);
const T s = c * t;
const T tau = s / (T(1) + c);
const T h = t * y;
Z[j] -= h;
Z[k] += h;
A[j][j] -= h;
A[k][k] += h;
A[j][k] = 0;
{
T& offd1 = l1 < j ? A[l1][j] : A[j][l1];
T& offd2 = l1 < k ? A[l1][k] : A[k][l1];
const T nu1 = offd1;
const T nu2 = offd2;
offd1 -= s * (nu2 + tau * nu1);
offd2 += s * (nu1 - tau * nu2);
}
{
T& offd1 = l2 < j ? A[l2][j] : A[j][l2];
T& offd2 = l2 < k ? A[l2][k] : A[k][l2];
const T nu1 = offd1;
const T nu2 = offd2;
offd1 -= s * (nu2 + tau * nu1);
offd2 += s * (nu1 - tau * nu2);
}
jacobiRotateRight<j, k> (V, s, tau);
return true;
}
template <typename TM>
inline
typename TM::BaseType
maxOffDiagSymm (const TM& A)
{
typedef typename TM::BaseType T;
T result = 0;
for (unsigned int i = 0; i < TM::dimensions(); ++i)
for (unsigned int j = i+1; j < TM::dimensions(); ++j)
result = std::max (result, std::abs (A[i][j]));
return result;
}
}
template <typename T>
void
jacobiEigenSolver (Matrix33<T>& A,
Vec3<T>& S,
Matrix33<T>& V,
const T tol)
{
V.makeIdentity();
for(int i = 0; i < 3; ++i) {
S[i] = A[i][i];
}
const int maxIter = 20;
const T absTol = tol * maxOffDiagSymm (A);
if (absTol != 0)
{
int numIter = 0;
do
{
Vec3<T> Z(0, 0, 0);
++numIter;
bool changed = jacobiRotation<0, 1, 2> (A, V, Z, tol);
changed = jacobiRotation<0, 2, 1> (A, V, Z, tol) || changed;
changed = jacobiRotation<1, 2, 0> (A, V, Z, tol) || changed;
for(int i = 0; i < 3; ++i) {
A[i][i] = S[i] += Z[i];
}
if (!changed)
break;
} while (maxOffDiagSymm(A) > absTol && numIter < maxIter);
}
}
template <typename T>
void
jacobiEigenSolver (Matrix44<T>& A,
Vec4<T>& S,
Matrix44<T>& V,
const T tol)
{
V.makeIdentity();
for(int i = 0; i < 4; ++i) {
S[i] = A[i][i];
}
const int maxIter = 20;
const T absTol = tol * maxOffDiagSymm (A);
if (absTol != 0)
{
int numIter = 0;
do
{
++numIter;
Vec4<T> Z(0, 0, 0, 0);
bool changed = jacobiRotation<0, 1, 2, 3> (A, V, Z, tol);
changed = jacobiRotation<0, 2, 1, 3> (A, V, Z, tol) || changed;
changed = jacobiRotation<0, 3, 1, 2> (A, V, Z, tol) || changed;
changed = jacobiRotation<1, 2, 0, 3> (A, V, Z, tol) || changed;
changed = jacobiRotation<1, 3, 0, 2> (A, V, Z, tol) || changed;
changed = jacobiRotation<2, 3, 0, 1> (A, V, Z, tol) || changed;
for(int i = 0; i < 4; ++i) {
A[i][i] = S[i] += Z[i];
}
if (!changed)
break;
} while (maxOffDiagSymm(A) > absTol && numIter < maxIter);
}
}
template <typename TM, typename TV>
void
maxEigenVector (TM& A, TV& V)
{
TV S;
TM MV;
jacobiEigenSolver(A, S, MV);
int maxIdx(0);
for(unsigned int i = 1; i < TV::dimensions(); ++i)
{
if(std::abs(S[i]) > std::abs(S[maxIdx]))
maxIdx = i;
}
for(unsigned int i = 0; i < TV::dimensions(); ++i)
V[i] = MV[i][maxIdx];
}
template <typename TM, typename TV>
void
minEigenVector (TM& A, TV& V)
{
TV S;
TM MV;
jacobiEigenSolver(A, S, MV);
int minIdx(0);
for(unsigned int i = 1; i < TV::dimensions(); ++i)
{
if(std::abs(S[i]) < std::abs(S[minIdx]))
minIdx = i;
}
for(unsigned int i = 0; i < TV::dimensions(); ++i)
V[i] = MV[i][minIdx];
}
template void jacobiEigenSolver (Matrix33<float>& A,
Vec3<float>& S,
Matrix33<float>& V,
const float tol);
template void jacobiEigenSolver (Matrix33<double>& A,
Vec3<double>& S,
Matrix33<double>& V,
const double tol);
template void jacobiEigenSolver (Matrix44<float>& A,
Vec4<float>& S,
Matrix44<float>& V,
const float tol);
template void jacobiEigenSolver (Matrix44<double>& A,
Vec4<double>& S,
Matrix44<double>& V,
const double tol);
template void maxEigenVector (Matrix33<float>& A,
Vec3<float>& S);
template void maxEigenVector (Matrix44<float>& A,
Vec4<float>& S);
template void maxEigenVector (Matrix33<double>& A,
Vec3<double>& S);
template void maxEigenVector (Matrix44<double>& A,
Vec4<double>& S);
template void minEigenVector (Matrix33<float>& A,
Vec3<float>& S);
template void minEigenVector (Matrix44<float>& A,
Vec4<float>& S);
template void minEigenVector (Matrix33<double>& A,
Vec3<double>& S);
template void minEigenVector (Matrix44<double>& A,
Vec4<double>& S);
}