LibreOffice Module basegfx (master) 1
b2dhommatrix.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21#include <hommatrixtemplate.hxx>
25#include <memory>
26
27namespace basegfx
28{
29 typedef ::basegfx::internal::ImplHomMatrixTemplate< 3 > Impl2DHomMatrix_Base;
31 {
32 };
33
35
37
39
41
43
44 B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
45 {
46 mpImpl->set(0, 0, f_0x0);
47 mpImpl->set(0, 1, f_0x1);
48 mpImpl->set(0, 2, f_0x2);
49 mpImpl->set(1, 0, f_1x0);
50 mpImpl->set(1, 1, f_1x1);
51 mpImpl->set(1, 2, f_1x2);
52 }
53
55
57
58 double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
59 {
60 return mpImpl->get(nRow, nColumn);
61 }
62
63 void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
64 {
65 mpImpl->set(nRow, nColumn, fValue);
66 }
67
68 void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
69 {
70 mpImpl->set(0, 0, f_0x0);
71 mpImpl->set(0, 1, f_0x1);
72 mpImpl->set(0, 2, f_0x2);
73 mpImpl->set(1, 0, f_1x0);
74 mpImpl->set(1, 1, f_1x1);
75 mpImpl->set(1, 2, f_1x2);
76 }
77
79 {
80 return mpImpl->isLastLineDefault();
81 }
82
84 {
85 return mpImpl.same_object(DEFAULT) || mpImpl->isIdentity();
86 }
87
89 {
91 }
92
94 {
95 return mpImpl->isInvertible();
96 }
97
99 {
100 if(isIdentity())
101 {
102 return true;
103 }
104
105 Impl2DHomMatrix aWork(*mpImpl);
106 sal_uInt16* pIndex = static_cast<sal_uInt16*>(alloca( sizeof(sal_uInt16) * Impl2DHomMatrix_Base::getEdgeLength() ));
107 sal_Int16 nParity;
108
109 if(aWork.ludcmp(pIndex, nParity))
110 {
111 mpImpl->doInvert(aWork, pIndex);
112 return true;
113 }
114
115 return false;
116 }
117
119 {
120 mpImpl->doAddMatrix(*rMat.mpImpl);
121 return *this;
122 }
123
125 {
126 mpImpl->doSubMatrix(*rMat.mpImpl);
127 return *this;
128 }
129
131 {
132 const double fOne(1.0);
133
134 if(!fTools::equal(fOne, fValue))
135 mpImpl->doMulMatrix(fValue);
136
137 return *this;
138 }
139
141 {
142 const double fOne(1.0);
143
144 if(!fTools::equal(fOne, fValue))
145 mpImpl->doMulMatrix(1.0 / fValue);
146
147 return *this;
148 }
149
151 {
152 if(rMat.isIdentity())
153 {
154 // multiply with identity, no change -> nothing to do
155 }
156 else if(isIdentity())
157 {
158 // we are identity, result will be rMat -> assign
159 *this = rMat;
160 }
161 else
162 {
163 // multiply
164 mpImpl->doMulMatrix(*rMat.mpImpl);
165 }
166
167 return *this;
168 }
169
171 {
172 if(mpImpl.same_object(rMat.mpImpl))
173 return true;
174
175 return mpImpl->isEqual(*rMat.mpImpl);
176 }
177
179 {
180 return !(*this == rMat);
181 }
182
183 void B2DHomMatrix::rotate(double fRadiant)
184 {
185 if(fTools::equalZero(fRadiant))
186 return;
187
188 double fSin(0.0);
189 double fCos(1.0);
190
191 utils::createSinCosOrthogonal(fSin, fCos, fRadiant);
192 Impl2DHomMatrix aRotMat;
193
194 aRotMat.set(0, 0, fCos);
195 aRotMat.set(1, 1, fCos);
196 aRotMat.set(1, 0, fSin);
197 aRotMat.set(0, 1, -fSin);
198
199 mpImpl->doMulMatrix(aRotMat);
200 }
201
202 void B2DHomMatrix::translate(double fX, double fY)
203 {
204 if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
205 {
206 Impl2DHomMatrix aTransMat;
207
208 aTransMat.set(0, 2, fX);
209 aTransMat.set(1, 2, fY);
210
211 mpImpl->doMulMatrix(aTransMat);
212 }
213 }
214
216 {
217 translate(rTuple.getX(), rTuple.getY());
218 }
219
220 void B2DHomMatrix::scale(double fX, double fY)
221 {
222 const double fOne(1.0);
223
224 if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
225 {
226 Impl2DHomMatrix aScaleMat;
227
228 aScaleMat.set(0, 0, fX);
229 aScaleMat.set(1, 1, fY);
230
231 mpImpl->doMulMatrix(aScaleMat);
232 }
233 }
234
235 void B2DHomMatrix::scale(const B2DTuple& rTuple)
236 {
237 scale(rTuple.getX(), rTuple.getY());
238 }
239
240 void B2DHomMatrix::shearX(double fSx)
241 {
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(fSx))
244 {
245 Impl2DHomMatrix aShearXMat;
246
247 aShearXMat.set(0, 1, fSx);
248
249 mpImpl->doMulMatrix(aShearXMat);
250 }
251 }
252
253 void B2DHomMatrix::shearY(double fSy)
254 {
255 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
256 if(!fTools::equalZero(fSy))
257 {
258 Impl2DHomMatrix aShearYMat;
259
260 aShearYMat.set(1, 0, fSy);
261
262 mpImpl->doMulMatrix(aShearYMat);
263 }
264 }
265
273 bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
274 {
275 // when perspective is used, decompose is not made here
276 if(!mpImpl->isLastLineDefault())
277 {
278 return false;
279 }
280
281 // reset rotate and shear and copy translation values in every case
282 rRotate = rShearX = 0.0;
283 rTranslate.setX(get(0, 2));
284 rTranslate.setY(get(1, 2));
285
286 // test for rotation and shear
287 if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
288 {
289 // no rotation and shear, copy scale values
290 rScale.setX(get(0, 0));
291 rScale.setY(get(1, 1));
292
293 // or is there?
294 if( rScale.getX() < 0 && rScale.getY() < 0 )
295 {
296 // there is - 180 degree rotated
297 rScale *= -1;
298 rRotate = M_PI;
299 }
300 }
301 else
302 {
303 // get the unit vectors of the transformation -> the perpendicular vectors
304 B2DVector aUnitVecX(get(0, 0), get(1, 0));
305 B2DVector aUnitVecY(get(0, 1), get(1, 1));
306 const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
307
308 // Test if shear is zero. That's the case if the unit vectors in the matrix
309 // are perpendicular -> scalar is zero. This is also the case when one of
310 // the unit vectors is zero.
311 if(fTools::equalZero(fScalarXY))
312 {
313 // calculate unsigned scale values
314 rScale.setX(aUnitVecX.getLength());
315 rScale.setY(aUnitVecY.getLength());
316
317 // check unit vectors for zero lengths
318 const bool bXIsZero(fTools::equalZero(rScale.getX()));
319 const bool bYIsZero(fTools::equalZero(rScale.getY()));
320
321 if(bXIsZero || bYIsZero)
322 {
323 // still extract as much as possible. Scalings are already set
324 if(!bXIsZero)
325 {
326 // get rotation of X-Axis
327 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
328 }
329 else if(!bYIsZero)
330 {
331 // get rotation of X-Axis. When assuming X and Y perpendicular
332 // and correct rotation, it's the Y-Axis rotation minus 90 degrees
333 rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
334 }
335
336 // one or both unit vectors do not exist, determinant is zero, no decomposition possible.
337 // Eventually used rotations or shears are lost
338 return false;
339 }
340 else
341 {
342 // no shear
343 // calculate rotation of X unit vector relative to (1, 0)
344 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
345
346 // use orientation to evtl. correct sign of Y-Scale
347 const double fCrossXY(aUnitVecX.cross(aUnitVecY));
348
349 if(fCrossXY < 0.0)
350 {
351 rScale.setY(-rScale.getY());
352 }
353 }
354 }
355 else
356 {
357 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
358 // shear, extract it
359 double fCrossXY(aUnitVecX.cross(aUnitVecY));
360
361 // get rotation by calculating angle of X unit vector relative to (1, 0).
362 // This is before the parallel test following the motto to extract
363 // as much as possible
364 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
365
366 // get unsigned scale value for X. It will not change and is useful
367 // for further corrections
368 rScale.setX(aUnitVecX.getLength());
369
370 if(fTools::equalZero(fCrossXY))
371 {
372 // extract as much as possible
373 rScale.setY(aUnitVecY.getLength());
374
375 // unit vectors are parallel, thus not linear independent. No
376 // useful decomposition possible. This should not happen since
377 // the only way to get the unit vectors nearly parallel is
378 // a very big shearing. Anyways, be prepared for hand-filled
379 // matrices
380 // Eventually used rotations or shears are lost
381 return false;
382 }
383 else
384 {
385 // calculate the contained shear
386 rShearX = fScalarXY / fCrossXY;
387
388 if(!fTools::equalZero(rRotate))
389 {
390 // To be able to correct the shear for aUnitVecY, rotation needs to be
391 // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
392 aUnitVecX.setX(rScale.getX());
393 aUnitVecX.setY(0.0);
394
395 // for Y correction we rotate the UnitVecY back about -rRotate
396 const double fNegRotate(-rRotate);
397 const double fSin(sin(fNegRotate));
398 const double fCos(cos(fNegRotate));
399
400 const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
401 const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
402
403 aUnitVecY.setX(fNewX);
404 aUnitVecY.setY(fNewY);
405 }
406
407 // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
408 // Shear correction can only work with removed rotation
409 aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
410 fCrossXY = aUnitVecX.cross(aUnitVecY);
411
412 // calculate unsigned scale value for Y, after the corrections since
413 // the shear correction WILL change the length of aUnitVecY
414 rScale.setY(aUnitVecY.getLength());
415
416 // use orientation to set sign of Y-Scale
417 if(fCrossXY < 0.0)
418 {
419 rScale.setY(-rScale.getY());
420 }
421 }
422 }
423 }
424
425 return true;
426 }
427} // end of namespace basegfx
428
429/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool isLastLineDefault() const
bool operator!=(const B2DHomMatrix &rMat) const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
Help routine to decompose given homogen 3x3 matrix to components.
B2DHomMatrix & operator=(const B2DHomMatrix &rMat)
bool isInvertible() const
void shearX(double fSx)
B2DHomMatrix & operator/=(double fValue)
void set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
allow setting all needed values for a 3x2 matrix in one call.
bool operator==(const B2DHomMatrix &rMat) const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void rotate(double fRadiant)
B2DHomMatrix & operator+=(const B2DHomMatrix &rMat)
void translate(double fX, double fY)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
void scale(double fX, double fY)
B2DHomMatrix & operator*=(double fValue)
void shearY(double fSy)
B2DHomMatrix & operator-=(const B2DHomMatrix &rMat)
bool isIdentity() const
Base class for all Points/Vectors with two double values.
Definition: b2dtuple.hxx:39
Base Point class with two double values.
Definition: b2dvector.hxx:40
double scalar(const B2DVector &rVec) const
Calculate the Scalar with another 2D Vector.
Definition: b2dvector.hxx:134
double cross(const B2DVector &rVec) const
Calculate the length of the cross product with another 2D Vector.
Definition: b2dvector.hxx:151
double getLength() const
Calculate the length of this 2D Vector.
Definition: b2dvector.cxx:54
TYPE getX() const
Get X-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:63
void setY(TYPE fY)
Set Y-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:72
TYPE getY() const
Get Y-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:66
void setX(TYPE fX)
Set X-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:69
void set(sal_uInt16 nRow, sal_uInt16 nColumn, const double &rValue)
bool ludcmp(sal_uInt16 nIndex[], sal_Int16 &nParity)
bool same_object(const cow_wrapper &rOther) const
bool equalZero(const T &rfVal)
Compare against small value.
Definition: ftools.hxx:156
bool equal(T const &rfValA, T const &rfValB)
Definition: ftools.hxx:169
void createSinCosOrthogonal(double &o_rSin, double &o_rCos, double fRadiant)
If the rotation angle is an approximate multiple of pi/2, force fSin/fCos to -1/0/1,...
static o3tl::cow_wrapper< Impl2DHomMatrix > DEFAULT
::basegfx::internal::ImplHomMatrixTemplate< 3 > Impl2DHomMatrix_Base