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