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 #include <osl/diagnose.h>
27 #include <rtl/instance.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <hommatrixtemplate.hxx>
30 #include <basegfx/tuple/b2dtuple.hxx>
31 #include <basegfx/vector/b2dvector.hxx>
32 #include <basegfx/matrix/b2dhommatrixtools.hxx>
33 
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 namespace basegfx
37 {
38     class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
39     {
40     };
41 
42     namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
43                                                             IdentityMatrix > {}; }
44 
B2DHomMatrix()45 	B2DHomMatrix::B2DHomMatrix() :
46         mpImpl( IdentityMatrix::get() ) // use common identity matrix
47 	{
48 	}
49 
B2DHomMatrix(const B2DHomMatrix & rMat)50 	B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
51         mpImpl(rMat.mpImpl)
52 	{
53 	}
54 
~B2DHomMatrix()55 	B2DHomMatrix::~B2DHomMatrix()
56 	{
57 	}
58 
B2DHomMatrix(double f_0x0,double f_0x1,double f_0x2,double f_1x0,double f_1x1,double f_1x2)59 	B2DHomMatrix::B2DHomMatrix(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
60 	:	mpImpl( IdentityMatrix::get() ) // use common identity matrix, will be made unique with 1st set-call
61 	{
62 		mpImpl->set(0, 0, f_0x0);
63 		mpImpl->set(0, 1, f_0x1);
64 		mpImpl->set(0, 2, f_0x2);
65 		mpImpl->set(1, 0, f_1x0);
66 		mpImpl->set(1, 1, f_1x1);
67 		mpImpl->set(1, 2, f_1x2);
68 	}
69 
operator =(const B2DHomMatrix & rMat)70 	B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
71 	{
72         mpImpl = rMat.mpImpl;
73 		return *this;
74 	}
75 
makeUnique()76     void B2DHomMatrix::makeUnique()
77     {
78         mpImpl.make_unique();
79     }
80 
get(sal_uInt16 nRow,sal_uInt16 nColumn) const81 	double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
82 	{
83 		return mpImpl->get(nRow, nColumn);
84 	}
85 
set(sal_uInt16 nRow,sal_uInt16 nColumn,double fValue)86 	void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
87 	{
88 		mpImpl->set(nRow, nColumn, fValue);
89 	}
90 
set3x2(double f_0x0,double f_0x1,double f_0x2,double f_1x0,double f_1x1,double f_1x2)91 	void B2DHomMatrix::set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
92 	{
93 		mpImpl->set(0, 0, f_0x0);
94 		mpImpl->set(0, 1, f_0x1);
95 		mpImpl->set(0, 2, f_0x2);
96 		mpImpl->set(1, 0, f_1x0);
97 		mpImpl->set(1, 1, f_1x1);
98 		mpImpl->set(1, 2, f_1x2);
99 	}
100 
isLastLineDefault() const101 	bool B2DHomMatrix::isLastLineDefault() const
102 	{
103 		return mpImpl->isLastLineDefault();
104 	}
105 
isIdentity() const106 	bool B2DHomMatrix::isIdentity() const
107 	{
108 		if(mpImpl.same_object(IdentityMatrix::get()))
109 			return true;
110 
111 		return mpImpl->isIdentity();
112 	}
113 
identity()114 	void B2DHomMatrix::identity()
115 	{
116         mpImpl = IdentityMatrix::get();
117 	}
118 
isInvertible() const119 	bool B2DHomMatrix::isInvertible() const
120 	{
121 		return mpImpl->isInvertible();
122 	}
123 
invert()124 	bool B2DHomMatrix::invert()
125 	{
126 		Impl2DHomMatrix aWork(*mpImpl);
127 		sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
128 		sal_Int16 nParity;
129 
130 		if(aWork.ludcmp(pIndex, nParity))
131 		{
132 			mpImpl->doInvert(aWork, pIndex);
133 			delete[] pIndex;
134 
135 			return true;
136 		}
137 
138 		delete[] pIndex;
139 		return false;
140 	}
141 
isNormalized() const142 	bool B2DHomMatrix::isNormalized() const
143 	{
144 		return mpImpl->isNormalized();
145 	}
146 
normalize()147 	void B2DHomMatrix::normalize()
148 	{
149 		if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
150 			mpImpl->doNormalize();
151 	}
152 
determinant() const153 	double B2DHomMatrix::determinant() const
154 	{
155 		return mpImpl->doDeterminant();
156 	}
157 
trace() const158 	double B2DHomMatrix::trace() const
159 	{
160 		return mpImpl->doTrace();
161 	}
162 
transpose()163 	void B2DHomMatrix::transpose()
164 	{
165 		mpImpl->doTranspose();
166 	}
167 
operator +=(const B2DHomMatrix & rMat)168 	B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
169 	{
170 		mpImpl->doAddMatrix(*rMat.mpImpl);
171 		return *this;
172 	}
173 
operator -=(const B2DHomMatrix & rMat)174 	B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
175 	{
176 		mpImpl->doSubMatrix(*rMat.mpImpl);
177 		return *this;
178 	}
179 
operator *=(double fValue)180 	B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
181 	{
182 		const double fOne(1.0);
183 
184 		if(!fTools::equal(fOne, fValue))
185 			mpImpl->doMulMatrix(fValue);
186 
187 		return *this;
188 	}
189 
operator /=(double fValue)190 	B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
191 	{
192 		const double fOne(1.0);
193 
194 		if(!fTools::equal(fOne, fValue))
195 			mpImpl->doMulMatrix(1.0 / fValue);
196 
197 		return *this;
198 	}
199 
operator *=(const B2DHomMatrix & rMat)200 	B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
201 	{
202 		if(!rMat.isIdentity())
203 			mpImpl->doMulMatrix(*rMat.mpImpl);
204 
205 		return *this;
206 	}
207 
operator ==(const B2DHomMatrix & rMat) const208 	bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
209 	{
210 		if(mpImpl.same_object(rMat.mpImpl))
211 			return true;
212 
213 		return mpImpl->isEqual(*rMat.mpImpl);
214 	}
215 
operator !=(const B2DHomMatrix & rMat) const216 	bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
217 	{
218         return !(*this == rMat);
219 	}
220 
rotate(double fRadiant)221 	void B2DHomMatrix::rotate(double fRadiant)
222 	{
223 		if(!fTools::equalZero(fRadiant))
224 		{
225 			double fSin(0.0);
226 			double fCos(1.0);
227 
228 			tools::createSinCosOrthogonal(fSin, fCos, fRadiant);
229 			Impl2DHomMatrix aRotMat;
230 
231 			aRotMat.set(0, 0, fCos);
232 			aRotMat.set(1, 1, fCos);
233 			aRotMat.set(1, 0, fSin);
234 			aRotMat.set(0, 1, -fSin);
235 
236 			mpImpl->doMulMatrix(aRotMat);
237 		}
238 	}
239 
translate(double fX,double fY)240 	void B2DHomMatrix::translate(double fX, double fY)
241 	{
242 		if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
243 		{
244 			Impl2DHomMatrix aTransMat;
245 
246 			aTransMat.set(0, 2, fX);
247 			aTransMat.set(1, 2, fY);
248 
249 			mpImpl->doMulMatrix(aTransMat);
250 		}
251 	}
252 
scale(double fX,double fY)253 	void B2DHomMatrix::scale(double fX, double fY)
254 	{
255 		const double fOne(1.0);
256 
257 		if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
258 		{
259 			Impl2DHomMatrix aScaleMat;
260 
261 			aScaleMat.set(0, 0, fX);
262 			aScaleMat.set(1, 1, fY);
263 
264 			mpImpl->doMulMatrix(aScaleMat);
265 		}
266 	}
267 
shearX(double fSx)268 	void B2DHomMatrix::shearX(double fSx)
269 	{
270 		// #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
271 		if(!fTools::equalZero(fSx))
272 		{
273 			Impl2DHomMatrix aShearXMat;
274 
275 			aShearXMat.set(0, 1, fSx);
276 
277 			mpImpl->doMulMatrix(aShearXMat);
278 		}
279 	}
280 
shearY(double fSy)281 	void B2DHomMatrix::shearY(double fSy)
282 	{
283 		// #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
284 		if(!fTools::equalZero(fSy))
285 		{
286 			Impl2DHomMatrix aShearYMat;
287 
288 			aShearYMat.set(1, 0, fSy);
289 
290 			mpImpl->doMulMatrix(aShearYMat);
291 		}
292 	}
293 
294 	/** Decomposition
295 
296 	   New, optimized version with local shearX detection. Old version (keeping
297 	   below, is working well, too) used the 3D matrix decomposition when
298 	   shear was used. Keeping old version as comment below since it may get
299 	   necessary to add the determinant() test from there here, too.
300 	*/
decompose(B2DTuple & rScale,B2DTuple & rTranslate,double & rRotate,double & rShearX) const301 	bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
302 	{
303 		// when perspective is used, decompose is not made here
304 		if(!mpImpl->isLastLineDefault())
305 		{
306 			return false;
307 		}
308 
309 		// reset rotate and shear and copy translation values in every case
310 		rRotate = rShearX = 0.0;
311 		rTranslate.setX(get(0, 2));
312 		rTranslate.setY(get(1, 2));
313 
314 		// test for rotation and shear
315 		if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
316 		{
317 			// no rotation and shear, copy scale values
318 			rScale.setX(get(0, 0));
319 			rScale.setY(get(1, 1));
320 		}
321 		else
322 		{
323 			// get the unit vectors of the transformation -> the perpendicular vectors
324 			B2DVector aUnitVecX(get(0, 0), get(1, 0));
325 			B2DVector aUnitVecY(get(0, 1), get(1, 1));
326 			const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
327 
328 			// Test if shear is zero. That's the case if the unit vectors in the matrix
329 			// are perpendicular -> scalar is zero. This is also the case when one of
330 			// the unit vectors is zero.
331 			if(fTools::equalZero(fScalarXY))
332 			{
333 				// calculate unsigned scale values
334 				rScale.setX(aUnitVecX.getLength());
335 				rScale.setY(aUnitVecY.getLength());
336 
337 				// check unit vectors for zero lengths
338 				const bool bXIsZero(fTools::equalZero(rScale.getX()));
339 				const bool bYIsZero(fTools::equalZero(rScale.getY()));
340 
341 				if(bXIsZero || bYIsZero)
342 				{
343 					// still extract as much as possible. Scalings are already set
344 					if(!bXIsZero)
345 					{
346 						// get rotation of X-Axis
347 						rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
348 					}
349 					else if(!bYIsZero)
350 					{
351 						// get rotation of X-Axis. When assuming X and Y perpendicular
352 						// and correct rotation, it's the Y-Axis rotation minus 90 degrees
353 						rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
354 					}
355 
356 					// one or both unit vectors do not extist, determinant is zero, no decomposition possible.
357 					// Eventually used rotations or shears are lost
358 					return false;
359 				}
360 				else
361 				{
362 					// no shear
363 					// calculate rotation of X unit vector relative to (1, 0)
364 					rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
365 
366 					// use orientation to evtl. correct sign of Y-Scale
367 					const double fCrossXY(aUnitVecX.cross(aUnitVecY));
368 
369 					if(fCrossXY < 0.0)
370 					{
371 						rScale.setY(-rScale.getY());
372 					}
373 				}
374 			}
375 			else
376 			{
377 				// fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
378 				// shear, extract it
379 				double fCrossXY(aUnitVecX.cross(aUnitVecY));
380 
381 				// get rotation by calculating angle of X unit vector relative to (1, 0).
382 				// This is before the parallel test following the motto to extract
383 				// as much as possible
384 				rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
385 
386 				// get unsigned scale value for X. It will not change and is useful
387 				// for further corrections
388 				rScale.setX(aUnitVecX.getLength());
389 
390 				if(fTools::equalZero(fCrossXY))
391 				{
392 					// extract as much as possible
393 					rScale.setY(aUnitVecY.getLength());
394 
395 					// unit vectors are parallel, thus not linear independent. No
396 					// useful decomposition possible. This should not happen since
397 					// the only way to get the unit vectors nearly parallel is
398 					// a very big shearing. Anyways, be prepared for hand-filled
399 					// matrices
400 					// Eventually used rotations or shears are lost
401 					return false;
402 				}
403 				else
404 				{
405 					// calculate the contained shear
406 					rShearX = fScalarXY / fCrossXY;
407 
408 					if(!fTools::equalZero(rRotate))
409 					{
410 						// To be able to correct the shear for aUnitVecY, rotation needs to be
411 						// removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
412 						aUnitVecX.setX(rScale.getX());
413 						aUnitVecX.setY(0.0);
414 
415 						// for Y correction we rotate the UnitVecY back about -rRotate
416 						const double fNegRotate(-rRotate);
417 						const double fSin(sin(fNegRotate));
418 						const double fCos(cos(fNegRotate));
419 
420 						const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
421 						const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
422 
423 						aUnitVecY.setX(fNewX);
424 						aUnitVecY.setY(fNewY);
425 					}
426 
427 					// Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
428 					// Shear correction can only work with removed rotation
429 					aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
430 					fCrossXY = aUnitVecX.cross(aUnitVecY);
431 
432 					// calculate unsigned scale value for Y, after the corrections since
433 					// the shear correction WILL change the length of aUnitVecY
434 					rScale.setY(aUnitVecY.getLength());
435 
436 					// use orientation to set sign of Y-Scale
437 					if(fCrossXY < 0.0)
438 					{
439 						rScale.setY(-rScale.getY());
440 					}
441 				}
442 			}
443 		}
444 
445 		return true;
446 	}
447 } // end of namespace basegfx
448 
449 ///////////////////////////////////////////////////////////////////////////////
450 // eof
451