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