Line data Source code
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 :
20 : #include <osl/diagnose.h>
21 : #include <rtl/instance.hxx>
22 : #include <basegfx/matrix/b2dhommatrix.hxx>
23 : #include <hommatrixtemplate.hxx>
24 : #include <basegfx/tuple/b2dtuple.hxx>
25 : #include <basegfx/vector/b2dvector.hxx>
26 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
27 :
28 : namespace basegfx
29 : {
30 2578007 : class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
31 : {
32 : };
33 :
34 : namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
35 : IdentityMatrix > {}; }
36 :
37 1021504 : B2DHomMatrix::B2DHomMatrix() :
38 1021504 : mpImpl( IdentityMatrix::get() ) // use common identity matrix
39 : {
40 1021504 : }
41 :
42 1646671 : B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
43 1646671 : mpImpl(rMat.mpImpl)
44 : {
45 1646671 : }
46 :
47 2771272 : B2DHomMatrix::~B2DHomMatrix()
48 : {
49 2771272 : }
50 :
51 105031 : B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
52 105031 : : mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call
53 : {
54 105031 : mpImpl->set(0, 0, f_0x0);
55 105031 : mpImpl->set(0, 1, f_0x1);
56 105031 : mpImpl->set(0, 2, f_0x2);
57 105031 : mpImpl->set(1, 0, f_1x0);
58 105031 : mpImpl->set(1, 1, f_1x1);
59 105031 : mpImpl->set(1, 2, f_1x2);
60 105031 : }
61 :
62 345237 : B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
63 : {
64 345237 : mpImpl = rMat.mpImpl;
65 345237 : return *this;
66 : }
67 :
68 62896461 : double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
69 : {
70 62896461 : return mpImpl->get(nRow, nColumn);
71 : }
72 :
73 1067358 : void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
74 : {
75 1067358 : mpImpl->set(nRow, nColumn, fValue);
76 1067358 : }
77 :
78 545 : void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
79 : {
80 545 : mpImpl->set(0, 0, f_0x0);
81 545 : mpImpl->set(0, 1, f_0x1);
82 545 : mpImpl->set(0, 2, f_0x2);
83 545 : mpImpl->set(1, 0, f_1x0);
84 545 : mpImpl->set(1, 1, f_1x1);
85 545 : mpImpl->set(1, 2, f_1x2);
86 545 : }
87 :
88 9746382 : bool B2DHomMatrix::isLastLineDefault() const
89 : {
90 9746382 : return mpImpl->isLastLineDefault();
91 : }
92 :
93 2279542 : bool B2DHomMatrix::isIdentity() const
94 : {
95 2279542 : if(mpImpl.same_object(IdentityMatrix::get()))
96 293017 : return true;
97 :
98 1986525 : return mpImpl->isIdentity();
99 : }
100 :
101 23757 : void B2DHomMatrix::identity()
102 : {
103 23757 : mpImpl = IdentityMatrix::get();
104 23757 : }
105 :
106 0 : bool B2DHomMatrix::isInvertible() const
107 : {
108 0 : return mpImpl->isInvertible();
109 : }
110 :
111 54032 : bool B2DHomMatrix::invert()
112 : {
113 54032 : Impl2DHomMatrix aWork(*mpImpl);
114 54032 : sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
115 : sal_Int16 nParity;
116 :
117 54032 : if(aWork.ludcmp(pIndex, nParity))
118 : {
119 54032 : mpImpl->doInvert(aWork, pIndex);
120 54032 : delete[] pIndex;
121 :
122 54032 : return true;
123 : }
124 :
125 0 : delete[] pIndex;
126 0 : return false;
127 : }
128 :
129 0 : B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
130 : {
131 0 : mpImpl->doAddMatrix(*rMat.mpImpl);
132 0 : return *this;
133 : }
134 :
135 0 : B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
136 : {
137 0 : mpImpl->doSubMatrix(*rMat.mpImpl);
138 0 : return *this;
139 : }
140 :
141 0 : B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
142 : {
143 0 : const double fOne(1.0);
144 :
145 0 : if(!fTools::equal(fOne, fValue))
146 0 : mpImpl->doMulMatrix(fValue);
147 :
148 0 : return *this;
149 : }
150 :
151 0 : B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
152 : {
153 0 : const double fOne(1.0);
154 :
155 0 : if(!fTools::equal(fOne, fValue))
156 0 : mpImpl->doMulMatrix(1.0 / fValue);
157 :
158 0 : return *this;
159 : }
160 :
161 298950 : B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
162 : {
163 298950 : if(!rMat.isIdentity())
164 271820 : mpImpl->doMulMatrix(*rMat.mpImpl);
165 :
166 298950 : return *this;
167 : }
168 :
169 71552 : bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
170 : {
171 71552 : if(mpImpl.same_object(rMat.mpImpl))
172 33533 : return true;
173 :
174 38019 : return mpImpl->isEqual(*rMat.mpImpl);
175 : }
176 :
177 18332 : bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
178 : {
179 18332 : return !(*this == rMat);
180 : }
181 :
182 106568 : void B2DHomMatrix::rotate(double fRadiant)
183 : {
184 106568 : if(!fTools::equalZero(fRadiant))
185 : {
186 72436 : double fSin(0.0);
187 72436 : double fCos(1.0);
188 :
189 72436 : tools::createSinCosOrthogonal(fSin, fCos, fRadiant);
190 72436 : Impl2DHomMatrix aRotMat;
191 :
192 72436 : aRotMat.set(0, 0, fCos);
193 72436 : aRotMat.set(1, 1, fCos);
194 72436 : aRotMat.set(1, 0, fSin);
195 72436 : aRotMat.set(0, 1, -fSin);
196 :
197 72436 : mpImpl->doMulMatrix(aRotMat);
198 : }
199 106568 : }
200 :
201 163785 : void B2DHomMatrix::translate(double fX, double fY)
202 : {
203 163785 : if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
204 : {
205 145976 : Impl2DHomMatrix aTransMat;
206 :
207 145976 : aTransMat.set(0, 2, fX);
208 145976 : aTransMat.set(1, 2, fY);
209 :
210 145976 : mpImpl->doMulMatrix(aTransMat);
211 : }
212 163785 : }
213 :
214 148040 : void B2DHomMatrix::scale(double fX, double fY)
215 : {
216 148040 : const double fOne(1.0);
217 :
218 148040 : if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
219 : {
220 119722 : Impl2DHomMatrix aScaleMat;
221 :
222 119722 : aScaleMat.set(0, 0, fX);
223 119722 : aScaleMat.set(1, 1, fY);
224 :
225 119722 : mpImpl->doMulMatrix(aScaleMat);
226 : }
227 148040 : }
228 :
229 575 : void B2DHomMatrix::shearX(double fSx)
230 : {
231 : // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
232 575 : if(!fTools::equalZero(fSx))
233 : {
234 102 : Impl2DHomMatrix aShearXMat;
235 :
236 102 : aShearXMat.set(0, 1, fSx);
237 :
238 102 : mpImpl->doMulMatrix(aShearXMat);
239 : }
240 575 : }
241 :
242 2 : void B2DHomMatrix::shearY(double fSy)
243 : {
244 : // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
245 2 : if(!fTools::equalZero(fSy))
246 : {
247 2 : Impl2DHomMatrix aShearYMat;
248 :
249 2 : aShearYMat.set(1, 0, fSy);
250 :
251 2 : mpImpl->doMulMatrix(aShearYMat);
252 : }
253 2 : }
254 :
255 : /** Decomposition
256 :
257 : New, optimized version with local shearX detection. Old version (keeping
258 : below, is working well, too) used the 3D matrix decomposition when
259 : shear was used. Keeping old version as comment below since it may get
260 : necessary to add the determinant() test from there here, too.
261 : */
262 116860 : bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
263 : {
264 : // when perspective is used, decompose is not made here
265 116860 : if(!mpImpl->isLastLineDefault())
266 : {
267 0 : return false;
268 : }
269 :
270 : // reset rotate and shear and copy translation values in every case
271 116860 : rRotate = rShearX = 0.0;
272 116860 : rTranslate.setX(get(0, 2));
273 116860 : rTranslate.setY(get(1, 2));
274 :
275 : // test for rotation and shear
276 116860 : if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
277 : {
278 : // no rotation and shear, copy scale values
279 114187 : rScale.setX(get(0, 0));
280 114187 : rScale.setY(get(1, 1));
281 :
282 : // or is there?
283 114187 : if( rScale.getX() < 0 && rScale.getY() < 0 )
284 : {
285 : // there is - 180 degree rotated
286 624 : rScale *= -1;
287 624 : rRotate = 180*F_PI180;
288 : }
289 : }
290 : else
291 : {
292 : // get the unit vectors of the transformation -> the perpendicular vectors
293 2673 : B2DVector aUnitVecX(get(0, 0), get(1, 0));
294 5346 : B2DVector aUnitVecY(get(0, 1), get(1, 1));
295 2673 : const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
296 :
297 : // Test if shear is zero. That's the case if the unit vectors in the matrix
298 : // are perpendicular -> scalar is zero. This is also the case when one of
299 : // the unit vectors is zero.
300 2673 : if(fTools::equalZero(fScalarXY))
301 : {
302 : // calculate unsigned scale values
303 2000 : rScale.setX(aUnitVecX.getLength());
304 2000 : rScale.setY(aUnitVecY.getLength());
305 :
306 : // check unit vectors for zero lengths
307 2000 : const bool bXIsZero(fTools::equalZero(rScale.getX()));
308 2000 : const bool bYIsZero(fTools::equalZero(rScale.getY()));
309 :
310 2000 : if(bXIsZero || bYIsZero)
311 : {
312 : // still extract as much as possible. Scalings are already set
313 0 : if(!bXIsZero)
314 : {
315 : // get rotation of X-Axis
316 0 : rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
317 : }
318 0 : else if(!bYIsZero)
319 : {
320 : // get rotation of X-Axis. When assuming X and Y perpendicular
321 : // and correct rotation, it's the Y-Axis rotation minus 90 degrees
322 0 : rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
323 : }
324 :
325 : // one or both unit vectors do not extist, determinant is zero, no decomposition possible.
326 : // Eventually used rotations or shears are lost
327 0 : return false;
328 : }
329 : else
330 : {
331 : // no shear
332 : // calculate rotation of X unit vector relative to (1, 0)
333 2000 : rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
334 :
335 : // use orientation to evtl. correct sign of Y-Scale
336 2000 : const double fCrossXY(aUnitVecX.cross(aUnitVecY));
337 :
338 2000 : if(fCrossXY < 0.0)
339 : {
340 8 : rScale.setY(-rScale.getY());
341 : }
342 : }
343 : }
344 : else
345 : {
346 : // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
347 : // shear, extract it
348 673 : double fCrossXY(aUnitVecX.cross(aUnitVecY));
349 :
350 : // get rotation by calculating angle of X unit vector relative to (1, 0).
351 : // This is before the parallell test following the motto to extract
352 : // as much as possible
353 673 : rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
354 :
355 : // get unsigned scale value for X. It will not change and is useful
356 : // for further corrections
357 673 : rScale.setX(aUnitVecX.getLength());
358 :
359 673 : if(fTools::equalZero(fCrossXY))
360 : {
361 : // extract as much as possible
362 0 : rScale.setY(aUnitVecY.getLength());
363 :
364 : // unit vectors are parallel, thus not linear independent. No
365 : // useful decomposition possible. This should not happen since
366 : // the only way to get the unit vectors nearly parallell is
367 : // a very big shearing. Anyways, be prepared for hand-filled
368 : // matrices
369 : // Eventually used rotations or shears are lost
370 0 : return false;
371 : }
372 : else
373 : {
374 : // calculate the contained shear
375 673 : rShearX = fScalarXY / fCrossXY;
376 :
377 673 : if(!fTools::equalZero(rRotate))
378 : {
379 : // To be able to correct the shear for aUnitVecY, rotation needs to be
380 : // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
381 665 : aUnitVecX.setX(rScale.getX());
382 665 : aUnitVecX.setY(0.0);
383 :
384 : // for Y correction we rotate the UnitVecY back about -rRotate
385 665 : const double fNegRotate(-rRotate);
386 665 : const double fSin(sin(fNegRotate));
387 665 : const double fCos(cos(fNegRotate));
388 :
389 665 : const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
390 665 : const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
391 :
392 665 : aUnitVecY.setX(fNewX);
393 665 : aUnitVecY.setY(fNewY);
394 : }
395 :
396 : // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
397 : // Shear correction can only work with removed rotation
398 673 : aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
399 673 : fCrossXY = aUnitVecX.cross(aUnitVecY);
400 :
401 : // calculate unsigned scale value for Y, after the corrections since
402 : // the shear correction WILL change the length of aUnitVecY
403 673 : rScale.setY(aUnitVecY.getLength());
404 :
405 : // use orientation to set sign of Y-Scale
406 673 : if(fCrossXY < 0.0)
407 : {
408 24 : rScale.setY(-rScale.getY());
409 : }
410 : }
411 2673 : }
412 : }
413 :
414 116860 : return true;
415 : }
416 : } // end of namespace basegfx
417 :
418 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|