michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_3DMATRIX_H michael@0: #define GFX_3DMATRIX_H michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: struct gfxMatrix; michael@0: michael@0: /** michael@0: * This class represents a 3D transformation. The matrix is laid michael@0: * out as follows: michael@0: * michael@0: * _11 _12 _13 _14 michael@0: * _21 _22 _23 _24 michael@0: * _31 _32 _33 _34 michael@0: * _41 _42 _43 _44 michael@0: * michael@0: * This matrix is treated as row-major. Assuming we consider our vectors row michael@0: * vectors, this matrix type will be identical in memory to the OpenGL and D3D michael@0: * matrices. OpenGL matrices are column-major, however OpenGL also treats michael@0: * vectors as column vectors, the double transposition makes everything work michael@0: * out nicely. michael@0: */ michael@0: class gfx3DMatrix michael@0: { michael@0: public: michael@0: /** michael@0: * Create matrix. michael@0: */ michael@0: gfx3DMatrix(void); michael@0: michael@0: /** michael@0: * Matrix multiplication. michael@0: */ michael@0: gfx3DMatrix operator*(const gfx3DMatrix &aMatrix) const; michael@0: gfx3DMatrix& operator*=(const gfx3DMatrix &aMatrix); michael@0: michael@0: gfxPointH3D& operator[](int aIndex) michael@0: { michael@0: NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); michael@0: return *reinterpret_cast((&_11)+4*aIndex); michael@0: } michael@0: const gfxPointH3D& operator[](int aIndex) const michael@0: { michael@0: NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); michael@0: return *reinterpret_cast((&_11)+4*aIndex); michael@0: } michael@0: michael@0: /** michael@0: * Return true if this matrix and |aMatrix| are the same matrix. michael@0: */ michael@0: bool operator==(const gfx3DMatrix& aMatrix) const; michael@0: bool operator!=(const gfx3DMatrix& aMatrix) const; michael@0: michael@0: bool FuzzyEqual(const gfx3DMatrix& aMatrix) const; michael@0: michael@0: /** michael@0: * Divide all values in the matrix by a scalar value michael@0: */ michael@0: gfx3DMatrix& operator/=(gfxFloat scalar); michael@0: michael@0: /** michael@0: * Create a 3D matrix from a gfxMatrix 2D affine transformation. michael@0: * michael@0: * \param aMatrix gfxMatrix 2D affine transformation. michael@0: */ michael@0: static gfx3DMatrix From2D(const gfxMatrix &aMatrix); michael@0: michael@0: /** michael@0: * Returns true if the matrix is isomorphic to a 2D affine transformation michael@0: * (i.e. as obtained by From2D). If it is, optionally returns the 2D michael@0: * matrix in aMatrix. michael@0: */ michael@0: bool Is2D(gfxMatrix* aMatrix) const; michael@0: bool Is2D() const; michael@0: michael@0: /** michael@0: * Returns true if the matrix can be reduced to a 2D affine transformation michael@0: * (i.e. as obtained by From2D). If it is, optionally returns the 2D michael@0: * matrix in aMatrix. This should only be used on matrices required for michael@0: * rendering, not for intermediate calculations. It is assumed that the 2D michael@0: * matrix will only be used for transforming objects on to the z=0 plane, michael@0: * therefore any z-component perspective is ignored. This means that if michael@0: * aMatrix is applied to objects with z != 0, the results may be incorrect. michael@0: * michael@0: * Since drawing is to a 2d plane, any 3d transform without perspective michael@0: * can be reduced by dropping the z row and column. michael@0: */ michael@0: bool CanDraw2D(gfxMatrix* aMatrix = nullptr) const; michael@0: michael@0: /** michael@0: * Converts the matrix to one that doesn't modify the z coordinate of points, michael@0: * but leaves the rest of the transformation unchanged. michael@0: */ michael@0: gfx3DMatrix& ProjectTo2D(); michael@0: michael@0: /** michael@0: * Returns true if the matrix is the identity matrix. The most important michael@0: * property we require is that gfx3DMatrix().IsIdentity() returns true. michael@0: */ michael@0: bool IsIdentity() const; michael@0: michael@0: /** michael@0: * Pre-multiplication transformation functions: michael@0: * michael@0: * These functions construct a temporary matrix containing michael@0: * a single transformation and pre-multiply it onto the current michael@0: * matrix. michael@0: */ michael@0: michael@0: /** michael@0: * Add a translation by aPoint to the matrix. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | 1 0 0 0 | michael@0: * | 0 1 0 0 | michael@0: * | 0 0 1 0 | michael@0: * | aPoint.x aPoint.y aPoint.z 1 | michael@0: */ michael@0: void Translate(const gfxPoint3D& aPoint); michael@0: michael@0: /** michael@0: * Skew the matrix. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | 1 tan(aYSkew) 0 0 | michael@0: * | tan(aXSkew) 1 0 0 | michael@0: * | 0 0 1 0 | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void SkewXY(double aXSkew, double aYSkew); michael@0: michael@0: void SkewXY(double aSkew); michael@0: void SkewXZ(double aSkew); michael@0: void SkewYZ(double aSkew); michael@0: michael@0: /** michael@0: * Scale the matrix michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | aX 0 0 0 | michael@0: * | 0 aY 0 0 | michael@0: * | 0 0 aZ 0 | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void Scale(float aX, float aY, float aZ); michael@0: michael@0: /** michael@0: * Return the currently set scaling factors. michael@0: */ michael@0: float GetXScale() const { return _11; } michael@0: float GetYScale() const { return _22; } michael@0: float GetZScale() const { return _33; } michael@0: michael@0: /** michael@0: * Rotate around the X axis.. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | 1 0 0 0 | michael@0: * | 0 cos(aTheta) sin(aTheta) 0 | michael@0: * | 0 -sin(aTheta) cos(aTheta) 0 | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void RotateX(double aTheta); michael@0: michael@0: /** michael@0: * Rotate around the Y axis.. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | cos(aTheta) 0 -sin(aTheta) 0 | michael@0: * | 0 1 0 0 | michael@0: * | sin(aTheta) 0 cos(aTheta) 0 | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void RotateY(double aTheta); michael@0: michael@0: /** michael@0: * Rotate around the Z axis.. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | cos(aTheta) sin(aTheta) 0 0 | michael@0: * | -sin(aTheta) cos(aTheta) 0 0 | michael@0: * | 0 0 1 0 | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void RotateZ(double aTheta); michael@0: michael@0: /** michael@0: * Apply perspective to the matrix. michael@0: * michael@0: * This creates this temporary matrix: michael@0: * | 1 0 0 0 | michael@0: * | 0 1 0 0 | michael@0: * | 0 0 1 -1/aDepth | michael@0: * | 0 0 0 1 | michael@0: */ michael@0: void Perspective(float aDepth); michael@0: michael@0: /** michael@0: * Pre multiply an existing matrix onto the current michael@0: * matrix michael@0: */ michael@0: void PreMultiply(const gfx3DMatrix& aOther); michael@0: void PreMultiply(const gfxMatrix& aOther); michael@0: michael@0: /** michael@0: * Post-multiplication transformation functions: michael@0: * michael@0: * These functions construct a temporary matrix containing michael@0: * a single transformation and post-multiply it onto the current michael@0: * matrix. michael@0: */ michael@0: michael@0: /** michael@0: * Add a translation by aPoint after the matrix. michael@0: * This is functionally equivalent to: michael@0: * matrix * gfx3DMatrix::Translation(aPoint) michael@0: */ michael@0: void TranslatePost(const gfxPoint3D& aPoint); michael@0: michael@0: void ScalePost(float aX, float aY, float aZ); michael@0: michael@0: /** michael@0: * Transforms a point according to this matrix. michael@0: */ michael@0: gfxPoint Transform(const gfxPoint& point) const; michael@0: michael@0: /** michael@0: * Transforms a rectangle according to this matrix michael@0: */ michael@0: gfxRect TransformBounds(const gfxRect& rect) const; michael@0: michael@0: michael@0: gfxQuad TransformRect(const gfxRect& aRect) const; michael@0: michael@0: /** michael@0: * Transforms a 3D vector according to this matrix. michael@0: */ michael@0: gfxPoint3D Transform3D(const gfxPoint3D& point) const; michael@0: gfxPointH3D Transform4D(const gfxPointH3D& aPoint) const; michael@0: gfxPointH3D TransposeTransform4D(const gfxPointH3D& aPoint) const; michael@0: michael@0: gfxPoint ProjectPoint(const gfxPoint& aPoint) const; michael@0: gfxRect ProjectRectBounds(const gfxRect& aRect) const; michael@0: michael@0: /** michael@0: * Transforms a point by the inverse of this matrix. In the case of perspective transforms, some screen michael@0: * points have no equivalent in the untransformed plane (if they exist past the vanishing point). To michael@0: * avoid this, we need to specify the bounds of the untransformed plane to restrict the search area. michael@0: * michael@0: * @param aPoint Point to untransform. michael@0: * @param aChildBounds Bounds of the untransformed plane. michael@0: * @param aOut Untransformed point. michael@0: * @return Returns true if a point was found within a ChildBounds, false otherwise. michael@0: */ michael@0: bool UntransformPoint(const gfxPoint& aPoint, const gfxRect& aChildBounds, gfxPoint* aOut) const; michael@0: michael@0: michael@0: /** michael@0: * Same as UntransformPoint, but untransforms a rect and returns the bounding rect of the result. michael@0: * Returns an empty rect if the result doesn't intersect aChildBounds. michael@0: */ michael@0: gfxRect UntransformBounds(const gfxRect& aRect, const gfxRect& aChildBounds) const; michael@0: michael@0: michael@0: /** michael@0: * Inverts this matrix, if possible. Otherwise, the matrix is left michael@0: * unchanged. michael@0: */ michael@0: gfx3DMatrix Inverse() const; michael@0: michael@0: gfx3DMatrix& Invert() michael@0: { michael@0: *this = Inverse(); michael@0: return *this; michael@0: } michael@0: michael@0: gfx3DMatrix& Normalize(); michael@0: michael@0: gfxPointH3D TransposedVector(int aIndex) const michael@0: { michael@0: NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); michael@0: return gfxPointH3D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex)); michael@0: } michael@0: michael@0: void SetTransposedVector(int aIndex, gfxPointH3D &aVector) michael@0: { michael@0: NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); michael@0: *((&_11)+aIndex) = aVector.x; michael@0: *((&_21)+aIndex) = aVector.y; michael@0: *((&_31)+aIndex) = aVector.z; michael@0: *((&_41)+aIndex) = aVector.w; michael@0: } michael@0: michael@0: gfx3DMatrix& Transpose(); michael@0: gfx3DMatrix Transposed() const; michael@0: michael@0: /** michael@0: * Returns a unit vector that is perpendicular to the plane formed michael@0: * by transform the screen plane (z=0) by this matrix. michael@0: */ michael@0: gfxPoint3D GetNormalVector() const; michael@0: michael@0: /** michael@0: * Returns true if a plane transformed by this matrix will michael@0: * have it's back face visible. michael@0: */ michael@0: bool IsBackfaceVisible() const; michael@0: michael@0: /** michael@0: * Check if matrix is singular (no inverse exists). michael@0: */ michael@0: bool IsSingular() const; michael@0: michael@0: /** michael@0: * Create a translation matrix. michael@0: * michael@0: * \param aX Translation on X-axis. michael@0: * \param aY Translation on Y-axis. michael@0: * \param aZ Translation on Z-axis. michael@0: */ michael@0: static gfx3DMatrix Translation(float aX, float aY, float aZ); michael@0: static gfx3DMatrix Translation(const gfxPoint3D& aPoint); michael@0: michael@0: /** michael@0: * Create a scale matrix. Scales uniformly along all axes. michael@0: * michael@0: * \param aScale Scale factor michael@0: */ michael@0: static gfx3DMatrix ScalingMatrix(float aFactor); michael@0: michael@0: /** michael@0: * Create a scale matrix. michael@0: */ michael@0: static gfx3DMatrix ScalingMatrix(float aX, float aY, float aZ); michael@0: michael@0: gfxFloat Determinant() const; michael@0: michael@0: void NudgeToIntegers(void); michael@0: michael@0: private: michael@0: michael@0: gfxFloat Determinant3x3() const; michael@0: gfx3DMatrix Inverse3x3() const; michael@0: michael@0: gfx3DMatrix Multiply2D(const gfx3DMatrix &aMatrix) const; michael@0: michael@0: public: michael@0: michael@0: /** Matrix elements */ michael@0: float _11, _12, _13, _14; michael@0: float _21, _22, _23, _24; michael@0: float _31, _32, _33, _34; michael@0: float _41, _42, _43, _44; michael@0: }; michael@0: michael@0: #endif /* GFX_3DMATRIX_H */