| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 |