// Wild Magic Source Code // David Eberly // http://www.geometrictools.com // Copyright (c) 1998-2007 // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. The license is available for reading at // either of the locations: // http://www.gnu.org/copyleft/lgpl.html // http://www.geometrictools.com/License/WildMagicLicense.pdf // The license applies to versions 0 through 4 of Wild Magic. // // Version: 4.0.0 (2006/06/28) #include "Wm4FoundationPCH.h" #include "Wm4Eigen.h" namespace Wm4 { //---------------------------------------------------------------------------- template Eigen::Eigen (int iSize) : m_kMat(iSize,iSize) { assert(iSize >= 2); m_iSize = iSize; m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; m_bIsRotation = false; } //---------------------------------------------------------------------------- template Eigen::Eigen (const Matrix2& rkM) : m_kMat(2,2,(const Real*)rkM) { m_iSize = 2; m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; m_bIsRotation = false; } //---------------------------------------------------------------------------- template Eigen::Eigen (const Matrix3& rkM) : m_kMat(3,3,(const Real*)rkM) { m_iSize = 3; m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; m_bIsRotation = false; } //---------------------------------------------------------------------------- template Eigen::Eigen (const GMatrix& rkM) : m_kMat(rkM) { m_iSize = rkM.GetRows(); assert(m_iSize >= 2 && (rkM.GetColumns() == m_iSize)); m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; m_bIsRotation = false; } //---------------------------------------------------------------------------- template Eigen::~Eigen () { WM4_DELETE[] m_afSubd; WM4_DELETE[] m_afDiag; } //---------------------------------------------------------------------------- template Real& Eigen::operator() (int iRow, int iCol) { return m_kMat[iRow][iCol]; } //---------------------------------------------------------------------------- template Eigen& Eigen::operator= (const Matrix2& rkM) { m_kMat.SetMatrix(2,2,(const Real*)rkM); m_iSize = 2; WM4_DELETE[] m_afDiag; WM4_DELETE[] m_afSubd; m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; return *this; } //---------------------------------------------------------------------------- template Eigen& Eigen::operator= (const Matrix3& rkM) { m_kMat.SetMatrix(3,3,(const Real*)rkM); m_iSize = 3; WM4_DELETE[] m_afDiag; WM4_DELETE[] m_afSubd; m_afDiag = WM4_NEW Real[m_iSize]; m_afSubd = WM4_NEW Real[m_iSize]; return *this; } //---------------------------------------------------------------------------- template Eigen& Eigen::operator= (const GMatrix& rkM) { m_kMat = rkM; return *this; } //---------------------------------------------------------------------------- template Real Eigen::GetEigenvalue (int i) const { return m_afDiag[i]; } //---------------------------------------------------------------------------- template const Real* Eigen::GetEigenvalues () const { return m_afDiag; } //---------------------------------------------------------------------------- template void Eigen::GetEigenvector (int i, Vector2& rkV) const { assert(m_iSize == 2); if (m_iSize == 2) { for (int iRow = 0; iRow < m_iSize; iRow++) { rkV[iRow] = m_kMat[iRow][i]; } } else { rkV = Vector2::ZERO; } } //---------------------------------------------------------------------------- template void Eigen::GetEigenvector (int i, Vector3& rkV) const { assert(m_iSize == 3); if (m_iSize == 3) { for (int iRow = 0; iRow < m_iSize; iRow++) { rkV[iRow] = m_kMat[iRow][i]; } } else { rkV = Vector3::ZERO; } } //---------------------------------------------------------------------------- template GVector Eigen::GetEigenvector (int i) const { return m_kMat.GetColumn(i); } //---------------------------------------------------------------------------- template const GMatrix& Eigen::GetEigenvectors () const { return m_kMat; } //---------------------------------------------------------------------------- template void Eigen::Tridiagonal2 () { // matrix is already tridiagonal m_afDiag[0] = m_kMat[0][0]; m_afDiag[1] = m_kMat[1][1]; m_afSubd[0] = m_kMat[0][1]; m_afSubd[1] = (Real)0.0; m_kMat[0][0] = (Real)1.0; m_kMat[0][1] = (Real)0.0; m_kMat[1][0] = (Real)0.0; m_kMat[1][1] = (Real)1.0; m_bIsRotation = true; } //---------------------------------------------------------------------------- template void Eigen::Tridiagonal3 () { Real fM00 = m_kMat[0][0]; Real fM01 = m_kMat[0][1]; Real fM02 = m_kMat[0][2]; Real fM11 = m_kMat[1][1]; Real fM12 = m_kMat[1][2]; Real fM22 = m_kMat[2][2]; m_afDiag[0] = fM00; m_afSubd[2] = (Real)0.0; if (Math::FAbs(fM02) > Math::ZERO_TOLERANCE) { Real fLength = Math::Sqrt(fM01*fM01+fM02*fM02); Real fInvLength = ((Real)1.0)/fLength; fM01 *= fInvLength; fM02 *= fInvLength; Real fQ = ((Real)2.0)*fM01*fM12+fM02*(fM22-fM11); m_afDiag[1] = fM11+fM02*fQ; m_afDiag[2] = fM22-fM02*fQ; m_afSubd[0] = fLength; m_afSubd[1] = fM12-fM01*fQ; m_kMat[0][0] = (Real)1.0; m_kMat[0][1] = (Real)0.0; m_kMat[0][2] = (Real)0.0; m_kMat[1][0] = (Real)0.0; m_kMat[1][1] = fM01; m_kMat[1][2] = fM02; m_kMat[2][0] = (Real)0.0; m_kMat[2][1] = fM02; m_kMat[2][2] = -fM01; m_bIsRotation = false; } else { m_afDiag[1] = fM11; m_afDiag[2] = fM22; m_afSubd[0] = fM01; m_afSubd[1] = fM12; m_kMat[0][0] = (Real)1.0; m_kMat[0][1] = (Real)0.0; m_kMat[0][2] = (Real)0.0; m_kMat[1][0] = (Real)0.0; m_kMat[1][1] = (Real)1.0; m_kMat[1][2] = (Real)0.0; m_kMat[2][0] = (Real)0.0; m_kMat[2][1] = (Real)0.0; m_kMat[2][2] = (Real)1.0; m_bIsRotation = true; } } //---------------------------------------------------------------------------- template void Eigen::TridiagonalN () { int i0, i1, i2, i3; for (i0 = m_iSize-1, i3 = m_iSize-2; i0 >= 1; i0--, i3--) { Real fH = (Real)0.0, fScale = (Real)0.0; if (i3 > 0) { for (i2 = 0; i2 <= i3; i2++) { fScale += Math::FAbs(m_kMat[i0][i2]); } if (fScale == (Real)0.0) { m_afSubd[i0] = m_kMat[i0][i3]; } else { Real fInvScale = ((Real)1.0)/fScale; for (i2 = 0; i2 <= i3; i2++) { m_kMat[i0][i2] *= fInvScale; fH += m_kMat[i0][i2]*m_kMat[i0][i2]; } Real fF = m_kMat[i0][i3]; Real fG = Math::Sqrt(fH); if (fF > (Real)0.0) { fG = -fG; } m_afSubd[i0] = fScale*fG; fH -= fF*fG; m_kMat[i0][i3] = fF-fG; fF = (Real)0.0; Real fInvH = ((Real)1.0)/fH; for (i1 = 0; i1 <= i3; i1++) { m_kMat[i1][i0] = m_kMat[i0][i1]*fInvH; fG = (Real)0.0; for (i2 = 0; i2 <= i1; i2++) { fG += m_kMat[i1][i2]*m_kMat[i0][i2]; } for (i2 = i1+1; i2 <= i3; i2++) { fG += m_kMat[i2][i1]*m_kMat[i0][i2]; } m_afSubd[i1] = fG*fInvH; fF += m_afSubd[i1]*m_kMat[i0][i1]; } Real fHalfFdivH = ((Real)0.5)*fF*fInvH; for (i1 = 0; i1 <= i3; i1++) { fF = m_kMat[i0][i1]; fG = m_afSubd[i1] - fHalfFdivH*fF; m_afSubd[i1] = fG; for (i2 = 0; i2 <= i1; i2++) { m_kMat[i1][i2] -= fF*m_afSubd[i2] + fG*m_kMat[i0][i2]; } } } } else { m_afSubd[i0] = m_kMat[i0][i3]; } m_afDiag[i0] = fH; } m_afDiag[0] = (Real)0.0; m_afSubd[0] = (Real)0.0; for (i0 = 0, i3 = -1; i0 <= m_iSize-1; i0++, i3++) { if (m_afDiag[i0] != (Real)0.0) { for (i1 = 0; i1 <= i3; i1++) { Real fSum = (Real)0.0; for (i2 = 0; i2 <= i3; i2++) { fSum += m_kMat[i0][i2]*m_kMat[i2][i1]; } for (i2 = 0; i2 <= i3; i2++) { m_kMat[i2][i1] -= fSum*m_kMat[i2][i0]; } } } m_afDiag[i0] = m_kMat[i0][i0]; m_kMat[i0][i0] = (Real)1.0; for (i1 = 0; i1 <= i3; i1++) { m_kMat[i1][i0] = (Real)0.0; m_kMat[i0][i1] = (Real)0.0; } } // re-ordering if Eigen::QLAlgorithm is used subsequently for (i0 = 1, i3 = 0; i0 < m_iSize; i0++, i3++) { m_afSubd[i3] = m_afSubd[i0]; } m_afSubd[m_iSize-1] = (Real)0.0; m_bIsRotation = ((m_iSize % 2) == 0); } //---------------------------------------------------------------------------- template bool Eigen::QLAlgorithm () { const int iMaxIter = 32; for (int i0 = 0; i0 < m_iSize; i0++) { int i1; for (i1 = 0; i1 < iMaxIter; i1++) { int i2; for (i2 = i0; i2 <= m_iSize-2; i2++) { Real fTmp = Math::FAbs(m_afDiag[i2]) + Math::FAbs(m_afDiag[i2+1]); if (Math::FAbs(m_afSubd[i2]) + fTmp == fTmp) { break; } } if (i2 == i0) { break; } Real fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Real)2.0) * m_afSubd[i0]); Real fR = Math::Sqrt(fG*fG+(Real)1.0); if ( fG < (Real)0.0 ) { fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR); } else { fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR); } Real fSin = (Real)1.0, fCos = (Real)1.0, fP = (Real)0.0; for (int i3 = i2-1; i3 >= i0; i3--) { Real fF = fSin*m_afSubd[i3]; Real fB = fCos*m_afSubd[i3]; if (Math::FAbs(fF) >= Math::FAbs(fG)) { fCos = fG/fF; fR = Math::Sqrt(fCos*fCos+(Real)1.0); m_afSubd[i3+1] = fF*fR; fSin = ((Real)1.0)/fR; fCos *= fSin; } else { fSin = fF/fG; fR = Math::Sqrt(fSin*fSin+(Real)1.0); m_afSubd[i3+1] = fG*fR; fCos = ((Real)1.0)/fR; fSin *= fCos; } fG = m_afDiag[i3+1]-fP; fR = (m_afDiag[i3]-fG)*fSin+((Real)2.0)*fB*fCos; fP = fSin*fR; m_afDiag[i3+1] = fG+fP; fG = fCos*fR-fB; for (int i4 = 0; i4 < m_iSize; i4++) { fF = m_kMat[i4][i3+1]; m_kMat[i4][i3+1] = fSin*m_kMat[i4][i3]+fCos*fF; m_kMat[i4][i3] = fCos*m_kMat[i4][i3]-fSin*fF; } } m_afDiag[i0] -= fP; m_afSubd[i0] = fG; m_afSubd[i2] = (Real)0.0; } if (i1 == iMaxIter) { return false; } } return true; } //---------------------------------------------------------------------------- template void Eigen::DecreasingSort () { // sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1] for (int i0 = 0, i1; i0 <= m_iSize-2; i0++) { // locate maximum eigenvalue i1 = i0; Real fMax = m_afDiag[i1]; int i2; for (i2 = i0+1; i2 < m_iSize; i2++) { if (m_afDiag[i2] > fMax) { i1 = i2; fMax = m_afDiag[i1]; } } if (i1 != i0) { // swap eigenvalues m_afDiag[i1] = m_afDiag[i0]; m_afDiag[i0] = fMax; // swap eigenvectors for (i2 = 0; i2 < m_iSize; i2++) { Real fTmp = m_kMat[i2][i0]; m_kMat[i2][i0] = m_kMat[i2][i1]; m_kMat[i2][i1] = fTmp; m_bIsRotation = !m_bIsRotation; } } } } //---------------------------------------------------------------------------- template void Eigen::IncreasingSort () { // sort eigenvalues in increasing order, e[0] <= ... <= e[iSize-1] for (int i0 = 0, i1; i0 <= m_iSize-2; i0++) { // locate minimum eigenvalue i1 = i0; Real fMin = m_afDiag[i1]; int i2; for (i2 = i0+1; i2 < m_iSize; i2++) { if (m_afDiag[i2] < fMin) { i1 = i2; fMin = m_afDiag[i1]; } } if (i1 != i0) { // swap eigenvalues m_afDiag[i1] = m_afDiag[i0]; m_afDiag[i0] = fMin; // swap eigenvectors for (i2 = 0; i2 < m_iSize; i2++) { Real fTmp = m_kMat[i2][i0]; m_kMat[i2][i0] = m_kMat[i2][i1]; m_kMat[i2][i1] = fTmp; m_bIsRotation = !m_bIsRotation; } } } } //---------------------------------------------------------------------------- template void Eigen::GuaranteeRotation () { if (!m_bIsRotation) { // change sign on the first column for (int iRow = 0; iRow < m_iSize; iRow++) { m_kMat[iRow][0] = -m_kMat[iRow][0]; } } } //---------------------------------------------------------------------------- template void Eigen::EigenStuff2 () { Tridiagonal2(); QLAlgorithm(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::EigenStuff3 () { Tridiagonal3(); QLAlgorithm(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::EigenStuffN () { TridiagonalN(); QLAlgorithm(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::EigenStuff () { switch (m_iSize) { case 2: Tridiagonal2(); break; case 3: Tridiagonal3(); break; default: TridiagonalN(); break; } QLAlgorithm(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::DecrSortEigenStuff2 () { Tridiagonal2(); QLAlgorithm(); DecreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::DecrSortEigenStuff3 () { Tridiagonal3(); QLAlgorithm(); DecreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::DecrSortEigenStuffN () { TridiagonalN(); QLAlgorithm(); DecreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::DecrSortEigenStuff () { switch (m_iSize) { case 2: Tridiagonal2(); break; case 3: Tridiagonal3(); break; default: TridiagonalN(); break; } QLAlgorithm(); DecreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::IncrSortEigenStuff2 () { Tridiagonal2(); QLAlgorithm(); IncreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::IncrSortEigenStuff3 () { Tridiagonal3(); QLAlgorithm(); IncreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::IncrSortEigenStuffN () { TridiagonalN(); QLAlgorithm(); IncreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- template void Eigen::IncrSortEigenStuff () { switch (m_iSize) { case 2: Tridiagonal2(); break; case 3: Tridiagonal3(); break; default: TridiagonalN(); break; } QLAlgorithm(); IncreasingSort(); GuaranteeRotation(); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // explicit instantiation //---------------------------------------------------------------------------- template WM4_FOUNDATION_ITEM class Eigen; template WM4_FOUNDATION_ITEM class Eigen; //---------------------------------------------------------------------------- }