xref: /trunk/main/basegfx/source/matrix/b2dhommatrix.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <osl/diagnose.h>
31 #include <rtl/instance.hxx>
32 #include <basegfx/matrix/b2dhommatrix.hxx>
33 #include <hommatrixtemplate.hxx>
34 #include <basegfx/tuple/b2dtuple.hxx>
35 #include <basegfx/vector/b2dvector.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 
38 ///////////////////////////////////////////////////////////////////////////////
39 
40 namespace basegfx
41 {
42     class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
43     {
44     };
45 
46     namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
47                                                             IdentityMatrix > {}; }
48 
49     B2DHomMatrix::B2DHomMatrix() :
50         mpImpl( IdentityMatrix::get() ) // use common identity matrix
51     {
52     }
53 
54     B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
55         mpImpl(rMat.mpImpl)
56     {
57     }
58 
59     B2DHomMatrix::~B2DHomMatrix()
60     {
61     }
62 
63     B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
64     :   mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call
65     {
66         mpImpl->set(0, 0, f_0x0);
67         mpImpl->set(0, 1, f_0x1);
68         mpImpl->set(0, 2, f_0x2);
69         mpImpl->set(1, 0, f_1x0);
70         mpImpl->set(1, 1, f_1x1);
71         mpImpl->set(1, 2, f_1x2);
72     }
73 
74     B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
75     {
76         mpImpl = rMat.mpImpl;
77         return *this;
78     }
79 
80     void B2DHomMatrix::makeUnique()
81     {
82         mpImpl.make_unique();
83     }
84 
85     double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
86     {
87         return mpImpl->get(nRow, nColumn);
88     }
89 
90     void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
91     {
92         mpImpl->set(nRow, nColumn, fValue);
93     }
94 
95     void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
96     {
97         mpImpl->set(0, 0, f_0x0);
98         mpImpl->set(0, 1, f_0x1);
99         mpImpl->set(0, 2, f_0x2);
100         mpImpl->set(1, 0, f_1x0);
101         mpImpl->set(1, 1, f_1x1);
102         mpImpl->set(1, 2, f_1x2);
103     }
104 
105     bool B2DHomMatrix::isLastLineDefault() const
106     {
107         return mpImpl->isLastLineDefault();
108     }
109 
110     bool B2DHomMatrix::isIdentity() const
111     {
112         if(mpImpl.same_object(IdentityMatrix::get()))
113             return true;
114 
115         return mpImpl->isIdentity();
116     }
117 
118     void B2DHomMatrix::identity()
119     {
120         mpImpl = IdentityMatrix::get();
121     }
122 
123     bool B2DHomMatrix::isInvertible() const
124     {
125         return mpImpl->isInvertible();
126     }
127 
128     bool B2DHomMatrix::invert()
129     {
130         Impl2DHomMatrix aWork(*mpImpl);
131         sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
132         sal_Int16 nParity;
133 
134         if(aWork.ludcmp(pIndex, nParity))
135         {
136             mpImpl->doInvert(aWork, pIndex);
137             delete[] pIndex;
138 
139             return true;
140         }
141 
142         delete[] pIndex;
143         return false;
144     }
145 
146     bool B2DHomMatrix::isNormalized() const
147     {
148         return mpImpl->isNormalized();
149     }
150 
151     void B2DHomMatrix::normalize()
152     {
153         if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
154             mpImpl->doNormalize();
155     }
156 
157     double B2DHomMatrix::determinant() const
158     {
159         return mpImpl->doDeterminant();
160     }
161 
162     double B2DHomMatrix::trace() const
163     {
164         return mpImpl->doTrace();
165     }
166 
167     void B2DHomMatrix::transpose()
168     {
169         mpImpl->doTranspose();
170     }
171 
172     B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
173     {
174         mpImpl->doAddMatrix(*rMat.mpImpl);
175         return *this;
176     }
177 
178     B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
179     {
180         mpImpl->doSubMatrix(*rMat.mpImpl);
181         return *this;
182     }
183 
184     B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
185     {
186         const double fOne(1.0);
187 
188         if(!fTools::equal(fOne, fValue))
189             mpImpl->doMulMatrix(fValue);
190 
191         return *this;
192     }
193 
194     B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
195     {
196         const double fOne(1.0);
197 
198         if(!fTools::equal(fOne, fValue))
199             mpImpl->doMulMatrix(1.0 / fValue);
200 
201         return *this;
202     }
203 
204     B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
205     {
206         if(!rMat.isIdentity())
207             mpImpl->doMulMatrix(*rMat.mpImpl);
208 
209         return *this;
210     }
211 
212     bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
213     {
214         if(mpImpl.same_object(rMat.mpImpl))
215             return true;
216 
217         return mpImpl->isEqual(*rMat.mpImpl);
218     }
219 
220     bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
221     {
222         return !(*this == rMat);
223     }
224 
225     void B2DHomMatrix::rotate(double fRadiant)
226     {
227         if(!fTools::equalZero(fRadiant))
228         {
229             double fSin(0.0);
230             double fCos(1.0);
231 
232             tools::createSinCosOrthogonal(fSin, fCos, fRadiant);
233             Impl2DHomMatrix aRotMat;
234 
235             aRotMat.set(0, 0, fCos);
236             aRotMat.set(1, 1, fCos);
237             aRotMat.set(1, 0, fSin);
238             aRotMat.set(0, 1, -fSin);
239 
240             mpImpl->doMulMatrix(aRotMat);
241         }
242     }
243 
244     void B2DHomMatrix::translate(double fX, double fY)
245     {
246         if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
247         {
248             Impl2DHomMatrix aTransMat;
249 
250             aTransMat.set(0, 2, fX);
251             aTransMat.set(1, 2, fY);
252 
253             mpImpl->doMulMatrix(aTransMat);
254         }
255     }
256 
257     void B2DHomMatrix::scale(double fX, double fY)
258     {
259         const double fOne(1.0);
260 
261         if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
262         {
263             Impl2DHomMatrix aScaleMat;
264 
265             aScaleMat.set(0, 0, fX);
266             aScaleMat.set(1, 1, fY);
267 
268             mpImpl->doMulMatrix(aScaleMat);
269         }
270     }
271 
272     void B2DHomMatrix::shearX(double fSx)
273     {
274         // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
275         if(!fTools::equalZero(fSx))
276         {
277             Impl2DHomMatrix aShearXMat;
278 
279             aShearXMat.set(0, 1, fSx);
280 
281             mpImpl->doMulMatrix(aShearXMat);
282         }
283     }
284 
285     void B2DHomMatrix::shearY(double fSy)
286     {
287         // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
288         if(!fTools::equalZero(fSy))
289         {
290             Impl2DHomMatrix aShearYMat;
291 
292             aShearYMat.set(1, 0, fSy);
293 
294             mpImpl->doMulMatrix(aShearYMat);
295         }
296     }
297 
298     /** Decomposition
299 
300        New, optimized version with local shearX detection. Old version (keeping
301        below, is working well, too) used the 3D matrix decomposition when
302        shear was used. Keeping old version as comment below since it may get
303        necessary to add the determinant() test from there here, too.
304     */
305     bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
306     {
307         // when perspective is used, decompose is not made here
308         if(!mpImpl->isLastLineDefault())
309         {
310             return false;
311         }
312 
313         // reset rotate and shear and copy translation values in every case
314         rRotate = rShearX = 0.0;
315         rTranslate.setX(get(0, 2));
316         rTranslate.setY(get(1, 2));
317 
318         // test for rotation and shear
319         if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
320         {
321             // no rotation and shear, copy scale values
322             rScale.setX(get(0, 0));
323             rScale.setY(get(1, 1));
324         }
325         else
326         {
327             // get the unit vectors of the transformation -> the perpendicular vectors
328             B2DVector aUnitVecX(get(0, 0), get(1, 0));
329             B2DVector aUnitVecY(get(0, 1), get(1, 1));
330             const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
331 
332             // Test if shear is zero. That's the case if the unit vectors in the matrix
333             // are perpendicular -> scalar is zero. This is also the case when one of
334             // the unit vectors is zero.
335             if(fTools::equalZero(fScalarXY))
336             {
337                 // calculate unsigned scale values
338                 rScale.setX(aUnitVecX.getLength());
339                 rScale.setY(aUnitVecY.getLength());
340 
341                 // check unit vectors for zero lengths
342                 const bool bXIsZero(fTools::equalZero(rScale.getX()));
343                 const bool bYIsZero(fTools::equalZero(rScale.getY()));
344 
345                 if(bXIsZero || bYIsZero)
346                 {
347                     // still extract as much as possible. Scalings are already set
348                     if(!bXIsZero)
349                     {
350                         // get rotation of X-Axis
351                         rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
352                     }
353                     else if(!bYIsZero)
354                     {
355                         // get rotation of X-Axis. When assuming X and Y perpendicular
356                         // and correct rotation, it's the Y-Axis rotation minus 90 degrees
357                         rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
358                     }
359 
360                     // one or both unit vectors do not extist, determinant is zero, no decomposition possible.
361                     // Eventually used rotations or shears are lost
362                     return false;
363                 }
364                 else
365                 {
366                     // no shear
367                     // calculate rotation of X unit vector relative to (1, 0)
368                     rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
369 
370                     // use orientation to evtl. correct sign of Y-Scale
371                     const double fCrossXY(aUnitVecX.cross(aUnitVecY));
372 
373                     if(fCrossXY < 0.0)
374                     {
375                         rScale.setY(-rScale.getY());
376                     }
377                 }
378             }
379             else
380             {
381                 // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
382                 // shear, extract it
383                 double fCrossXY(aUnitVecX.cross(aUnitVecY));
384 
385                 // get rotation by calculating angle of X unit vector relative to (1, 0).
386                 // This is before the parallell test following the motto to extract
387                 // as much as possible
388                 rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
389 
390                 // get unsigned scale value for X. It will not change and is useful
391                 // for further corrections
392                 rScale.setX(aUnitVecX.getLength());
393 
394                 if(fTools::equalZero(fCrossXY))
395                 {
396                     // extract as much as possible
397                     rScale.setY(aUnitVecY.getLength());
398 
399                     // unit vectors are parallel, thus not linear independent. No
400                     // useful decomposition possible. This should not happen since
401                     // the only way to get the unit vectors nearly parallell is
402                     // a very big shearing. Anyways, be prepared for hand-filled
403                     // matrices
404                     // Eventually used rotations or shears are lost
405                     return false;
406                 }
407                 else
408                 {
409                     // calculate the contained shear
410                     rShearX = fScalarXY / fCrossXY;
411 
412                     if(!fTools::equalZero(rRotate))
413                     {
414                         // To be able to correct the shear for aUnitVecY, rotation needs to be
415                         // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
416                         aUnitVecX.setX(rScale.getX());
417                         aUnitVecX.setY(0.0);
418 
419                         // for Y correction we rotate the UnitVecY back about -rRotate
420                         const double fNegRotate(-rRotate);
421                         const double fSin(sin(fNegRotate));
422                         const double fCos(cos(fNegRotate));
423 
424                         const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
425                         const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
426 
427                         aUnitVecY.setX(fNewX);
428                         aUnitVecY.setY(fNewY);
429                     }
430 
431                     // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
432                     // Shear correction can only work with removed rotation
433                     aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
434                     fCrossXY = aUnitVecX.cross(aUnitVecY);
435 
436                     // calculate unsigned scale value for Y, after the corrections since
437                     // the shear correction WILL change the length of aUnitVecY
438                     rScale.setY(aUnitVecY.getLength());
439 
440                     // use orientation to set sign of Y-Scale
441                     if(fCrossXY < 0.0)
442                     {
443                         rScale.setY(-rScale.getY());
444                     }
445                 }
446             }
447         }
448 
449         return true;
450     }
451 } // end of namespace basegfx
452 
453 ///////////////////////////////////////////////////////////////////////////////
454 // eof
455