Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <osl/diagnose.h>
30 : : #include <rtl/instance.hxx>
31 : : #include <basegfx/matrix/b2dhommatrix.hxx>
32 : : #include <hommatrixtemplate.hxx>
33 : : #include <basegfx/tuple/b2dtuple.hxx>
34 : : #include <basegfx/vector/b2dvector.hxx>
35 : : #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 : :
37 : : ///////////////////////////////////////////////////////////////////////////////
38 : :
39 : : namespace basegfx
40 : : {
41 : 2407494 : class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
42 : : {
43 : : };
44 : :
45 : : namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
46 : : IdentityMatrix > {}; }
47 : :
48 : 1002836 : B2DHomMatrix::B2DHomMatrix() :
49 : 1002836 : mpImpl( IdentityMatrix::get() ) // use common identity matrix
50 : : {
51 : 1002836 : }
52 : :
53 : 1556443 : B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
54 : 1556443 : mpImpl(rMat.mpImpl)
55 : : {
56 : 1556443 : }
57 : :
58 : 2677719 : B2DHomMatrix::~B2DHomMatrix()
59 : : {
60 : 2677719 : }
61 : :
62 : 120661 : B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
63 : 120661 : : mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call
64 : : {
65 [ + - ][ + - ]: 120661 : mpImpl->set(0, 0, f_0x0);
66 [ + - ][ + - ]: 120661 : mpImpl->set(0, 1, f_0x1);
67 [ + - ][ + - ]: 120661 : mpImpl->set(0, 2, f_0x2);
68 [ + - ][ + - ]: 120661 : mpImpl->set(1, 0, f_1x0);
69 [ + - ][ + - ]: 120661 : mpImpl->set(1, 1, f_1x1);
70 [ + - ][ + - ]: 120661 : mpImpl->set(1, 2, f_1x2);
71 : 120661 : }
72 : :
73 : 269188 : B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
74 : : {
75 : 269188 : mpImpl = rMat.mpImpl;
76 : 269188 : return *this;
77 : : }
78 : :
79 : 30362981 : double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
80 : : {
81 : 30362981 : return mpImpl->get(nRow, nColumn);
82 : : }
83 : :
84 : 1565149 : void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
85 : : {
86 : 1565149 : mpImpl->set(nRow, nColumn, fValue);
87 : 1565149 : }
88 : :
89 : 341 : void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
90 : : {
91 : 341 : mpImpl->set(0, 0, f_0x0);
92 : 341 : mpImpl->set(0, 1, f_0x1);
93 : 341 : mpImpl->set(0, 2, f_0x2);
94 : 341 : mpImpl->set(1, 0, f_1x0);
95 : 341 : mpImpl->set(1, 1, f_1x1);
96 : 341 : mpImpl->set(1, 2, f_1x2);
97 : 341 : }
98 : :
99 : 4307478 : bool B2DHomMatrix::isLastLineDefault() const
100 : : {
101 : 4307478 : return mpImpl->isLastLineDefault();
102 : : }
103 : :
104 : 2556144 : bool B2DHomMatrix::isIdentity() const
105 : : {
106 [ + + ]: 2556144 : if(mpImpl.same_object(IdentityMatrix::get()))
107 : 244052 : return true;
108 : :
109 : 2556144 : return mpImpl->isIdentity();
110 : : }
111 : :
112 : 21578 : void B2DHomMatrix::identity()
113 : : {
114 : 21578 : mpImpl = IdentityMatrix::get();
115 : 21578 : }
116 : :
117 : 0 : bool B2DHomMatrix::isInvertible() const
118 : : {
119 : 0 : return mpImpl->isInvertible();
120 : : }
121 : :
122 : 57145 : bool B2DHomMatrix::invert()
123 : : {
124 [ + - ][ + - ]: 57145 : Impl2DHomMatrix aWork(*mpImpl);
125 [ + - ][ + - ]: 57145 : sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
126 : : sal_Int16 nParity;
127 : :
128 [ + - ][ + - ]: 57145 : if(aWork.ludcmp(pIndex, nParity))
129 : : {
130 [ + - ][ + - ]: 57145 : mpImpl->doInvert(aWork, pIndex);
131 [ + - ]: 57145 : delete[] pIndex;
132 : :
133 : 57145 : return true;
134 : : }
135 : :
136 [ # # ]: 0 : delete[] pIndex;
137 [ + - ]: 57145 : return false;
138 : : }
139 : :
140 : 0 : B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
141 : : {
142 : 0 : mpImpl->doAddMatrix(*rMat.mpImpl);
143 : 0 : return *this;
144 : : }
145 : :
146 : 0 : B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
147 : : {
148 : 0 : mpImpl->doSubMatrix(*rMat.mpImpl);
149 : 0 : return *this;
150 : : }
151 : :
152 : 0 : B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
153 : : {
154 : 0 : const double fOne(1.0);
155 : :
156 [ # # ]: 0 : if(!fTools::equal(fOne, fValue))
157 [ # # ][ # # ]: 0 : mpImpl->doMulMatrix(fValue);
158 : :
159 : 0 : return *this;
160 : : }
161 : :
162 : 0 : B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
163 : : {
164 : 0 : const double fOne(1.0);
165 : :
166 [ # # ]: 0 : if(!fTools::equal(fOne, fValue))
167 [ # # ][ # # ]: 0 : mpImpl->doMulMatrix(1.0 / fValue);
168 : :
169 : 0 : return *this;
170 : : }
171 : :
172 : 393728 : B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
173 : : {
174 [ + + ]: 393728 : if(!rMat.isIdentity())
175 : 376664 : mpImpl->doMulMatrix(*rMat.mpImpl);
176 : :
177 : 393728 : return *this;
178 : : }
179 : :
180 : 84511 : bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
181 : : {
182 [ + + ]: 84511 : if(mpImpl.same_object(rMat.mpImpl))
183 : 25115 : return true;
184 : :
185 : 84511 : return mpImpl->isEqual(*rMat.mpImpl);
186 : : }
187 : :
188 : 20563 : bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
189 : : {
190 : 20563 : return !(*this == rMat);
191 : : }
192 : :
193 : 75362 : void B2DHomMatrix::rotate(double fRadiant)
194 : : {
195 [ + + ]: 75362 : if(!fTools::equalZero(fRadiant))
196 : : {
197 : 43999 : double fSin(0.0);
198 : 43999 : double fCos(1.0);
199 : :
200 [ + - ]: 43999 : tools::createSinCosOrthogonal(fSin, fCos, fRadiant);
201 [ + - ]: 43999 : Impl2DHomMatrix aRotMat;
202 : :
203 [ + - ]: 43999 : aRotMat.set(0, 0, fCos);
204 [ + - ]: 43999 : aRotMat.set(1, 1, fCos);
205 [ + - ]: 43999 : aRotMat.set(1, 0, fSin);
206 [ + - ]: 43999 : aRotMat.set(0, 1, -fSin);
207 : :
208 [ + - ][ + - ]: 43999 : mpImpl->doMulMatrix(aRotMat);
[ + - ]
209 : : }
210 : 75362 : }
211 : :
212 : 67567 : void B2DHomMatrix::translate(double fX, double fY)
213 : : {
214 [ + + ][ + + ]: 67567 : if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
[ + + ]
215 : : {
216 [ + - ]: 63084 : Impl2DHomMatrix aTransMat;
217 : :
218 [ + - ]: 63084 : aTransMat.set(0, 2, fX);
219 [ + - ]: 63084 : aTransMat.set(1, 2, fY);
220 : :
221 [ + - ][ + - ]: 63084 : mpImpl->doMulMatrix(aTransMat);
[ + - ]
222 : : }
223 : 67567 : }
224 : :
225 : 44049 : void B2DHomMatrix::scale(double fX, double fY)
226 : : {
227 : 44049 : const double fOne(1.0);
228 : :
229 [ + + ][ + + ]: 44049 : if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
[ + + ]
230 : : {
231 [ + - ]: 29545 : Impl2DHomMatrix aScaleMat;
232 : :
233 [ + - ]: 29545 : aScaleMat.set(0, 0, fX);
234 [ + - ]: 29545 : aScaleMat.set(1, 1, fY);
235 : :
236 [ + - ][ + - ]: 29545 : mpImpl->doMulMatrix(aScaleMat);
[ + - ]
237 : : }
238 : 44049 : }
239 : :
240 : 438 : void B2DHomMatrix::shearX(double fSx)
241 : : {
242 : : // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
243 [ + + ]: 438 : if(!fTools::equalZero(fSx))
244 : : {
245 [ + - ]: 245 : Impl2DHomMatrix aShearXMat;
246 : :
247 [ + - ]: 245 : aShearXMat.set(0, 1, fSx);
248 : :
249 [ + - ][ + - ]: 245 : mpImpl->doMulMatrix(aShearXMat);
[ + - ]
250 : : }
251 : 438 : }
252 : :
253 : 5 : void B2DHomMatrix::shearY(double fSy)
254 : : {
255 : : // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
256 [ + - ]: 5 : if(!fTools::equalZero(fSy))
257 : : {
258 [ + - ]: 5 : Impl2DHomMatrix aShearYMat;
259 : :
260 [ + - ]: 5 : aShearYMat.set(1, 0, fSy);
261 : :
262 [ + - ][ + - ]: 5 : mpImpl->doMulMatrix(aShearYMat);
[ + - ]
263 : : }
264 : 5 : }
265 : :
266 : : /** Decomposition
267 : :
268 : : New, optimized version with local shearX detection. Old version (keeping
269 : : below, is working well, too) used the 3D matrix decomposition when
270 : : shear was used. Keeping old version as comment below since it may get
271 : : necessary to add the determinant() test from there here, too.
272 : : */
273 : 282434 : bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
274 : : {
275 : : // when perspective is used, decompose is not made here
276 [ - + ]: 282434 : if(!mpImpl->isLastLineDefault())
277 : : {
278 : 0 : return false;
279 : : }
280 : :
281 : : // reset rotate and shear and copy translation values in every case
282 : 282434 : rRotate = rShearX = 0.0;
283 : 282434 : rTranslate.setX(get(0, 2));
284 : 282434 : rTranslate.setY(get(1, 2));
285 : :
286 : : // test for rotation and shear
287 [ + + ][ + - ]: 282434 : if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
[ + - ][ + + ]
[ + - ][ + +
# # # # ]
[ + - ]
288 : : {
289 : : // no rotation and shear, copy scale values
290 : 273935 : rScale.setX(get(0, 0));
291 : 273935 : rScale.setY(get(1, 1));
292 : :
293 : : // or is there?
294 [ + + ][ + + ]: 273935 : if( rScale.getX() < 0 && rScale.getY() < 0 )
[ + + ]
295 : : {
296 : : // there is - 180 degree rotated
297 : 10 : rScale *= -1;
298 : 10 : rRotate = 180*F_PI180;
299 : : }
300 : : }
301 : : else
302 : : {
303 : : // get the unit vectors of the transformation -> the perpendicular vectors
304 [ + - ][ + - ]: 8499 : B2DVector aUnitVecX(get(0, 0), get(1, 0));
305 [ + - ][ + - ]: 8499 : B2DVector aUnitVecY(get(0, 1), get(1, 1));
306 [ + - ]: 8499 : 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 [ + + ]: 8499 : if(fTools::equalZero(fScalarXY))
312 : : {
313 : : // calculate unsigned scale values
314 [ + - ]: 5900 : rScale.setX(aUnitVecX.getLength());
315 [ + - ]: 5900 : rScale.setY(aUnitVecY.getLength());
316 : :
317 : : // check unit vectors for zero lengths
318 : 5900 : const bool bXIsZero(fTools::equalZero(rScale.getX()));
319 : 5900 : const bool bYIsZero(fTools::equalZero(rScale.getY()));
320 : :
321 [ - + ][ + - ]: 5900 : if(bXIsZero || bYIsZero)
322 : : {
323 : : // still extract as much as possible. Scalings are already set
324 [ # # ]: 0 : if(!bXIsZero)
325 : : {
326 : : // get rotation of X-Axis
327 : 0 : rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
328 : : }
329 [ # # ]: 0 : 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 : 0 : rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
334 : : }
335 : :
336 : : // one or both unit vectors do not extist, determinant is zero, no decomposition possible.
337 : : // Eventually used rotations or shears are lost
338 : 0 : return false;
339 : : }
340 : : else
341 : : {
342 : : // no shear
343 : : // calculate rotation of X unit vector relative to (1, 0)
344 : 5900 : rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
345 : :
346 : : // use orientation to evtl. correct sign of Y-Scale
347 [ + - ]: 5900 : const double fCrossXY(aUnitVecX.cross(aUnitVecY));
348 : :
349 [ + + ]: 5900 : if(fCrossXY < 0.0)
350 : : {
351 : 20 : 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 [ + - ]: 2599 : 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 parallell test following the motto to extract
363 : : // as much as possible
364 : 2599 : 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 [ + - ]: 2599 : rScale.setX(aUnitVecX.getLength());
369 : :
370 [ - + ]: 2599 : if(fTools::equalZero(fCrossXY))
371 : : {
372 : : // extract as much as possible
373 [ # # ]: 0 : 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 parallell is
378 : : // a very big shearing. Anyways, be prepared for hand-filled
379 : : // matrices
380 : : // Eventually used rotations or shears are lost
381 : 0 : return false;
382 : : }
383 : : else
384 : : {
385 : : // calculate the contained shear
386 : 2599 : rShearX = fScalarXY / fCrossXY;
387 : :
388 [ + + ]: 2599 : 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 : 2579 : aUnitVecX.setX(rScale.getX());
393 : 2579 : aUnitVecX.setY(0.0);
394 : :
395 : : // for Y correction we rotate the UnitVecY back about -rRotate
396 : 2579 : const double fNegRotate(-rRotate);
397 : 2579 : const double fSin(sin(fNegRotate));
398 : 2579 : const double fCos(cos(fNegRotate));
399 : :
400 : 2579 : const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
401 : 2579 : const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
402 : :
403 : 2579 : aUnitVecY.setX(fNewX);
404 : 2579 : 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 : 2599 : aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
410 [ + - ]: 2599 : 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 [ + - ]: 2599 : rScale.setY(aUnitVecY.getLength());
415 : :
416 : : // use orientation to set sign of Y-Scale
417 [ + + ]: 2599 : if(fCrossXY < 0.0)
418 : : {
419 : 8499 : rScale.setY(-rScale.getY());
420 : : }
421 : : }
422 [ - + ][ + - ]: 8499 : }
423 : : }
424 : :
425 : 282434 : return true;
426 : : }
427 : : } // end of namespace basegfx
428 : :
429 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|