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