merged tag ooo/OOO330_m14
[LibreOffice.git] / basegfx / source / matrix / b2dhommatrix.cxx
blob96d3bdb01c01c4b122979545ad0da8403dda5a5c
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <osl/diagnose.h>
31 #include <rtl/instance.hxx>
32 #include <basegfx/matrix/b2dhommatrix.hxx>
33 #include <hommatrixtemplate.hxx>
34 #include <basegfx/tuple/b2dtuple.hxx>
35 #include <basegfx/vector/b2dvector.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 ///////////////////////////////////////////////////////////////////////////////
40 namespace basegfx
42 class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
46 namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
47 IdentityMatrix > {}; }
49 B2DHomMatrix::B2DHomMatrix() :
50 mpImpl( IdentityMatrix::get() ) // use common identity matrix
54 B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
55 mpImpl(rMat.mpImpl)
59 B2DHomMatrix::~B2DHomMatrix()
63 B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
64 : mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call
66 mpImpl->set(0, 0, f_0x0);
67 mpImpl->set(0, 1, f_0x1);
68 mpImpl->set(0, 2, f_0x2);
69 mpImpl->set(1, 0, f_1x0);
70 mpImpl->set(1, 1, f_1x1);
71 mpImpl->set(1, 2, f_1x2);
74 B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
76 mpImpl = rMat.mpImpl;
77 return *this;
80 void B2DHomMatrix::makeUnique()
82 mpImpl.make_unique();
85 double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
87 return mpImpl->get(nRow, nColumn);
90 void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
92 mpImpl->set(nRow, nColumn, fValue);
95 void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
97 mpImpl->set(0, 0, f_0x0);
98 mpImpl->set(0, 1, f_0x1);
99 mpImpl->set(0, 2, f_0x2);
100 mpImpl->set(1, 0, f_1x0);
101 mpImpl->set(1, 1, f_1x1);
102 mpImpl->set(1, 2, f_1x2);
105 bool B2DHomMatrix::isLastLineDefault() const
107 return mpImpl->isLastLineDefault();
110 bool B2DHomMatrix::isIdentity() const
112 if(mpImpl.same_object(IdentityMatrix::get()))
113 return true;
115 return mpImpl->isIdentity();
118 void B2DHomMatrix::identity()
120 mpImpl = IdentityMatrix::get();
123 bool B2DHomMatrix::isInvertible() const
125 return mpImpl->isInvertible();
128 bool B2DHomMatrix::invert()
130 Impl2DHomMatrix aWork(*mpImpl);
131 sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
132 sal_Int16 nParity;
134 if(aWork.ludcmp(pIndex, nParity))
136 mpImpl->doInvert(aWork, pIndex);
137 delete[] pIndex;
139 return true;
142 delete[] pIndex;
143 return false;
146 bool B2DHomMatrix::isNormalized() const
148 return mpImpl->isNormalized();
151 void B2DHomMatrix::normalize()
153 if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
154 mpImpl->doNormalize();
157 double B2DHomMatrix::determinant() const
159 return mpImpl->doDeterminant();
162 double B2DHomMatrix::trace() const
164 return mpImpl->doTrace();
167 void B2DHomMatrix::transpose()
169 mpImpl->doTranspose();
172 B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
174 mpImpl->doAddMatrix(*rMat.mpImpl);
175 return *this;
178 B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
180 mpImpl->doSubMatrix(*rMat.mpImpl);
181 return *this;
184 B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
186 const double fOne(1.0);
188 if(!fTools::equal(fOne, fValue))
189 mpImpl->doMulMatrix(fValue);
191 return *this;
194 B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
196 const double fOne(1.0);
198 if(!fTools::equal(fOne, fValue))
199 mpImpl->doMulMatrix(1.0 / fValue);
201 return *this;
204 B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
206 if(!rMat.isIdentity())
207 mpImpl->doMulMatrix(*rMat.mpImpl);
209 return *this;
212 bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
214 if(mpImpl.same_object(rMat.mpImpl))
215 return true;
217 return mpImpl->isEqual(*rMat.mpImpl);
220 bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
222 return !(*this == rMat);
225 void B2DHomMatrix::rotate(double fRadiant)
227 if(!fTools::equalZero(fRadiant))
229 double fSin(0.0);
230 double fCos(1.0);
232 tools::createSinCosOrthogonal(fSin, fCos, fRadiant);
233 Impl2DHomMatrix aRotMat;
235 aRotMat.set(0, 0, fCos);
236 aRotMat.set(1, 1, fCos);
237 aRotMat.set(1, 0, fSin);
238 aRotMat.set(0, 1, -fSin);
240 mpImpl->doMulMatrix(aRotMat);
244 void B2DHomMatrix::translate(double fX, double fY)
246 if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
248 Impl2DHomMatrix aTransMat;
250 aTransMat.set(0, 2, fX);
251 aTransMat.set(1, 2, fY);
253 mpImpl->doMulMatrix(aTransMat);
257 void B2DHomMatrix::scale(double fX, double fY)
259 const double fOne(1.0);
261 if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
263 Impl2DHomMatrix aScaleMat;
265 aScaleMat.set(0, 0, fX);
266 aScaleMat.set(1, 1, fY);
268 mpImpl->doMulMatrix(aScaleMat);
272 void B2DHomMatrix::shearX(double fSx)
274 // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
275 if(!fTools::equalZero(fSx))
277 Impl2DHomMatrix aShearXMat;
279 aShearXMat.set(0, 1, fSx);
281 mpImpl->doMulMatrix(aShearXMat);
285 void B2DHomMatrix::shearY(double fSy)
287 // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
288 if(!fTools::equalZero(fSy))
290 Impl2DHomMatrix aShearYMat;
292 aShearYMat.set(1, 0, fSy);
294 mpImpl->doMulMatrix(aShearYMat);
298 /** Decomposition
300 New, optimized version with local shearX detection. Old version (keeping
301 below, is working well, too) used the 3D matrix decomposition when
302 shear was used. Keeping old version as comment below since it may get
303 necessary to add the determinant() test from there here, too.
305 bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
307 // when perspective is used, decompose is not made here
308 if(!mpImpl->isLastLineDefault())
310 return false;
313 // reset rotate and shear and copy translation values in every case
314 rRotate = rShearX = 0.0;
315 rTranslate.setX(get(0, 2));
316 rTranslate.setY(get(1, 2));
318 // test for rotation and shear
319 if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
321 // no rotation and shear, copy scale values
322 rScale.setX(get(0, 0));
323 rScale.setY(get(1, 1));
325 else
327 // get the unit vectors of the transformation -> the perpendicular vectors
328 B2DVector aUnitVecX(get(0, 0), get(1, 0));
329 B2DVector aUnitVecY(get(0, 1), get(1, 1));
330 const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
332 // Test if shear is zero. That's the case if the unit vectors in the matrix
333 // are perpendicular -> scalar is zero. This is also the case when one of
334 // the unit vectors is zero.
335 if(fTools::equalZero(fScalarXY))
337 // calculate unsigned scale values
338 rScale.setX(aUnitVecX.getLength());
339 rScale.setY(aUnitVecY.getLength());
341 // check unit vectors for zero lengths
342 const bool bXIsZero(fTools::equalZero(rScale.getX()));
343 const bool bYIsZero(fTools::equalZero(rScale.getY()));
345 if(bXIsZero || bYIsZero)
347 // still extract as much as possible. Scalings are already set
348 if(!bXIsZero)
350 // get rotation of X-Axis
351 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
353 else if(!bYIsZero)
355 // get rotation of X-Axis. When assuming X and Y perpendicular
356 // and correct rotation, it's the Y-Axis rotation minus 90 degrees
357 rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
360 // one or both unit vectors do not extist, determinant is zero, no decomposition possible.
361 // Eventually used rotations or shears are lost
362 return false;
364 else
366 // no shear
367 // calculate rotation of X unit vector relative to (1, 0)
368 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
370 // use orientation to evtl. correct sign of Y-Scale
371 const double fCrossXY(aUnitVecX.cross(aUnitVecY));
373 if(fCrossXY < 0.0)
375 rScale.setY(-rScale.getY());
379 else
381 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
382 // shear, extract it
383 double fCrossXY(aUnitVecX.cross(aUnitVecY));
385 // get rotation by calculating angle of X unit vector relative to (1, 0).
386 // This is before the parallell test following the motto to extract
387 // as much as possible
388 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
390 // get unsigned scale value for X. It will not change and is useful
391 // for further corrections
392 rScale.setX(aUnitVecX.getLength());
394 if(fTools::equalZero(fCrossXY))
396 // extract as much as possible
397 rScale.setY(aUnitVecY.getLength());
399 // unit vectors are parallel, thus not linear independent. No
400 // useful decomposition possible. This should not happen since
401 // the only way to get the unit vectors nearly parallell is
402 // a very big shearing. Anyways, be prepared for hand-filled
403 // matrices
404 // Eventually used rotations or shears are lost
405 return false;
407 else
409 // calculate the contained shear
410 rShearX = fScalarXY / fCrossXY;
412 if(!fTools::equalZero(rRotate))
414 // To be able to correct the shear for aUnitVecY, rotation needs to be
415 // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
416 aUnitVecX.setX(rScale.getX());
417 aUnitVecX.setY(0.0);
419 // for Y correction we rotate the UnitVecY back about -rRotate
420 const double fNegRotate(-rRotate);
421 const double fSin(sin(fNegRotate));
422 const double fCos(cos(fNegRotate));
424 const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
425 const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
427 aUnitVecY.setX(fNewX);
428 aUnitVecY.setY(fNewY);
431 // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
432 // Shear correction can only work with removed rotation
433 aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
434 fCrossXY = aUnitVecX.cross(aUnitVecY);
436 // calculate unsigned scale value for Y, after the corrections since
437 // the shear correction WILL change the length of aUnitVecY
438 rScale.setY(aUnitVecY.getLength());
440 // use orientation to set sign of Y-Scale
441 if(fCrossXY < 0.0)
443 rScale.setY(-rScale.getY());
449 return true;
451 } // end of namespace basegfx
453 ///////////////////////////////////////////////////////////////////////////////
454 // eof