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