404 lines
13 KiB
C
404 lines
13 KiB
C
|
/**
|
|||
|
\file Matrix3.h
|
|||
|
|
|||
|
3x3 matrix class
|
|||
|
|
|||
|
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
|||
|
|
|||
|
\cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
|
|||
|
|
|||
|
\created 2001-06-02
|
|||
|
\edited 2011-05-05
|
|||
|
*/
|
|||
|
|
|||
|
#ifndef G3D_Matrix3_h
|
|||
|
#define G3D_Matrix3_h
|
|||
|
|
|||
|
#include "G3D/platform.h"
|
|||
|
#include "G3D/Vector3.h"
|
|||
|
#include "G3D/Vector4.h"
|
|||
|
#include "G3D/debugAssert.h"
|
|||
|
|
|||
|
#include <cstring>
|
|||
|
|
|||
|
namespace G3D {
|
|||
|
|
|||
|
#ifdef _MSC_VER
|
|||
|
// Turn off "conditional expression is constant" warning; MSVC generates this
|
|||
|
// for debug assertions in inlined methods.
|
|||
|
# pragma warning (disable : 4127)
|
|||
|
#endif
|
|||
|
|
|||
|
class Any;
|
|||
|
|
|||
|
/**
|
|||
|
A 3x3 matrix. Do not subclass. Data is unitializd when default constructed.
|
|||
|
*/
|
|||
|
class Matrix3 {
|
|||
|
private:
|
|||
|
|
|||
|
// Row, column
|
|||
|
float elt[3][3];
|
|||
|
|
|||
|
// Hidden operators
|
|||
|
bool operator<(const Matrix3&) const;
|
|||
|
bool operator>(const Matrix3&) const;
|
|||
|
bool operator<=(const Matrix3&) const;
|
|||
|
bool operator>=(const Matrix3&) const;
|
|||
|
|
|||
|
public:
|
|||
|
|
|||
|
/** Must be in one of the following forms:
|
|||
|
- Matrix3(#, #, # .... #)
|
|||
|
- Matrix3::fromAxisAngle(#, #)
|
|||
|
- Matrix3::diagonal(#, #, #)
|
|||
|
- Matrix3::identity()
|
|||
|
*/
|
|||
|
Matrix3(const Any& any);
|
|||
|
|
|||
|
static Matrix3 fromColumns(const Vector3& c0, const Vector3& c1, const Vector3& c2) {
|
|||
|
Matrix3 m;
|
|||
|
for (int r = 0; r < 3; ++r) {
|
|||
|
m.elt[r][0] = c0[r];
|
|||
|
m.elt[r][1] = c1[r];
|
|||
|
m.elt[r][2] = c2[r];
|
|||
|
}
|
|||
|
return m;
|
|||
|
}
|
|||
|
|
|||
|
static Matrix3 fromRows(const Vector3& r0, const Vector3& r1, const Vector3& r2) {
|
|||
|
Matrix3 m;
|
|||
|
for (int c = 0; c < 3; ++c) {
|
|||
|
m.elt[0][c] = r0[c];
|
|||
|
m.elt[1][c] = r1[c];
|
|||
|
m.elt[2][c] = r2[c];
|
|||
|
}
|
|||
|
return m;
|
|||
|
}
|
|||
|
|
|||
|
Any toAny() const;
|
|||
|
|
|||
|
/** Initial values are undefined for performance.
|
|||
|
\sa Matrix3::zero, Matrix3::identity, Matrix3::fromAxisAngle, etc.*/
|
|||
|
Matrix3() {}
|
|||
|
|
|||
|
Matrix3 (class BinaryInput& b);
|
|||
|
Matrix3 (const float aafEntry[3][3]);
|
|||
|
Matrix3 (const Matrix3& rkMatrix);
|
|||
|
Matrix3 (float fEntry00, float fEntry01, float fEntry02,
|
|||
|
float fEntry10, float fEntry11, float fEntry12,
|
|||
|
float fEntry20, float fEntry21, float fEntry22);
|
|||
|
|
|||
|
bool fuzzyEq(const Matrix3& b) const;
|
|||
|
|
|||
|
/** Constructs a matrix from a quaternion.
|
|||
|
@cite Graphics Gems II, p. 351--354
|
|||
|
@cite Implementation from Watt and Watt, pg 362*/
|
|||
|
Matrix3(const class Quat& q);
|
|||
|
|
|||
|
static Matrix3 diagonal(float e00, float e11, float e22) {
|
|||
|
return Matrix3(e00, 0, 0,
|
|||
|
0, e11, 0,
|
|||
|
0, 0, e22);
|
|||
|
}
|
|||
|
|
|||
|
void serialize(class BinaryOutput& b) const;
|
|||
|
void deserialize(class BinaryInput& b);
|
|||
|
|
|||
|
/** Returns true if column(0).cross(column(1)).dot(column(2)) > 0. */
|
|||
|
bool isRightHanded() const;
|
|||
|
|
|||
|
/**
|
|||
|
Sets all elements.
|
|||
|
*/
|
|||
|
void set(float fEntry00, float fEntry01, float fEntry02,
|
|||
|
float fEntry10, float fEntry11, float fEntry12,
|
|||
|
float fEntry20, float fEntry21, float fEntry22);
|
|||
|
|
|||
|
/**
|
|||
|
Member access, allows use of construct mat[r][c]
|
|||
|
*/
|
|||
|
inline float* operator[] (int iRow) {
|
|||
|
debugAssert(iRow >= 0);
|
|||
|
debugAssert(iRow < 3);
|
|||
|
return (float*)&elt[iRow][0];
|
|||
|
}
|
|||
|
|
|||
|
inline const float* operator[] (int iRow) const {
|
|||
|
debugAssert(iRow >= 0);
|
|||
|
debugAssert(iRow < 3);
|
|||
|
return (const float*)&elt[iRow][0];
|
|||
|
}
|
|||
|
|
|||
|
inline operator float* () {
|
|||
|
return (float*)&elt[0][0];
|
|||
|
}
|
|||
|
|
|||
|
inline operator const float* () const{
|
|||
|
return (const float*)&elt[0][0];
|
|||
|
}
|
|||
|
|
|||
|
Vector3 column(int c) const;
|
|||
|
const Vector3& row(int r) const;
|
|||
|
|
|||
|
void setColumn(int iCol, const Vector3 &vector);
|
|||
|
void setRow(int iRow, const Vector3 &vector);
|
|||
|
|
|||
|
// assignment and comparison
|
|||
|
inline Matrix3& operator= (const Matrix3& rkMatrix) {
|
|||
|
memcpy(elt, rkMatrix.elt, 9 * sizeof(float));
|
|||
|
return *this;
|
|||
|
}
|
|||
|
|
|||
|
bool operator== (const Matrix3& rkMatrix) const;
|
|||
|
bool operator!= (const Matrix3& rkMatrix) const;
|
|||
|
|
|||
|
// arithmetic operations
|
|||
|
Matrix3 operator+ (const Matrix3& rkMatrix) const;
|
|||
|
Matrix3 operator- (const Matrix3& rkMatrix) const;
|
|||
|
/** Matrix-matrix multiply */
|
|||
|
Matrix3 operator* (const Matrix3& rkMatrix) const;
|
|||
|
Matrix3 operator- () const;
|
|||
|
|
|||
|
Matrix3& operator+= (const Matrix3& rkMatrix);
|
|||
|
Matrix3& operator-= (const Matrix3& rkMatrix);
|
|||
|
Matrix3& operator*= (const Matrix3& rkMatrix);
|
|||
|
|
|||
|
/**
|
|||
|
* matrix * vector [3x3 * 3x1 = 3x1]
|
|||
|
*/
|
|||
|
inline Vector3 operator* (const Vector3& v) const {
|
|||
|
Vector3 kProd;
|
|||
|
|
|||
|
for (int r = 0; r < 3; ++r) {
|
|||
|
kProd[r] =
|
|||
|
elt[r][0] * v[0] +
|
|||
|
elt[r][1] * v[1] +
|
|||
|
elt[r][2] * v[2];
|
|||
|
}
|
|||
|
|
|||
|
return kProd;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* vector * matrix [1x3 * 3x3 = 1x3]
|
|||
|
*/
|
|||
|
friend Vector3 operator* (const Vector3& rkVector,
|
|||
|
const Matrix3& rkMatrix);
|
|||
|
|
|||
|
/**
|
|||
|
* matrix * scalar
|
|||
|
*/
|
|||
|
Matrix3 operator* (float fScalar) const;
|
|||
|
|
|||
|
/** scalar * matrix */
|
|||
|
friend Matrix3 operator* (double fScalar, const Matrix3& rkMatrix);
|
|||
|
friend Matrix3 operator* (float fScalar, const Matrix3& rkMatrix);
|
|||
|
friend Matrix3 operator* (int fScalar, const Matrix3& rkMatrix);
|
|||
|
|
|||
|
Matrix3& operator*= (float k);
|
|||
|
Matrix3& operator/= (float k);
|
|||
|
|
|||
|
|
|||
|
private:
|
|||
|
/** Multiplication where out != A and out != B */
|
|||
|
static void _mul(const Matrix3& A, const Matrix3& B, Matrix3& out);
|
|||
|
public:
|
|||
|
|
|||
|
/** Optimized implementation of out = A * B. It is safe (but slow) to call
|
|||
|
with A, B, and out possibly pointer equal to one another.*/
|
|||
|
// This is a static method so that it is not ambiguous whether "this"
|
|||
|
// is an input or output argument.
|
|||
|
inline static void mul(const Matrix3& A, const Matrix3& B, Matrix3& out) {
|
|||
|
if ((&out == &A) || (&out == &B)) {
|
|||
|
// We need a temporary anyway, so revert to the stack method.
|
|||
|
out = A * B;
|
|||
|
} else {
|
|||
|
// Optimized in-place multiplication.
|
|||
|
_mul(A, B, out);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private:
|
|||
|
static void _transpose(const Matrix3& A, Matrix3& out);
|
|||
|
public:
|
|||
|
|
|||
|
/** Optimized implementation of out = A.transpose(). It is safe (but slow) to call
|
|||
|
with A and out possibly pointer equal to one another.
|
|||
|
|
|||
|
Note that <CODE>A.transpose() * v</CODE> can be computed
|
|||
|
more efficiently as <CODE>v * A</CODE>.
|
|||
|
*/
|
|||
|
inline static void transpose(const Matrix3& A, Matrix3& out) {
|
|||
|
if (&A == &out) {
|
|||
|
out = A.transpose();
|
|||
|
} else {
|
|||
|
_transpose(A, out);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/** Returns true if the rows and column L2 norms are 1.0 and the rows are orthogonal. */
|
|||
|
bool isOrthonormal() const;
|
|||
|
|
|||
|
Matrix3 transpose () const;
|
|||
|
bool inverse (Matrix3& rkInverse, float fTolerance = 1e-06f) const;
|
|||
|
Matrix3 inverse (float fTolerance = 1e-06f) const;
|
|||
|
float determinant () const;
|
|||
|
|
|||
|
/** singular value decomposition */
|
|||
|
void singularValueDecomposition (Matrix3& rkL, Vector3& rkS,
|
|||
|
Matrix3& rkR) const;
|
|||
|
/** singular value decomposition */
|
|||
|
void singularValueComposition (const Matrix3& rkL,
|
|||
|
const Vector3& rkS, const Matrix3& rkR);
|
|||
|
|
|||
|
/** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
|
|||
|
void orthonormalize();
|
|||
|
|
|||
|
/** orthogonal Q, diagonal D, upper triangular U stored as (u01,u02,u12) */
|
|||
|
void qDUDecomposition (Matrix3& rkQ, Vector3& rkD,
|
|||
|
Vector3& rkU) const;
|
|||
|
|
|||
|
/**
|
|||
|
Polar decomposition of a matrix. Based on pseudocode from Nicholas J
|
|||
|
Higham, "Computing the Polar Decomposition -- with Applications Siam
|
|||
|
Journal of Science and Statistical Computing, Vol 7, No. 4, October
|
|||
|
1986.
|
|||
|
|
|||
|
Decomposes A into R*S, where R is orthogonal and S is symmetric.
|
|||
|
|
|||
|
Ken Shoemake's "Matrix animation and polar decomposition"
|
|||
|
in Proceedings of the conference on Graphics interface '92
|
|||
|
seems to be better known in the world of graphics, but Higham's version
|
|||
|
uses a scaling constant that can lead to faster convergence than
|
|||
|
Shoemake's when the initial matrix is far from orthogonal.
|
|||
|
*/
|
|||
|
void polarDecomposition(Matrix3 &R, Matrix3 &S) const;
|
|||
|
|
|||
|
/**
|
|||
|
* Matrix norms.
|
|||
|
*/
|
|||
|
float spectralNorm () const;
|
|||
|
|
|||
|
float squaredFrobeniusNorm() const;
|
|||
|
|
|||
|
float frobeniusNorm() const;
|
|||
|
|
|||
|
float l1Norm() const;
|
|||
|
|
|||
|
float lInfNorm() const;
|
|||
|
|
|||
|
float diffOneNorm(const Matrix3 &y) const;
|
|||
|
|
|||
|
/** matrix must be orthonormal */
|
|||
|
void toAxisAngle(Vector3& rkAxis, float& rfRadians) const;
|
|||
|
|
|||
|
static Matrix3 fromDiagonal(const Vector3& d) {
|
|||
|
return Matrix3(d.x, 0, 0,
|
|||
|
0, d.y, 0,
|
|||
|
0, 0, d.z);
|
|||
|
}
|
|||
|
|
|||
|
/** \sa fromUnitAxisAngle */
|
|||
|
static Matrix3 fromAxisAngle(const Vector3& rkAxis, float fRadians);
|
|||
|
|
|||
|
/** Assumes that rkAxis has unit length */
|
|||
|
static Matrix3 fromUnitAxisAngle(const Vector3& rkAxis, float fRadians);
|
|||
|
|
|||
|
/**
|
|||
|
* The matrix must be orthonormal. The decomposition is yaw*pitch*roll
|
|||
|
* where yaw is rotation about the Up vector, pitch is rotation about the
|
|||
|
* right axis, and roll is rotation about the Direction axis.
|
|||
|
*/
|
|||
|
bool toEulerAnglesXYZ (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
bool toEulerAnglesXZY (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
bool toEulerAnglesYXZ (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
bool toEulerAnglesYZX (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
bool toEulerAnglesZXY (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
bool toEulerAnglesZYX (float& rfYAngle, float& rfPAngle,
|
|||
|
float& rfRAngle) const;
|
|||
|
static Matrix3 fromEulerAnglesXYZ (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
static Matrix3 fromEulerAnglesXZY (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
static Matrix3 fromEulerAnglesYXZ (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
static Matrix3 fromEulerAnglesYZX (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
static Matrix3 fromEulerAnglesZXY (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
static Matrix3 fromEulerAnglesZYX (float fYAngle, float fPAngle, float fRAngle);
|
|||
|
|
|||
|
/** eigensolver, matrix must be symmetric */
|
|||
|
void eigenSolveSymmetric (float afEigenvalue[3],
|
|||
|
Vector3 akEigenvector[3]) const;
|
|||
|
|
|||
|
static void tensorProduct (const Vector3& rkU, const Vector3& rkV,
|
|||
|
Matrix3& rkProduct);
|
|||
|
std::string toString() const;
|
|||
|
|
|||
|
static const float EPSILON;
|
|||
|
|
|||
|
// Special values.
|
|||
|
// The unguaranteed order of initialization of static variables across
|
|||
|
// translation units can be a source of annoying bugs, so now the static
|
|||
|
// special values (like Vector3::ZERO, Color3::WHITE, ...) are wrapped
|
|||
|
// inside static functions that return references to them.
|
|||
|
// These functions are intentionally not inlined, because:
|
|||
|
// "You might be tempted to write [...] them as inline functions
|
|||
|
// inside their respective header files, but this is something you
|
|||
|
// must definitely not do. An inline function can be duplicated
|
|||
|
// in every file in which it appears <20><><EFBFBD><EFBFBD> and this duplication
|
|||
|
// includes the static object definition. Because inline functions
|
|||
|
// automatically default to internal linkage, this would result in
|
|||
|
// having multiple static objects across the various translation
|
|||
|
// units, which would certainly cause problems. So you must
|
|||
|
// ensure that there is only one definition of each wrapping
|
|||
|
// function, and this means not making the wrapping functions inline",
|
|||
|
// according to Chapter 10 of "Thinking in C++, 2nd ed. Volume 1" by Bruce Eckel,
|
|||
|
// http://www.mindview.net/
|
|||
|
static const Matrix3& zero();
|
|||
|
static const Matrix3& identity();
|
|||
|
|
|||
|
protected:
|
|||
|
|
|||
|
// support for eigensolver
|
|||
|
void tridiagonal (float afDiag[3], float afSubDiag[3]);
|
|||
|
bool qLAlgorithm (float afDiag[3], float afSubDiag[3]);
|
|||
|
|
|||
|
// support for singular value decomposition
|
|||
|
static const float ms_fSvdEpsilon;
|
|||
|
static const int ms_iSvdMaxIterations;
|
|||
|
static void bidiagonalize (Matrix3& kA, Matrix3& kL,
|
|||
|
Matrix3& kR);
|
|||
|
static void golubKahanStep (Matrix3& kA, Matrix3& kL,
|
|||
|
Matrix3& kR);
|
|||
|
|
|||
|
// support for spectral norm
|
|||
|
static float maxCubicRoot (float afCoeff[3]);
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
/** <code>v * M == M.transpose() * v</code> */
|
|||
|
inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) {
|
|||
|
Vector3 kProd;
|
|||
|
|
|||
|
for (int r = 0; r < 3; ++r) {
|
|||
|
kProd[r] =
|
|||
|
rkPoint[0] * rkMatrix.elt[0][r] +
|
|||
|
rkPoint[1] * rkMatrix.elt[1][r] +
|
|||
|
rkPoint[2] * rkMatrix.elt[2][r];
|
|||
|
}
|
|||
|
|
|||
|
return kProd;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} // namespace
|
|||
|
|
|||
|
#endif
|
|||
|
|