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