1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <basegfx/matrix/hommatrixtemplate.hxx>
22 #include <basegfx/tuple/b2dtuple.hxx>
23 #include <basegfx/vector/b2dvector.hxx>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 constexpr int RowSize
= 3;
31 void B2DHomMatrix::set3x2(double f_0x0
, double f_0x1
, double f_0x2
, double f_1x0
, double f_1x1
, double f_1x2
)
33 mfValues
[0][0] = f_0x0
;
34 mfValues
[0][1] = f_0x1
;
35 mfValues
[0][2] = f_0x2
;
36 mfValues
[1][0] = f_1x0
;
37 mfValues
[1][1] = f_1x1
;
38 mfValues
[1][2] = f_1x2
;
41 bool B2DHomMatrix::isIdentity() const
43 for(sal_uInt16
a(0); a
< RowSize
- 1; a
++)
45 for(sal_uInt16
b(0); b
< RowSize
; b
++)
47 const double fDefault(internal::implGetDefaultValue(a
, b
));
48 const double fValueAB(get(a
, b
));
50 if(!::basegfx::fTools::equal(fDefault
, fValueAB
))
60 void B2DHomMatrix::identity()
62 for(sal_uInt16
a(0); a
< RowSize
- 1; a
++)
64 for(sal_uInt16
b(0); b
< RowSize
; b
++)
65 mfValues
[a
][b
] = internal::implGetDefaultValue(a
, b
);
69 bool B2DHomMatrix::isInvertible() const
72 /* Compute adjoint: */
74 /* Compute determinant: */
75 double det
= computeDeterminant(dst
);
76 if (fTools::equalZero(det
))
81 bool B2DHomMatrix::invert()
88 /* Compute adjoint: */
91 /* Compute determinant: */
92 double det
= computeDeterminant(dst
);
93 if (fTools::equalZero(det
))
96 /* Multiply adjoint with reciprocal of determinant: */
98 mfValues
[0][0] = dst
[0] * det
;
99 mfValues
[0][1] = dst
[1] * det
;
100 mfValues
[0][2] = dst
[2] * det
;
101 mfValues
[1][0] = dst
[3] * det
;
102 mfValues
[1][1] = dst
[4] * det
;
103 mfValues
[1][2] = dst
[5] * det
;
108 /* Compute adjoint, optimised for the case where the last (not stored) row is { 0, 0, 1 } */
109 void B2DHomMatrix::computeAdjoint(double (&dst
)[6]) const
111 dst
[0] = + get(1, 1);
112 dst
[1] = - get(0, 1);
113 dst
[2] = + get(0, 1) * get(1, 2) - get(0, 2) * get(1, 1);
114 dst
[3] = - get(1, 0);
115 dst
[4] = + get(0, 0);
116 dst
[5] = - get(0, 0) * get(1, 2) + get(0, 2) * get(1, 0);
119 /* Compute the determinant, given the adjoint matrix */
120 double B2DHomMatrix::computeDeterminant(double (&dst
)[6]) const
122 return mfValues
[0][0] * dst
[0] + mfValues
[0][1] * dst
[3];
125 B2DHomMatrix
& B2DHomMatrix::operator*=(const B2DHomMatrix
& rMat
)
127 if(rMat
.isIdentity())
129 // multiply with identity, no change -> nothing to do
131 else if(isIdentity())
133 // we are identity, result will be rMat -> assign
145 void B2DHomMatrix::doMulMatrix(const B2DHomMatrix
& rMat
)
147 // create a copy as source for the original values
148 const B2DHomMatrix
aCopy(*this);
150 for(sal_uInt16
a(0); a
< 2; ++a
)
152 for(sal_uInt16
b(0); b
< 3; ++b
)
156 for(sal_uInt16
c(0); c
< 2; ++c
)
157 fValue
+= aCopy
.mfValues
[c
][b
] * rMat
.mfValues
[a
][c
];
159 mfValues
[a
][b
] = fValue
;
161 mfValues
[a
][2] += rMat
.mfValues
[a
][2];
165 bool B2DHomMatrix::operator==(const B2DHomMatrix
& rMat
) const
169 for(sal_uInt16
a(0); a
< 2; a
++)
171 for(sal_uInt16
b(0); b
< 3; b
++)
173 const double fValueA(mfValues
[a
][b
]);
174 const double fValueB(rMat
.mfValues
[a
][b
]);
176 if(!::basegfx::fTools::equal(fValueA
, fValueB
))
185 void B2DHomMatrix::rotate(double fRadiant
)
187 if(fTools::equalZero(fRadiant
))
193 utils::createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
194 B2DHomMatrix aRotMat
;
196 aRotMat
.set(0, 0, fCos
);
197 aRotMat
.set(1, 1, fCos
);
198 aRotMat
.set(1, 0, fSin
);
199 aRotMat
.set(0, 1, -fSin
);
201 doMulMatrix(aRotMat
);
204 void B2DHomMatrix::translate(double fX
, double fY
)
206 if(!fTools::equalZero(fX
) || !fTools::equalZero(fY
))
208 B2DHomMatrix aTransMat
;
210 aTransMat
.set(0, 2, fX
);
211 aTransMat
.set(1, 2, fY
);
213 doMulMatrix(aTransMat
);
217 void B2DHomMatrix::translate(const B2DTuple
& rTuple
)
219 translate(rTuple
.getX(), rTuple
.getY());
222 void B2DHomMatrix::scale(double fX
, double fY
)
224 const double fOne(1.0);
226 if(!fTools::equal(fOne
, fX
) || !fTools::equal(fOne
, fY
))
228 B2DHomMatrix aScaleMat
;
230 aScaleMat
.set(0, 0, fX
);
231 aScaleMat
.set(1, 1, fY
);
233 doMulMatrix(aScaleMat
);
237 void B2DHomMatrix::scale(const B2DTuple
& rTuple
)
239 scale(rTuple
.getX(), rTuple
.getY());
242 void B2DHomMatrix::shearX(double fSx
)
244 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
245 if(!fTools::equalZero(fSx
))
247 B2DHomMatrix aShearXMat
;
249 aShearXMat
.set(0, 1, fSx
);
251 doMulMatrix(aShearXMat
);
255 void B2DHomMatrix::shearY(double fSy
)
257 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
258 if(!fTools::equalZero(fSy
))
260 B2DHomMatrix aShearYMat
;
262 aShearYMat
.set(1, 0, fSy
);
264 doMulMatrix(aShearYMat
);
270 New, optimized version with local shearX detection. Old version (keeping
271 below, is working well, too) used the 3D matrix decomposition when
272 shear was used. Keeping old version as comment below since it may get
273 necessary to add the determinant() test from there here, too.
275 bool B2DHomMatrix::decompose(B2DTuple
& rScale
, B2DTuple
& rTranslate
, double& rRotate
, double& rShearX
) const
277 // reset rotate and shear and copy translation values in every case
278 rRotate
= rShearX
= 0.0;
279 rTranslate
.setX(get(0, 2));
280 rTranslate
.setY(get(1, 2));
282 // test for rotation and shear
283 if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
285 // no rotation and shear, copy scale values
286 rScale
.setX(get(0, 0));
287 rScale
.setY(get(1, 1));
290 if( rScale
.getX() < 0 && rScale
.getY() < 0 )
292 // there is - 180 degree rotated
299 // get the unit vectors of the transformation -> the perpendicular vectors
300 B2DVector
aUnitVecX(get(0, 0), get(1, 0));
301 B2DVector
aUnitVecY(get(0, 1), get(1, 1));
302 const double fScalarXY(aUnitVecX
.scalar(aUnitVecY
));
304 // Test if shear is zero. That's the case if the unit vectors in the matrix
305 // are perpendicular -> scalar is zero. This is also the case when one of
306 // the unit vectors is zero.
307 if(fTools::equalZero(fScalarXY
))
309 // calculate unsigned scale values
310 rScale
.setX(aUnitVecX
.getLength());
311 rScale
.setY(aUnitVecY
.getLength());
313 // check unit vectors for zero lengths
314 const bool bXIsZero(fTools::equalZero(rScale
.getX()));
315 const bool bYIsZero(fTools::equalZero(rScale
.getY()));
317 if(bXIsZero
|| bYIsZero
)
319 // still extract as much as possible. Scalings are already set
322 // get rotation of X-Axis
323 rRotate
= atan2(aUnitVecX
.getY(), aUnitVecX
.getX());
327 // get rotation of X-Axis. When assuming X and Y perpendicular
328 // and correct rotation, it's the Y-Axis rotation minus 90 degrees
329 rRotate
= atan2(aUnitVecY
.getY(), aUnitVecY
.getX()) - M_PI_2
;
332 // one or both unit vectors do not exist, determinant is zero, no decomposition possible.
333 // Eventually used rotations or shears are lost
339 // calculate rotation of X unit vector relative to (1, 0)
340 rRotate
= atan2(aUnitVecX
.getY(), aUnitVecX
.getX());
342 // use orientation to evtl. correct sign of Y-Scale
343 const double fCrossXY(aUnitVecX
.cross(aUnitVecY
));
347 rScale
.setY(-rScale
.getY());
353 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
355 double fCrossXY(aUnitVecX
.cross(aUnitVecY
));
357 // get rotation by calculating angle of X unit vector relative to (1, 0).
358 // This is before the parallel test following the motto to extract
359 // as much as possible
360 rRotate
= atan2(aUnitVecX
.getY(), aUnitVecX
.getX());
362 // get unsigned scale value for X. It will not change and is useful
363 // for further corrections
364 rScale
.setX(aUnitVecX
.getLength());
366 if(fTools::equalZero(fCrossXY
))
368 // extract as much as possible
369 rScale
.setY(aUnitVecY
.getLength());
371 // unit vectors are parallel, thus not linear independent. No
372 // useful decomposition possible. This should not happen since
373 // the only way to get the unit vectors nearly parallel is
374 // a very big shearing. Anyways, be prepared for hand-filled
376 // Eventually used rotations or shears are lost
381 // calculate the contained shear
382 rShearX
= fScalarXY
/ fCrossXY
;
384 if(!fTools::equalZero(rRotate
))
386 // To be able to correct the shear for aUnitVecY, rotation needs to be
387 // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
388 aUnitVecX
.setX(rScale
.getX());
391 // for Y correction we rotate the UnitVecY back about -rRotate
392 const double fNegRotate(-rRotate
);
393 const double fSin(sin(fNegRotate
));
394 const double fCos(cos(fNegRotate
));
396 const double fNewX(aUnitVecY
.getX() * fCos
- aUnitVecY
.getY() * fSin
);
397 const double fNewY(aUnitVecY
.getX() * fSin
+ aUnitVecY
.getY() * fCos
);
399 aUnitVecY
.setX(fNewX
);
400 aUnitVecY
.setY(fNewY
);
403 // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
404 // Shear correction can only work with removed rotation
405 aUnitVecY
.setX(aUnitVecY
.getX() - (aUnitVecY
.getY() * rShearX
));
406 fCrossXY
= aUnitVecX
.cross(aUnitVecY
);
408 // calculate unsigned scale value for Y, after the corrections since
409 // the shear correction WILL change the length of aUnitVecY
410 rScale
.setY(aUnitVecY
.getLength());
412 // use orientation to set sign of Y-Scale
415 rScale
.setY(-rScale
.getY());
423 } // end of namespace basegfx
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */