Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / basegfx / source / matrix / b2dhommatrix.cxx
blob61178c4960116eb44c11ce0e0614719d7583ef91
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())
153 // multiply with identity, no change -> nothing to do
155 else if(isIdentity())
157 // we are identity, result will be rMat -> assign
158 *this = rMat;
160 else
162 // multiply
163 mpImpl->doMulMatrix(*rMat.mpImpl);
166 return *this;
169 bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
171 if(mpImpl.same_object(rMat.mpImpl))
172 return true;
174 return mpImpl->isEqual(*rMat.mpImpl);
177 bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
179 return !(*this == rMat);
182 void B2DHomMatrix::rotate(double fRadiant)
184 if(!fTools::equalZero(fRadiant))
186 double fSin(0.0);
187 double fCos(1.0);
189 utils::createSinCosOrthogonal(fSin, fCos, fRadiant);
190 Impl2DHomMatrix aRotMat;
192 aRotMat.set(0, 0, fCos);
193 aRotMat.set(1, 1, fCos);
194 aRotMat.set(1, 0, fSin);
195 aRotMat.set(0, 1, -fSin);
197 mpImpl->doMulMatrix(aRotMat);
201 void B2DHomMatrix::translate(double fX, double fY)
203 if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
205 Impl2DHomMatrix aTransMat;
207 aTransMat.set(0, 2, fX);
208 aTransMat.set(1, 2, fY);
210 mpImpl->doMulMatrix(aTransMat);
214 void B2DHomMatrix::translate(const B2DTuple& rTuple)
216 translate(rTuple.getX(), rTuple.getY());
219 void B2DHomMatrix::scale(double fX, double fY)
221 const double fOne(1.0);
223 if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
225 Impl2DHomMatrix aScaleMat;
227 aScaleMat.set(0, 0, fX);
228 aScaleMat.set(1, 1, fY);
230 mpImpl->doMulMatrix(aScaleMat);
234 void B2DHomMatrix::scale(const B2DTuple& rTuple)
236 scale(rTuple.getX(), rTuple.getY());
239 void B2DHomMatrix::shearX(double fSx)
241 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
242 if(!fTools::equalZero(fSx))
244 Impl2DHomMatrix aShearXMat;
246 aShearXMat.set(0, 1, fSx);
248 mpImpl->doMulMatrix(aShearXMat);
252 void B2DHomMatrix::shearY(double fSy)
254 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
255 if(!fTools::equalZero(fSy))
257 Impl2DHomMatrix aShearYMat;
259 aShearYMat.set(1, 0, fSy);
261 mpImpl->doMulMatrix(aShearYMat);
265 /** Decomposition
267 New, optimized version with local shearX detection. Old version (keeping
268 below, is working well, too) used the 3D matrix decomposition when
269 shear was used. Keeping old version as comment below since it may get
270 necessary to add the determinant() test from there here, too.
272 bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
274 // when perspective is used, decompose is not made here
275 if(!mpImpl->isLastLineDefault())
277 return false;
280 // reset rotate and shear and copy translation values in every case
281 rRotate = rShearX = 0.0;
282 rTranslate.setX(get(0, 2));
283 rTranslate.setY(get(1, 2));
285 // test for rotation and shear
286 if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
288 // no rotation and shear, copy scale values
289 rScale.setX(get(0, 0));
290 rScale.setY(get(1, 1));
292 // or is there?
293 if( rScale.getX() < 0 && rScale.getY() < 0 )
295 // there is - 180 degree rotated
296 rScale *= -1;
297 rRotate = M_PI;
300 else
302 // get the unit vectors of the transformation -> the perpendicular vectors
303 B2DVector aUnitVecX(get(0, 0), get(1, 0));
304 B2DVector aUnitVecY(get(0, 1), get(1, 1));
305 const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
307 // Test if shear is zero. That's the case if the unit vectors in the matrix
308 // are perpendicular -> scalar is zero. This is also the case when one of
309 // the unit vectors is zero.
310 if(fTools::equalZero(fScalarXY))
312 // calculate unsigned scale values
313 rScale.setX(aUnitVecX.getLength());
314 rScale.setY(aUnitVecY.getLength());
316 // check unit vectors for zero lengths
317 const bool bXIsZero(fTools::equalZero(rScale.getX()));
318 const bool bYIsZero(fTools::equalZero(rScale.getY()));
320 if(bXIsZero || bYIsZero)
322 // still extract as much as possible. Scalings are already set
323 if(!bXIsZero)
325 // get rotation of X-Axis
326 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
328 else if(!bYIsZero)
330 // get rotation of X-Axis. When assuming X and Y perpendicular
331 // and correct rotation, it's the Y-Axis rotation minus 90 degrees
332 rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
335 // one or both unit vectors do not exist, determinant is zero, no decomposition possible.
336 // Eventually used rotations or shears are lost
337 return false;
339 else
341 // no shear
342 // calculate rotation of X unit vector relative to (1, 0)
343 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
345 // use orientation to evtl. correct sign of Y-Scale
346 const double fCrossXY(aUnitVecX.cross(aUnitVecY));
348 if(fCrossXY < 0.0)
350 rScale.setY(-rScale.getY());
354 else
356 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
357 // shear, extract it
358 double fCrossXY(aUnitVecX.cross(aUnitVecY));
360 // get rotation by calculating angle of X unit vector relative to (1, 0).
361 // This is before the parallel test following the motto to extract
362 // as much as possible
363 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
365 // get unsigned scale value for X. It will not change and is useful
366 // for further corrections
367 rScale.setX(aUnitVecX.getLength());
369 if(fTools::equalZero(fCrossXY))
371 // extract as much as possible
372 rScale.setY(aUnitVecY.getLength());
374 // unit vectors are parallel, thus not linear independent. No
375 // useful decomposition possible. This should not happen since
376 // the only way to get the unit vectors nearly parallel is
377 // a very big shearing. Anyways, be prepared for hand-filled
378 // matrices
379 // Eventually used rotations or shears are lost
380 return false;
382 else
384 // calculate the contained shear
385 rShearX = fScalarXY / fCrossXY;
387 if(!fTools::equalZero(rRotate))
389 // To be able to correct the shear for aUnitVecY, rotation needs to be
390 // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
391 aUnitVecX.setX(rScale.getX());
392 aUnitVecX.setY(0.0);
394 // for Y correction we rotate the UnitVecY back about -rRotate
395 const double fNegRotate(-rRotate);
396 const double fSin(sin(fNegRotate));
397 const double fCos(cos(fNegRotate));
399 const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
400 const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
402 aUnitVecY.setX(fNewX);
403 aUnitVecY.setY(fNewY);
406 // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
407 // Shear correction can only work with removed rotation
408 aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
409 fCrossXY = aUnitVecX.cross(aUnitVecY);
411 // calculate unsigned scale value for Y, after the corrections since
412 // the shear correction WILL change the length of aUnitVecY
413 rScale.setY(aUnitVecY.getLength());
415 // use orientation to set sign of Y-Scale
416 if(fCrossXY < 0.0)
418 rScale.setY(-rScale.getY());
424 return true;
426 } // end of namespace basegfx
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */