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 <basegfx/matrix/b3dhommatrix.hxx>
21 #include <hommatrixtemplate.hxx>
22 #include <basegfx/vector/b3dvector.hxx>
23 #include <memory>
24 
25 namespace basegfx
26 {
27     typedef ::basegfx::internal::ImplHomMatrixTemplate< 4 >Impl3DHomMatrix_Base;
28     class Impl3DHomMatrix : public Impl3DHomMatrix_Base
29     {
30     };
31 
32     B3DHomMatrix::B3DHomMatrix() = default;
33 
34     B3DHomMatrix::B3DHomMatrix(const B3DHomMatrix&) = default;
35 
36     B3DHomMatrix::B3DHomMatrix(B3DHomMatrix&&) = default;
37 
38     B3DHomMatrix::~B3DHomMatrix() = default;
39 
40     B3DHomMatrix& B3DHomMatrix::operator=(const B3DHomMatrix&) = default;
41 
42     B3DHomMatrix& B3DHomMatrix::operator=(B3DHomMatrix&&) = default;
43 
get(sal_uInt16 nRow,sal_uInt16 nColumn) const44     double B3DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
45     {
46         return mpImpl->get(nRow, nColumn);
47     }
48 
set(sal_uInt16 nRow,sal_uInt16 nColumn,double fValue)49     void B3DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
50     {
51         mpImpl->set(nRow, nColumn, fValue);
52     }
53 
isLastLineDefault() const54     bool B3DHomMatrix::isLastLineDefault() const
55     {
56         return mpImpl->isLastLineDefault();
57     }
58 
isIdentity() const59     bool B3DHomMatrix::isIdentity() const
60     {
61         return mpImpl->isIdentity();
62     }
63 
identity()64     void B3DHomMatrix::identity()
65     {
66         *mpImpl = Impl3DHomMatrix();
67     }
68 
invert()69     void B3DHomMatrix::invert()
70     {
71         Impl3DHomMatrix aWork(*mpImpl);
72         std::unique_ptr<sal_uInt16[]> pIndex( new sal_uInt16[Impl3DHomMatrix_Base::getEdgeLength()] );
73         sal_Int16 nParity;
74 
75         if(aWork.ludcmp(pIndex.get(), nParity))
76         {
77             mpImpl->doInvert(aWork, pIndex.get());
78         }
79     }
80 
determinant() const81     double B3DHomMatrix::determinant() const
82     {
83         return mpImpl->doDeterminant();
84     }
85 
operator +=(const B3DHomMatrix & rMat)86     B3DHomMatrix& B3DHomMatrix::operator+=(const B3DHomMatrix& rMat)
87     {
88         mpImpl->doAddMatrix(*rMat.mpImpl);
89         return *this;
90     }
91 
operator -=(const B3DHomMatrix & rMat)92     B3DHomMatrix& B3DHomMatrix::operator-=(const B3DHomMatrix& rMat)
93     {
94         mpImpl->doSubMatrix(*rMat.mpImpl);
95         return *this;
96     }
97 
operator *=(double fValue)98     B3DHomMatrix& B3DHomMatrix::operator*=(double fValue)
99     {
100         const double fOne(1.0);
101 
102         if(!fTools::equal(fOne, fValue))
103             mpImpl->doMulMatrix(fValue);
104 
105         return *this;
106     }
107 
operator /=(double fValue)108     B3DHomMatrix& B3DHomMatrix::operator/=(double fValue)
109     {
110         const double fOne(1.0);
111 
112         if(!fTools::equal(fOne, fValue))
113             mpImpl->doMulMatrix(1.0 / fValue);
114 
115         return *this;
116     }
117 
operator *=(const B3DHomMatrix & rMat)118     B3DHomMatrix& B3DHomMatrix::operator*=(const B3DHomMatrix& rMat)
119     {
120         if(rMat.isIdentity())
121         {
122             // multiply with identity, no change -> nothing to do
123         }
124         else if(isIdentity())
125         {
126             // we are identity, result will be rMat -> assign
127             *this = rMat;
128         }
129         else
130         {
131             // multiply
132             mpImpl->doMulMatrix(*rMat.mpImpl);
133         }
134         return *this;
135     }
136 
operator ==(const B3DHomMatrix & rMat) const137     bool B3DHomMatrix::operator==(const B3DHomMatrix& rMat) const
138     {
139         if(mpImpl.same_object(rMat.mpImpl))
140             return true;
141 
142         return mpImpl->isEqual(*rMat.mpImpl);
143     }
144 
operator !=(const B3DHomMatrix & rMat) const145     bool B3DHomMatrix::operator!=(const B3DHomMatrix& rMat) const
146     {
147         return !(*this == rMat);
148     }
149 
rotate(double fAngleX,double fAngleY,double fAngleZ)150     void B3DHomMatrix::rotate(double fAngleX,double fAngleY,double fAngleZ)
151     {
152         if(!fTools::equalZero(fAngleX) || !fTools::equalZero(fAngleY) || !fTools::equalZero(fAngleZ))
153         {
154             if(!fTools::equalZero(fAngleX))
155             {
156                 Impl3DHomMatrix aRotMatX;
157                 double fSin(sin(fAngleX));
158                 double fCos(cos(fAngleX));
159 
160                 aRotMatX.set(1, 1, fCos);
161                 aRotMatX.set(2, 2, fCos);
162                 aRotMatX.set(2, 1, fSin);
163                 aRotMatX.set(1, 2, -fSin);
164 
165                 mpImpl->doMulMatrix(aRotMatX);
166             }
167 
168             if(!fTools::equalZero(fAngleY))
169             {
170                 Impl3DHomMatrix aRotMatY;
171                 double fSin(sin(fAngleY));
172                 double fCos(cos(fAngleY));
173 
174                 aRotMatY.set(0, 0, fCos);
175                 aRotMatY.set(2, 2, fCos);
176                 aRotMatY.set(0, 2, fSin);
177                 aRotMatY.set(2, 0, -fSin);
178 
179                 mpImpl->doMulMatrix(aRotMatY);
180             }
181 
182             if(!fTools::equalZero(fAngleZ))
183             {
184                 Impl3DHomMatrix aRotMatZ;
185                 double fSin(sin(fAngleZ));
186                 double fCos(cos(fAngleZ));
187 
188                 aRotMatZ.set(0, 0, fCos);
189                 aRotMatZ.set(1, 1, fCos);
190                 aRotMatZ.set(1, 0, fSin);
191                 aRotMatZ.set(0, 1, -fSin);
192 
193                 mpImpl->doMulMatrix(aRotMatZ);
194             }
195         }
196     }
197 
rotate(const B3DTuple & rRotation)198     void B3DHomMatrix::rotate(const B3DTuple& rRotation)
199     {
200         rotate(rRotation.getX(), rRotation.getY(), rRotation.getZ());
201     }
202 
translate(double fX,double fY,double fZ)203     void B3DHomMatrix::translate(double fX, double fY, double fZ)
204     {
205         if(!fTools::equalZero(fX) || !fTools::equalZero(fY) || !fTools::equalZero(fZ))
206         {
207             Impl3DHomMatrix aTransMat;
208 
209             aTransMat.set(0, 3, fX);
210             aTransMat.set(1, 3, fY);
211             aTransMat.set(2, 3, fZ);
212 
213             mpImpl->doMulMatrix(aTransMat);
214         }
215     }
216 
translate(const B3DTuple & rRotation)217     void B3DHomMatrix::translate(const B3DTuple& rRotation)
218     {
219         translate(rRotation.getX(), rRotation.getY(), rRotation.getZ());
220     }
221 
scale(double fX,double fY,double fZ)222     void B3DHomMatrix::scale(double fX, double fY, double fZ)
223     {
224         const double fOne(1.0);
225 
226         if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY) ||!fTools::equal(fOne, fZ))
227         {
228             Impl3DHomMatrix aScaleMat;
229 
230             aScaleMat.set(0, 0, fX);
231             aScaleMat.set(1, 1, fY);
232             aScaleMat.set(2, 2, fZ);
233 
234             mpImpl->doMulMatrix(aScaleMat);
235         }
236     }
237 
scale(const B3DTuple & rRotation)238     void B3DHomMatrix::scale(const B3DTuple& rRotation)
239     {
240         scale(rRotation.getX(), rRotation.getY(), rRotation.getZ());
241     }
242 
shearXY(double fSx,double fSy)243     void B3DHomMatrix::shearXY(double fSx, double fSy)
244     {
245         // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
246         if(!fTools::equalZero(fSx) || !fTools::equalZero(fSy))
247         {
248             Impl3DHomMatrix aShearXYMat;
249 
250             aShearXYMat.set(0, 2, fSx);
251             aShearXYMat.set(1, 2, fSy);
252 
253             mpImpl->doMulMatrix(aShearXYMat);
254         }
255     }
256 
shearXZ(double fSx,double fSz)257     void B3DHomMatrix::shearXZ(double fSx, double fSz)
258     {
259         // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
260         if(!fTools::equalZero(fSx) || !fTools::equalZero(fSz))
261         {
262             Impl3DHomMatrix aShearXZMat;
263 
264             aShearXZMat.set(0, 1, fSx);
265             aShearXZMat.set(2, 1, fSz);
266 
267             mpImpl->doMulMatrix(aShearXZMat);
268         }
269     }
frustum(double fLeft,double fRight,double fBottom,double fTop,double fNear,double fFar)270     void B3DHomMatrix::frustum(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
271     {
272         const double fZero(0.0);
273         const double fOne(1.0);
274 
275         if(!fTools::more(fNear, fZero))
276         {
277             fNear = 0.001;
278         }
279 
280         if(!fTools::more(fFar, fZero))
281         {
282             fFar = fOne;
283         }
284 
285         if(fTools::equal(fNear, fFar))
286         {
287             fFar = fNear + fOne;
288         }
289 
290         if(fTools::equal(fLeft, fRight))
291         {
292             fLeft -= fOne;
293             fRight += fOne;
294         }
295 
296         if(fTools::equal(fTop, fBottom))
297         {
298             fBottom -= fOne;
299             fTop += fOne;
300         }
301 
302         Impl3DHomMatrix aFrustumMat;
303 
304         aFrustumMat.set(0, 0, 2.0 * fNear / (fRight - fLeft));
305         aFrustumMat.set(1, 1, 2.0 * fNear / (fTop - fBottom));
306         aFrustumMat.set(0, 2, (fRight + fLeft) / (fRight - fLeft));
307         aFrustumMat.set(1, 2, (fTop + fBottom) / (fTop - fBottom));
308         aFrustumMat.set(2, 2, -fOne * ((fFar + fNear) / (fFar - fNear)));
309         aFrustumMat.set(3, 2, -fOne);
310         aFrustumMat.set(2, 3, -fOne * ((2.0 * fFar * fNear) / (fFar - fNear)));
311         aFrustumMat.set(3, 3, fZero);
312 
313         mpImpl->doMulMatrix(aFrustumMat);
314     }
315 
ortho(double fLeft,double fRight,double fBottom,double fTop,double fNear,double fFar)316     void B3DHomMatrix::ortho(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
317     {
318         if(fTools::equal(fNear, fFar))
319         {
320             fFar = fNear + 1.0;
321         }
322 
323         if(fTools::equal(fLeft, fRight))
324         {
325             fLeft -= 1.0;
326             fRight += 1.0;
327         }
328 
329         if(fTools::equal(fTop, fBottom))
330         {
331             fBottom -= 1.0;
332             fTop += 1.0;
333         }
334 
335         Impl3DHomMatrix aOrthoMat;
336 
337         aOrthoMat.set(0, 0, 2.0 / (fRight - fLeft));
338         aOrthoMat.set(1, 1, 2.0 / (fTop - fBottom));
339         aOrthoMat.set(2, 2, -1.0 * (2.0 / (fFar - fNear)));
340         aOrthoMat.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft)));
341         aOrthoMat.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom)));
342         aOrthoMat.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear)));
343 
344         mpImpl->doMulMatrix(aOrthoMat);
345     }
346 
orientation(const B3DPoint & rVRP,B3DVector aVPN,B3DVector aVUV)347     void B3DHomMatrix::orientation(const B3DPoint& rVRP, B3DVector aVPN, B3DVector aVUV)
348     {
349         Impl3DHomMatrix aOrientationMat;
350 
351         // translate -VRP
352         aOrientationMat.set(0, 3, -rVRP.getX());
353         aOrientationMat.set(1, 3, -rVRP.getY());
354         aOrientationMat.set(2, 3, -rVRP.getZ());
355 
356         // build rotation
357         aVUV.normalize();
358         aVPN.normalize();
359 
360         // build x-axis as perpendicular from aVUV and aVPN
361         B3DVector aRx(aVUV.getPerpendicular(aVPN));
362         aRx.normalize();
363 
364         // y-axis perpendicular to that
365         B3DVector aRy(aVPN.getPerpendicular(aRx));
366         aRy.normalize();
367 
368         // the calculated normals are the line vectors of the rotation matrix,
369         // set them to create rotation
370         aOrientationMat.set(0, 0, aRx.getX());
371         aOrientationMat.set(0, 1, aRx.getY());
372         aOrientationMat.set(0, 2, aRx.getZ());
373         aOrientationMat.set(1, 0, aRy.getX());
374         aOrientationMat.set(1, 1, aRy.getY());
375         aOrientationMat.set(1, 2, aRy.getZ());
376         aOrientationMat.set(2, 0, aVPN.getX());
377         aOrientationMat.set(2, 1, aVPN.getY());
378         aOrientationMat.set(2, 2, aVPN.getZ());
379 
380         mpImpl->doMulMatrix(aOrientationMat);
381     }
382 
decompose(B3DTuple & rScale,B3DTuple & rTranslate,B3DTuple & rRotate,B3DTuple & rShear) const383     void B3DHomMatrix::decompose(B3DTuple& rScale, B3DTuple& rTranslate, B3DTuple& rRotate, B3DTuple& rShear) const
384     {
385         // when perspective is used, decompose is not made here
386         if(!mpImpl->isLastLineDefault())
387             return;
388 
389         // If determinant is zero, decomposition is not possible
390         if(determinant() == 0.0)
391             return;
392 
393         // isolate translation
394         rTranslate.setX(mpImpl->get(0, 3));
395         rTranslate.setY(mpImpl->get(1, 3));
396         rTranslate.setZ(mpImpl->get(2, 3));
397 
398         // correct translate values
399         rTranslate.correctValues();
400 
401         // get scale and shear
402         B3DVector aCol0(mpImpl->get(0, 0), mpImpl->get(1, 0), mpImpl->get(2, 0));
403         B3DVector aCol1(mpImpl->get(0, 1), mpImpl->get(1, 1), mpImpl->get(2, 1));
404         B3DVector aCol2(mpImpl->get(0, 2), mpImpl->get(1, 2), mpImpl->get(2, 2));
405         B3DVector aTemp;
406 
407         // get ScaleX
408         rScale.setX(aCol0.getLength());
409         aCol0.normalize();
410 
411         // get ShearXY
412         rShear.setX(aCol0.scalar(aCol1));
413 
414         if(fTools::equalZero(rShear.getX()))
415         {
416             rShear.setX(0.0);
417         }
418         else
419         {
420             aTemp.setX(aCol1.getX() - rShear.getX() * aCol0.getX());
421             aTemp.setY(aCol1.getY() - rShear.getX() * aCol0.getY());
422             aTemp.setZ(aCol1.getZ() - rShear.getX() * aCol0.getZ());
423             aCol1 = aTemp;
424         }
425 
426         // get ScaleY
427         rScale.setY(aCol1.getLength());
428         aCol1.normalize();
429 
430         const double fShearX(rShear.getX());
431 
432         if(!fTools::equalZero(fShearX))
433         {
434             rShear.setX(rShear.getX() / rScale.getY());
435         }
436 
437         // get ShearXZ
438         rShear.setY(aCol0.scalar(aCol2));
439 
440         if(fTools::equalZero(rShear.getY()))
441         {
442             rShear.setY(0.0);
443         }
444         else
445         {
446             aTemp.setX(aCol2.getX() - rShear.getY() * aCol0.getX());
447             aTemp.setY(aCol2.getY() - rShear.getY() * aCol0.getY());
448             aTemp.setZ(aCol2.getZ() - rShear.getY() * aCol0.getZ());
449             aCol2 = aTemp;
450         }
451 
452         // get ShearYZ
453         rShear.setZ(aCol1.scalar(aCol2));
454 
455         if(fTools::equalZero(rShear.getZ()))
456         {
457             rShear.setZ(0.0);
458         }
459         else
460         {
461             aTemp.setX(aCol2.getX() - rShear.getZ() * aCol1.getX());
462             aTemp.setY(aCol2.getY() - rShear.getZ() * aCol1.getY());
463             aTemp.setZ(aCol2.getZ() - rShear.getZ() * aCol1.getZ());
464             aCol2 = aTemp;
465         }
466 
467         // get ScaleZ
468         rScale.setZ(aCol2.getLength());
469         aCol2.normalize();
470 
471         const double fShearY(rShear.getY());
472 
473         if(!fTools::equalZero(fShearY))
474         {
475             // coverity[copy_paste_error : FALSE] - this is correct getZ, not getY
476             rShear.setY(rShear.getY() / rScale.getZ());
477         }
478 
479         const double fShearZ(rShear.getZ());
480 
481         if(!fTools::equalZero(fShearZ))
482         {
483             // coverity[original] - this is not an original copy-and-paste source for ^^^
484             rShear.setZ(rShear.getZ() / rScale.getZ());
485         }
486 
487         // correct shear values
488         rShear.correctValues();
489 
490         // Coordinate system flip?
491         if(0.0 > aCol0.scalar(aCol1.getPerpendicular(aCol2)))
492         {
493             rScale = -rScale;
494             aCol0 = -aCol0;
495             aCol1 = -aCol1;
496             aCol2 = -aCol2;
497         }
498 
499         // correct scale values
500         rScale.correctValues(1.0);
501 
502         // Get rotations
503         {
504             double fy=0;
505             double cy=0;
506 
507             if( ::basegfx::fTools::equal( aCol0.getZ(), 1.0 )
508                 || aCol0.getZ() > 1.0 )
509             {
510                 fy = -F_PI/2.0;
511                 cy = 0.0;
512             }
513             else if( ::basegfx::fTools::equal( aCol0.getZ(), -1.0 )
514                 || aCol0.getZ() < -1.0 )
515             {
516                 fy = F_PI/2.0;
517                 cy = 0.0;
518             }
519             else
520             {
521                 fy = asin( -aCol0.getZ() );
522                 cy = cos(fy);
523             }
524 
525             rRotate.setY(fy);
526             if( ::basegfx::fTools::equalZero( cy ) )
527             {
528                 if( aCol0.getZ() > 0.0 )
529                     rRotate.setX(atan2(-1.0*aCol1.getX(), aCol1.getY()));
530                 else
531                     rRotate.setX(atan2(aCol1.getX(), aCol1.getY()));
532                 rRotate.setZ(0.0);
533             }
534             else
535             {
536                 rRotate.setX(atan2(aCol1.getZ(), aCol2.getZ()));
537                 rRotate.setZ(atan2(aCol0.getY(), aCol0.getX()));
538             }
539 
540             // correct rotate values
541             rRotate.correctValues();
542         }
543     }
544 } // end of namespace basegfx
545 
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
547