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