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