Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / basegfx / source / matrix / b2dhommatrix.cxx
blob053ca89d02de9f02c96319a51bb234ca7b1385d0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basegfx/matrix/b2dhommatrix.hxx>
21 #include <hommatrixtemplate.hxx>
22 #include <basegfx/tuple/b2dtuple.hxx>
23 #include <basegfx/vector/b2dvector.hxx>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 #include <memory>
27 namespace basegfx
29 typedef ::basegfx::internal::ImplHomMatrixTemplate< 3 > Impl2DHomMatrix_Base;
30 class Impl2DHomMatrix : public Impl2DHomMatrix_Base
34 B2DHomMatrix::B2DHomMatrix() = default;
36 B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix&) = default;
38 B2DHomMatrix::B2DHomMatrix(B2DHomMatrix&&) = default;
40 B2DHomMatrix::~B2DHomMatrix() = default;
42 B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
43 : mpImpl() // identity
45 mpImpl->set(0, 0, f_0x0);
46 mpImpl->set(0, 1, f_0x1);
47 mpImpl->set(0, 2, f_0x2);
48 mpImpl->set(1, 0, f_1x0);
49 mpImpl->set(1, 1, f_1x1);
50 mpImpl->set(1, 2, f_1x2);
53 B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix&) = default;
55 B2DHomMatrix& B2DHomMatrix::operator=(B2DHomMatrix&&) = default;
57 double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
59 return mpImpl->get(nRow, nColumn);
62 void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
64 mpImpl->set(nRow, nColumn, fValue);
67 void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
69 mpImpl->set(0, 0, f_0x0);
70 mpImpl->set(0, 1, f_0x1);
71 mpImpl->set(0, 2, f_0x2);
72 mpImpl->set(1, 0, f_1x0);
73 mpImpl->set(1, 1, f_1x1);
74 mpImpl->set(1, 2, f_1x2);
77 bool B2DHomMatrix::isLastLineDefault() const
79 return mpImpl->isLastLineDefault();
82 bool B2DHomMatrix::isIdentity() const
84 return mpImpl->isIdentity();
87 void B2DHomMatrix::identity()
89 *mpImpl = Impl2DHomMatrix();
92 bool B2DHomMatrix::isInvertible() const
94 return mpImpl->isInvertible();
97 bool B2DHomMatrix::invert()
99 if(isIdentity())
101 return true;
104 Impl2DHomMatrix aWork(*mpImpl);
105 std::unique_ptr<sal_uInt16[]> pIndex( new sal_uInt16[Impl2DHomMatrix_Base::getEdgeLength()] );
106 sal_Int16 nParity;
108 if(aWork.ludcmp(pIndex.get(), nParity))
110 mpImpl->doInvert(aWork, pIndex.get());
111 return true;
114 return false;
117 B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
119 mpImpl->doAddMatrix(*rMat.mpImpl);
120 return *this;
123 B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
125 mpImpl->doSubMatrix(*rMat.mpImpl);
126 return *this;
129 B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
131 const double fOne(1.0);
133 if(!fTools::equal(fOne, fValue))
134 mpImpl->doMulMatrix(fValue);
136 return *this;
139 B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
141 const double fOne(1.0);
143 if(!fTools::equal(fOne, fValue))
144 mpImpl->doMulMatrix(1.0 / fValue);
146 return *this;
149 B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
151 if(!rMat.isIdentity())
152 mpImpl->doMulMatrix(*rMat.mpImpl);
154 return *this;
157 bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
159 if(mpImpl.same_object(rMat.mpImpl))
160 return true;
162 return mpImpl->isEqual(*rMat.mpImpl);
165 bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
167 return !(*this == rMat);
170 void B2DHomMatrix::rotate(double fRadiant)
172 if(!fTools::equalZero(fRadiant))
174 double fSin(0.0);
175 double fCos(1.0);
177 utils::createSinCosOrthogonal(fSin, fCos, fRadiant);
178 Impl2DHomMatrix aRotMat;
180 aRotMat.set(0, 0, fCos);
181 aRotMat.set(1, 1, fCos);
182 aRotMat.set(1, 0, fSin);
183 aRotMat.set(0, 1, -fSin);
185 mpImpl->doMulMatrix(aRotMat);
189 void B2DHomMatrix::translate(double fX, double fY)
191 if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
193 Impl2DHomMatrix aTransMat;
195 aTransMat.set(0, 2, fX);
196 aTransMat.set(1, 2, fY);
198 mpImpl->doMulMatrix(aTransMat);
202 void B2DHomMatrix::translate(const B2DTuple& rTuple)
204 translate(rTuple.getX(), rTuple.getY());
207 void B2DHomMatrix::scale(double fX, double fY)
209 const double fOne(1.0);
211 if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
213 Impl2DHomMatrix aScaleMat;
215 aScaleMat.set(0, 0, fX);
216 aScaleMat.set(1, 1, fY);
218 mpImpl->doMulMatrix(aScaleMat);
222 void B2DHomMatrix::scale(const B2DTuple& rTuple)
224 scale(rTuple.getX(), rTuple.getY());
227 void B2DHomMatrix::shearX(double fSx)
229 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
230 if(!fTools::equalZero(fSx))
232 Impl2DHomMatrix aShearXMat;
234 aShearXMat.set(0, 1, fSx);
236 mpImpl->doMulMatrix(aShearXMat);
240 void B2DHomMatrix::shearY(double fSy)
242 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
243 if(!fTools::equalZero(fSy))
245 Impl2DHomMatrix aShearYMat;
247 aShearYMat.set(1, 0, fSy);
249 mpImpl->doMulMatrix(aShearYMat);
253 /** Decomposition
255 New, optimized version with local shearX detection. Old version (keeping
256 below, is working well, too) used the 3D matrix decomposition when
257 shear was used. Keeping old version as comment below since it may get
258 necessary to add the determinant() test from there here, too.
260 bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
262 // when perspective is used, decompose is not made here
263 if(!mpImpl->isLastLineDefault())
265 return false;
268 // reset rotate and shear and copy translation values in every case
269 rRotate = rShearX = 0.0;
270 rTranslate.setX(get(0, 2));
271 rTranslate.setY(get(1, 2));
273 // test for rotation and shear
274 if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
276 // no rotation and shear, copy scale values
277 rScale.setX(get(0, 0));
278 rScale.setY(get(1, 1));
280 // or is there?
281 if( rScale.getX() < 0 && rScale.getY() < 0 )
283 // there is - 180 degree rotated
284 rScale *= -1;
285 rRotate = 180*F_PI180;
288 else
290 // get the unit vectors of the transformation -> the perpendicular vectors
291 B2DVector aUnitVecX(get(0, 0), get(1, 0));
292 B2DVector aUnitVecY(get(0, 1), get(1, 1));
293 const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
295 // Test if shear is zero. That's the case if the unit vectors in the matrix
296 // are perpendicular -> scalar is zero. This is also the case when one of
297 // the unit vectors is zero.
298 if(fTools::equalZero(fScalarXY))
300 // calculate unsigned scale values
301 rScale.setX(aUnitVecX.getLength());
302 rScale.setY(aUnitVecY.getLength());
304 // check unit vectors for zero lengths
305 const bool bXIsZero(fTools::equalZero(rScale.getX()));
306 const bool bYIsZero(fTools::equalZero(rScale.getY()));
308 if(bXIsZero || bYIsZero)
310 // still extract as much as possible. Scalings are already set
311 if(!bXIsZero)
313 // get rotation of X-Axis
314 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
316 else if(!bYIsZero)
318 // get rotation of X-Axis. When assuming X and Y perpendicular
319 // and correct rotation, it's the Y-Axis rotation minus 90 degrees
320 rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
323 // one or both unit vectors do not exist, determinant is zero, no decomposition possible.
324 // Eventually used rotations or shears are lost
325 return false;
327 else
329 // no shear
330 // calculate rotation of X unit vector relative to (1, 0)
331 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
333 // use orientation to evtl. correct sign of Y-Scale
334 const double fCrossXY(aUnitVecX.cross(aUnitVecY));
336 if(fCrossXY < 0.0)
338 rScale.setY(-rScale.getY());
342 else
344 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
345 // shear, extract it
346 double fCrossXY(aUnitVecX.cross(aUnitVecY));
348 // get rotation by calculating angle of X unit vector relative to (1, 0).
349 // This is before the parallel test following the motto to extract
350 // as much as possible
351 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
353 // get unsigned scale value for X. It will not change and is useful
354 // for further corrections
355 rScale.setX(aUnitVecX.getLength());
357 if(fTools::equalZero(fCrossXY))
359 // extract as much as possible
360 rScale.setY(aUnitVecY.getLength());
362 // unit vectors are parallel, thus not linear independent. No
363 // useful decomposition possible. This should not happen since
364 // the only way to get the unit vectors nearly parallel is
365 // a very big shearing. Anyways, be prepared for hand-filled
366 // matrices
367 // Eventually used rotations or shears are lost
368 return false;
370 else
372 // calculate the contained shear
373 rShearX = fScalarXY / fCrossXY;
375 if(!fTools::equalZero(rRotate))
377 // To be able to correct the shear for aUnitVecY, rotation needs to be
378 // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
379 aUnitVecX.setX(rScale.getX());
380 aUnitVecX.setY(0.0);
382 // for Y correction we rotate the UnitVecY back about -rRotate
383 const double fNegRotate(-rRotate);
384 const double fSin(sin(fNegRotate));
385 const double fCos(cos(fNegRotate));
387 const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
388 const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
390 aUnitVecY.setX(fNewX);
391 aUnitVecY.setY(fNewY);
394 // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
395 // Shear correction can only work with removed rotation
396 aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
397 fCrossXY = aUnitVecX.cross(aUnitVecY);
399 // calculate unsigned scale value for Y, after the corrections since
400 // the shear correction WILL change the length of aUnitVecY
401 rScale.setY(aUnitVecY.getLength());
403 // use orientation to set sign of Y-Scale
404 if(fCrossXY < 0.0)
406 rScale.setY(-rScale.getY());
412 return true;
414 } // end of namespace basegfx
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */