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