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