diff options
Diffstat (limited to 'basegfx/source/matrix/b2dhommatrix.cxx')
-rw-r--r-- | basegfx/source/matrix/b2dhommatrix.cxx | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/basegfx/source/matrix/b2dhommatrix.cxx b/basegfx/source/matrix/b2dhommatrix.cxx new file mode 100644 index 000000000000..96d3bdb01c01 --- /dev/null +++ b/basegfx/source/matrix/b2dhommatrix.cxx @@ -0,0 +1,454 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +#include <osl/diagnose.h> +#include <rtl/instance.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <hommatrixtemplate.hxx> +#include <basegfx/tuple/b2dtuple.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +/////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 > + { + }; + + namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType, + IdentityMatrix > {}; } + + B2DHomMatrix::B2DHomMatrix() : + mpImpl( IdentityMatrix::get() ) // use common identity matrix + { + } + + B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) : + mpImpl(rMat.mpImpl) + { + } + + B2DHomMatrix::~B2DHomMatrix() + { + } + + B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2) + : mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call + { + mpImpl->set(0, 0, f_0x0); + mpImpl->set(0, 1, f_0x1); + mpImpl->set(0, 2, f_0x2); + mpImpl->set(1, 0, f_1x0); + mpImpl->set(1, 1, f_1x1); + mpImpl->set(1, 2, f_1x2); + } + + B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat) + { + mpImpl = rMat.mpImpl; + return *this; + } + + void B2DHomMatrix::makeUnique() + { + mpImpl.make_unique(); + } + + double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const + { + return mpImpl->get(nRow, nColumn); + } + + void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue) + { + mpImpl->set(nRow, nColumn, fValue); + } + + void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2) + { + mpImpl->set(0, 0, f_0x0); + mpImpl->set(0, 1, f_0x1); + mpImpl->set(0, 2, f_0x2); + mpImpl->set(1, 0, f_1x0); + mpImpl->set(1, 1, f_1x1); + mpImpl->set(1, 2, f_1x2); + } + + bool B2DHomMatrix::isLastLineDefault() const + { + return mpImpl->isLastLineDefault(); + } + + bool B2DHomMatrix::isIdentity() const + { + if(mpImpl.same_object(IdentityMatrix::get())) + return true; + + return mpImpl->isIdentity(); + } + + void B2DHomMatrix::identity() + { + mpImpl = IdentityMatrix::get(); + } + + bool B2DHomMatrix::isInvertible() const + { + return mpImpl->isInvertible(); + } + + bool B2DHomMatrix::invert() + { + Impl2DHomMatrix aWork(*mpImpl); + sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()]; + sal_Int16 nParity; + + if(aWork.ludcmp(pIndex, nParity)) + { + mpImpl->doInvert(aWork, pIndex); + delete[] pIndex; + + return true; + } + + delete[] pIndex; + return false; + } + + bool B2DHomMatrix::isNormalized() const + { + return mpImpl->isNormalized(); + } + + void B2DHomMatrix::normalize() + { + if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized()) + mpImpl->doNormalize(); + } + + double B2DHomMatrix::determinant() const + { + return mpImpl->doDeterminant(); + } + + double B2DHomMatrix::trace() const + { + return mpImpl->doTrace(); + } + + void B2DHomMatrix::transpose() + { + mpImpl->doTranspose(); + } + + B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat) + { + mpImpl->doAddMatrix(*rMat.mpImpl); + return *this; + } + + B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat) + { + mpImpl->doSubMatrix(*rMat.mpImpl); + return *this; + } + + B2DHomMatrix& B2DHomMatrix::operator*=(double fValue) + { + const double fOne(1.0); + + if(!fTools::equal(fOne, fValue)) + mpImpl->doMulMatrix(fValue); + + return *this; + } + + B2DHomMatrix& B2DHomMatrix::operator/=(double fValue) + { + const double fOne(1.0); + + if(!fTools::equal(fOne, fValue)) + mpImpl->doMulMatrix(1.0 / fValue); + + return *this; + } + + B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat) + { + if(!rMat.isIdentity()) + mpImpl->doMulMatrix(*rMat.mpImpl); + + return *this; + } + + bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const + { + if(mpImpl.same_object(rMat.mpImpl)) + return true; + + return mpImpl->isEqual(*rMat.mpImpl); + } + + bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const + { + return !(*this == rMat); + } + + void B2DHomMatrix::rotate(double fRadiant) + { + if(!fTools::equalZero(fRadiant)) + { + double fSin(0.0); + double fCos(1.0); + + tools::createSinCosOrthogonal(fSin, fCos, fRadiant); + Impl2DHomMatrix aRotMat; + + aRotMat.set(0, 0, fCos); + aRotMat.set(1, 1, fCos); + aRotMat.set(1, 0, fSin); + aRotMat.set(0, 1, -fSin); + + mpImpl->doMulMatrix(aRotMat); + } + } + + void B2DHomMatrix::translate(double fX, double fY) + { + if(!fTools::equalZero(fX) || !fTools::equalZero(fY)) + { + Impl2DHomMatrix aTransMat; + + aTransMat.set(0, 2, fX); + aTransMat.set(1, 2, fY); + + mpImpl->doMulMatrix(aTransMat); + } + } + + void B2DHomMatrix::scale(double fX, double fY) + { + const double fOne(1.0); + + if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY)) + { + Impl2DHomMatrix aScaleMat; + + aScaleMat.set(0, 0, fX); + aScaleMat.set(1, 1, fY); + + mpImpl->doMulMatrix(aScaleMat); + } + } + + void B2DHomMatrix::shearX(double fSx) + { + // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!) + if(!fTools::equalZero(fSx)) + { + Impl2DHomMatrix aShearXMat; + + aShearXMat.set(0, 1, fSx); + + mpImpl->doMulMatrix(aShearXMat); + } + } + + void B2DHomMatrix::shearY(double fSy) + { + // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!) + if(!fTools::equalZero(fSy)) + { + Impl2DHomMatrix aShearYMat; + + aShearYMat.set(1, 0, fSy); + + mpImpl->doMulMatrix(aShearYMat); + } + } + + /** Decomposition + + New, optimized version with local shearX detection. Old version (keeping + below, is working well, too) used the 3D matrix decomposition when + shear was used. Keeping old version as comment below since it may get + necessary to add the determinant() test from there here, too. + */ + bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const + { + // when perspective is used, decompose is not made here + if(!mpImpl->isLastLineDefault()) + { + return false; + } + + // reset rotate and shear and copy translation values in every case + rRotate = rShearX = 0.0; + rTranslate.setX(get(0, 2)); + rTranslate.setY(get(1, 2)); + + // test for rotation and shear + if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0))) + { + // no rotation and shear, copy scale values + rScale.setX(get(0, 0)); + rScale.setY(get(1, 1)); + } + else + { + // get the unit vectors of the transformation -> the perpendicular vectors + B2DVector aUnitVecX(get(0, 0), get(1, 0)); + B2DVector aUnitVecY(get(0, 1), get(1, 1)); + const double fScalarXY(aUnitVecX.scalar(aUnitVecY)); + + // Test if shear is zero. That's the case if the unit vectors in the matrix + // are perpendicular -> scalar is zero. This is also the case when one of + // the unit vectors is zero. + if(fTools::equalZero(fScalarXY)) + { + // calculate unsigned scale values + rScale.setX(aUnitVecX.getLength()); + rScale.setY(aUnitVecY.getLength()); + + // check unit vectors for zero lengths + const bool bXIsZero(fTools::equalZero(rScale.getX())); + const bool bYIsZero(fTools::equalZero(rScale.getY())); + + if(bXIsZero || bYIsZero) + { + // still extract as much as possible. Scalings are already set + if(!bXIsZero) + { + // get rotation of X-Axis + rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX()); + } + else if(!bYIsZero) + { + // get rotation of X-Axis. When assuming X and Y perpendicular + // and correct rotation, it's the Y-Axis rotation minus 90 degrees + rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2; + } + + // one or both unit vectors do not extist, determinant is zero, no decomposition possible. + // Eventually used rotations or shears are lost + return false; + } + else + { + // no shear + // calculate rotation of X unit vector relative to (1, 0) + rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX()); + + // use orientation to evtl. correct sign of Y-Scale + const double fCrossXY(aUnitVecX.cross(aUnitVecY)); + + if(fCrossXY < 0.0) + { + rScale.setY(-rScale.getY()); + } + } + } + else + { + // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here + // shear, extract it + double fCrossXY(aUnitVecX.cross(aUnitVecY)); + + // get rotation by calculating angle of X unit vector relative to (1, 0). + // This is before the parallell test following the motto to extract + // as much as possible + rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX()); + + // get unsigned scale value for X. It will not change and is useful + // for further corrections + rScale.setX(aUnitVecX.getLength()); + + if(fTools::equalZero(fCrossXY)) + { + // extract as much as possible + rScale.setY(aUnitVecY.getLength()); + + // unit vectors are parallel, thus not linear independent. No + // useful decomposition possible. This should not happen since + // the only way to get the unit vectors nearly parallell is + // a very big shearing. Anyways, be prepared for hand-filled + // matrices + // Eventually used rotations or shears are lost + return false; + } + else + { + // calculate the contained shear + rShearX = fScalarXY / fCrossXY; + + if(!fTools::equalZero(rRotate)) + { + // To be able to correct the shear for aUnitVecY, rotation needs to be + // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0). + aUnitVecX.setX(rScale.getX()); + aUnitVecX.setY(0.0); + + // for Y correction we rotate the UnitVecY back about -rRotate + const double fNegRotate(-rRotate); + const double fSin(sin(fNegRotate)); + const double fCos(cos(fNegRotate)); + + const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin); + const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos); + + aUnitVecY.setX(fNewX); + aUnitVecY.setY(fNewY); + } + + // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed. + // Shear correction can only work with removed rotation + aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX)); + fCrossXY = aUnitVecX.cross(aUnitVecY); + + // calculate unsigned scale value for Y, after the corrections since + // the shear correction WILL change the length of aUnitVecY + rScale.setY(aUnitVecY.getLength()); + + // use orientation to set sign of Y-Scale + if(fCrossXY < 0.0) + { + rScale.setY(-rScale.getY()); + } + } + } + } + + return true; + } +} // end of namespace basegfx + +/////////////////////////////////////////////////////////////////////////////// +// eof |