/** @file Matrix3.h 3x3 matrix class @maintainer Morgan McGuire, matrix@graphics3d.com @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com @created 2001-06-02 @edited 2006-04-05 */ #ifndef G3D_MATRIX3_H #define G3D_MATRIX3_H #include "G3D/platform.h" #include "G3D/System.h" #include "G3D/Vector3.h" #include "G3D/Vector4.h" namespace G3D { /** 3x3 matrix. Do not subclass. */ class Matrix3 { private: 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: /** Initial values are undefined for performance. See also Matrix3::zero(), Matrix3::identity(), Matrix3::fromAxisAngle, etc.*/ inline Matrix3() {} 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); /** 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 getColumn (int iCol) const; Vector3 getRow (int iRow) const; void setColumn(int iCol, const Vector3 &vector); void setRow(int iRow, const Vector3 &vector); // assignment and comparison inline Matrix3& operator= (const Matrix3& rkMatrix) { System::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); 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 A.transpose() * v can be computed more efficiently as v * A. */ 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-06) const; Matrix3 inverse (float fTolerance = 1e-06) 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; float spectralNorm () const; /** matrix must be orthonormal */ void toAxisAngle(Vector3& rkAxis, float& rfRadians) const; static Matrix3 fromAxisAngle(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 – 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(); // Deprecated. /** @deprecated Use Matrix3::zero() */ static const Matrix3 ZERO; /** @deprecated Use Matrix3::identity() */ 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]); }; //---------------------------------------------------------------------------- /** v * M == M.transpose() * v */ 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