1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <basegfx/matrix/b3dhommatrix.hxx> 21 #include <hommatrixtemplate.hxx> 22 #include <basegfx/vector/b3dvector.hxx> 23 #include <memory> 24 25 namespace basegfx 26 { 27 typedef ::basegfx::internal::ImplHomMatrixTemplate< 4 >Impl3DHomMatrix_Base; 28 class Impl3DHomMatrix : public Impl3DHomMatrix_Base 29 { 30 }; 31 32 B3DHomMatrix::B3DHomMatrix() = default; 33 34 B3DHomMatrix::B3DHomMatrix(const B3DHomMatrix&) = default; 35 36 B3DHomMatrix::B3DHomMatrix(B3DHomMatrix&&) = default; 37 38 B3DHomMatrix::~B3DHomMatrix() = default; 39 40 B3DHomMatrix& B3DHomMatrix::operator=(const B3DHomMatrix&) = default; 41 42 B3DHomMatrix& B3DHomMatrix::operator=(B3DHomMatrix&&) = default; 43 get(sal_uInt16 nRow,sal_uInt16 nColumn) const44 double B3DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const 45 { 46 return mpImpl->get(nRow, nColumn); 47 } 48 set(sal_uInt16 nRow,sal_uInt16 nColumn,double fValue)49 void B3DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue) 50 { 51 mpImpl->set(nRow, nColumn, fValue); 52 } 53 isLastLineDefault() const54 bool B3DHomMatrix::isLastLineDefault() const 55 { 56 return mpImpl->isLastLineDefault(); 57 } 58 isIdentity() const59 bool B3DHomMatrix::isIdentity() const 60 { 61 return mpImpl->isIdentity(); 62 } 63 identity()64 void B3DHomMatrix::identity() 65 { 66 *mpImpl = Impl3DHomMatrix(); 67 } 68 invert()69 void B3DHomMatrix::invert() 70 { 71 Impl3DHomMatrix aWork(*mpImpl); 72 std::unique_ptr<sal_uInt16[]> pIndex( new sal_uInt16[Impl3DHomMatrix_Base::getEdgeLength()] ); 73 sal_Int16 nParity; 74 75 if(aWork.ludcmp(pIndex.get(), nParity)) 76 { 77 mpImpl->doInvert(aWork, pIndex.get()); 78 } 79 } 80 determinant() const81 double B3DHomMatrix::determinant() const 82 { 83 return mpImpl->doDeterminant(); 84 } 85 operator +=(const B3DHomMatrix & rMat)86 B3DHomMatrix& B3DHomMatrix::operator+=(const B3DHomMatrix& rMat) 87 { 88 mpImpl->doAddMatrix(*rMat.mpImpl); 89 return *this; 90 } 91 operator -=(const B3DHomMatrix & rMat)92 B3DHomMatrix& B3DHomMatrix::operator-=(const B3DHomMatrix& rMat) 93 { 94 mpImpl->doSubMatrix(*rMat.mpImpl); 95 return *this; 96 } 97 operator *=(double fValue)98 B3DHomMatrix& B3DHomMatrix::operator*=(double fValue) 99 { 100 const double fOne(1.0); 101 102 if(!fTools::equal(fOne, fValue)) 103 mpImpl->doMulMatrix(fValue); 104 105 return *this; 106 } 107 operator /=(double fValue)108 B3DHomMatrix& B3DHomMatrix::operator/=(double fValue) 109 { 110 const double fOne(1.0); 111 112 if(!fTools::equal(fOne, fValue)) 113 mpImpl->doMulMatrix(1.0 / fValue); 114 115 return *this; 116 } 117 operator *=(const B3DHomMatrix & rMat)118 B3DHomMatrix& B3DHomMatrix::operator*=(const B3DHomMatrix& rMat) 119 { 120 if(rMat.isIdentity()) 121 { 122 // multiply with identity, no change -> nothing to do 123 } 124 else if(isIdentity()) 125 { 126 // we are identity, result will be rMat -> assign 127 *this = rMat; 128 } 129 else 130 { 131 // multiply 132 mpImpl->doMulMatrix(*rMat.mpImpl); 133 } 134 return *this; 135 } 136 operator ==(const B3DHomMatrix & rMat) const137 bool B3DHomMatrix::operator==(const B3DHomMatrix& rMat) const 138 { 139 if(mpImpl.same_object(rMat.mpImpl)) 140 return true; 141 142 return mpImpl->isEqual(*rMat.mpImpl); 143 } 144 operator !=(const B3DHomMatrix & rMat) const145 bool B3DHomMatrix::operator!=(const B3DHomMatrix& rMat) const 146 { 147 return !(*this == rMat); 148 } 149 rotate(double fAngleX,double fAngleY,double fAngleZ)150 void B3DHomMatrix::rotate(double fAngleX,double fAngleY,double fAngleZ) 151 { 152 if(!fTools::equalZero(fAngleX) || !fTools::equalZero(fAngleY) || !fTools::equalZero(fAngleZ)) 153 { 154 if(!fTools::equalZero(fAngleX)) 155 { 156 Impl3DHomMatrix aRotMatX; 157 double fSin(sin(fAngleX)); 158 double fCos(cos(fAngleX)); 159 160 aRotMatX.set(1, 1, fCos); 161 aRotMatX.set(2, 2, fCos); 162 aRotMatX.set(2, 1, fSin); 163 aRotMatX.set(1, 2, -fSin); 164 165 mpImpl->doMulMatrix(aRotMatX); 166 } 167 168 if(!fTools::equalZero(fAngleY)) 169 { 170 Impl3DHomMatrix aRotMatY; 171 double fSin(sin(fAngleY)); 172 double fCos(cos(fAngleY)); 173 174 aRotMatY.set(0, 0, fCos); 175 aRotMatY.set(2, 2, fCos); 176 aRotMatY.set(0, 2, fSin); 177 aRotMatY.set(2, 0, -fSin); 178 179 mpImpl->doMulMatrix(aRotMatY); 180 } 181 182 if(!fTools::equalZero(fAngleZ)) 183 { 184 Impl3DHomMatrix aRotMatZ; 185 double fSin(sin(fAngleZ)); 186 double fCos(cos(fAngleZ)); 187 188 aRotMatZ.set(0, 0, fCos); 189 aRotMatZ.set(1, 1, fCos); 190 aRotMatZ.set(1, 0, fSin); 191 aRotMatZ.set(0, 1, -fSin); 192 193 mpImpl->doMulMatrix(aRotMatZ); 194 } 195 } 196 } 197 rotate(const B3DTuple & rRotation)198 void B3DHomMatrix::rotate(const B3DTuple& rRotation) 199 { 200 rotate(rRotation.getX(), rRotation.getY(), rRotation.getZ()); 201 } 202 translate(double fX,double fY,double fZ)203 void B3DHomMatrix::translate(double fX, double fY, double fZ) 204 { 205 if(!fTools::equalZero(fX) || !fTools::equalZero(fY) || !fTools::equalZero(fZ)) 206 { 207 Impl3DHomMatrix aTransMat; 208 209 aTransMat.set(0, 3, fX); 210 aTransMat.set(1, 3, fY); 211 aTransMat.set(2, 3, fZ); 212 213 mpImpl->doMulMatrix(aTransMat); 214 } 215 } 216 translate(const B3DTuple & rRotation)217 void B3DHomMatrix::translate(const B3DTuple& rRotation) 218 { 219 translate(rRotation.getX(), rRotation.getY(), rRotation.getZ()); 220 } 221 scale(double fX,double fY,double fZ)222 void B3DHomMatrix::scale(double fX, double fY, double fZ) 223 { 224 const double fOne(1.0); 225 226 if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY) ||!fTools::equal(fOne, fZ)) 227 { 228 Impl3DHomMatrix aScaleMat; 229 230 aScaleMat.set(0, 0, fX); 231 aScaleMat.set(1, 1, fY); 232 aScaleMat.set(2, 2, fZ); 233 234 mpImpl->doMulMatrix(aScaleMat); 235 } 236 } 237 scale(const B3DTuple & rRotation)238 void B3DHomMatrix::scale(const B3DTuple& rRotation) 239 { 240 scale(rRotation.getX(), rRotation.getY(), rRotation.getZ()); 241 } 242 shearXY(double fSx,double fSy)243 void B3DHomMatrix::shearXY(double fSx, double fSy) 244 { 245 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!) 246 if(!fTools::equalZero(fSx) || !fTools::equalZero(fSy)) 247 { 248 Impl3DHomMatrix aShearXYMat; 249 250 aShearXYMat.set(0, 2, fSx); 251 aShearXYMat.set(1, 2, fSy); 252 253 mpImpl->doMulMatrix(aShearXYMat); 254 } 255 } 256 shearXZ(double fSx,double fSz)257 void B3DHomMatrix::shearXZ(double fSx, double fSz) 258 { 259 // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!) 260 if(!fTools::equalZero(fSx) || !fTools::equalZero(fSz)) 261 { 262 Impl3DHomMatrix aShearXZMat; 263 264 aShearXZMat.set(0, 1, fSx); 265 aShearXZMat.set(2, 1, fSz); 266 267 mpImpl->doMulMatrix(aShearXZMat); 268 } 269 } frustum(double fLeft,double fRight,double fBottom,double fTop,double fNear,double fFar)270 void B3DHomMatrix::frustum(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar) 271 { 272 const double fZero(0.0); 273 const double fOne(1.0); 274 275 if(!fTools::more(fNear, fZero)) 276 { 277 fNear = 0.001; 278 } 279 280 if(!fTools::more(fFar, fZero)) 281 { 282 fFar = fOne; 283 } 284 285 if(fTools::equal(fNear, fFar)) 286 { 287 fFar = fNear + fOne; 288 } 289 290 if(fTools::equal(fLeft, fRight)) 291 { 292 fLeft -= fOne; 293 fRight += fOne; 294 } 295 296 if(fTools::equal(fTop, fBottom)) 297 { 298 fBottom -= fOne; 299 fTop += fOne; 300 } 301 302 Impl3DHomMatrix aFrustumMat; 303 304 aFrustumMat.set(0, 0, 2.0 * fNear / (fRight - fLeft)); 305 aFrustumMat.set(1, 1, 2.0 * fNear / (fTop - fBottom)); 306 aFrustumMat.set(0, 2, (fRight + fLeft) / (fRight - fLeft)); 307 aFrustumMat.set(1, 2, (fTop + fBottom) / (fTop - fBottom)); 308 aFrustumMat.set(2, 2, -fOne * ((fFar + fNear) / (fFar - fNear))); 309 aFrustumMat.set(3, 2, -fOne); 310 aFrustumMat.set(2, 3, -fOne * ((2.0 * fFar * fNear) / (fFar - fNear))); 311 aFrustumMat.set(3, 3, fZero); 312 313 mpImpl->doMulMatrix(aFrustumMat); 314 } 315 ortho(double fLeft,double fRight,double fBottom,double fTop,double fNear,double fFar)316 void B3DHomMatrix::ortho(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar) 317 { 318 if(fTools::equal(fNear, fFar)) 319 { 320 fFar = fNear + 1.0; 321 } 322 323 if(fTools::equal(fLeft, fRight)) 324 { 325 fLeft -= 1.0; 326 fRight += 1.0; 327 } 328 329 if(fTools::equal(fTop, fBottom)) 330 { 331 fBottom -= 1.0; 332 fTop += 1.0; 333 } 334 335 Impl3DHomMatrix aOrthoMat; 336 337 aOrthoMat.set(0, 0, 2.0 / (fRight - fLeft)); 338 aOrthoMat.set(1, 1, 2.0 / (fTop - fBottom)); 339 aOrthoMat.set(2, 2, -1.0 * (2.0 / (fFar - fNear))); 340 aOrthoMat.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft))); 341 aOrthoMat.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom))); 342 aOrthoMat.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear))); 343 344 mpImpl->doMulMatrix(aOrthoMat); 345 } 346 orientation(const B3DPoint & rVRP,B3DVector aVPN,B3DVector aVUV)347 void B3DHomMatrix::orientation(const B3DPoint& rVRP, B3DVector aVPN, B3DVector aVUV) 348 { 349 Impl3DHomMatrix aOrientationMat; 350 351 // translate -VRP 352 aOrientationMat.set(0, 3, -rVRP.getX()); 353 aOrientationMat.set(1, 3, -rVRP.getY()); 354 aOrientationMat.set(2, 3, -rVRP.getZ()); 355 356 // build rotation 357 aVUV.normalize(); 358 aVPN.normalize(); 359 360 // build x-axis as perpendicular from aVUV and aVPN 361 B3DVector aRx(aVUV.getPerpendicular(aVPN)); 362 aRx.normalize(); 363 364 // y-axis perpendicular to that 365 B3DVector aRy(aVPN.getPerpendicular(aRx)); 366 aRy.normalize(); 367 368 // the calculated normals are the line vectors of the rotation matrix, 369 // set them to create rotation 370 aOrientationMat.set(0, 0, aRx.getX()); 371 aOrientationMat.set(0, 1, aRx.getY()); 372 aOrientationMat.set(0, 2, aRx.getZ()); 373 aOrientationMat.set(1, 0, aRy.getX()); 374 aOrientationMat.set(1, 1, aRy.getY()); 375 aOrientationMat.set(1, 2, aRy.getZ()); 376 aOrientationMat.set(2, 0, aVPN.getX()); 377 aOrientationMat.set(2, 1, aVPN.getY()); 378 aOrientationMat.set(2, 2, aVPN.getZ()); 379 380 mpImpl->doMulMatrix(aOrientationMat); 381 } 382 decompose(B3DTuple & rScale,B3DTuple & rTranslate,B3DTuple & rRotate,B3DTuple & rShear) const383 void B3DHomMatrix::decompose(B3DTuple& rScale, B3DTuple& rTranslate, B3DTuple& rRotate, B3DTuple& rShear) const 384 { 385 // when perspective is used, decompose is not made here 386 if(!mpImpl->isLastLineDefault()) 387 return; 388 389 // If determinant is zero, decomposition is not possible 390 if(determinant() == 0.0) 391 return; 392 393 // isolate translation 394 rTranslate.setX(mpImpl->get(0, 3)); 395 rTranslate.setY(mpImpl->get(1, 3)); 396 rTranslate.setZ(mpImpl->get(2, 3)); 397 398 // correct translate values 399 rTranslate.correctValues(); 400 401 // get scale and shear 402 B3DVector aCol0(mpImpl->get(0, 0), mpImpl->get(1, 0), mpImpl->get(2, 0)); 403 B3DVector aCol1(mpImpl->get(0, 1), mpImpl->get(1, 1), mpImpl->get(2, 1)); 404 B3DVector aCol2(mpImpl->get(0, 2), mpImpl->get(1, 2), mpImpl->get(2, 2)); 405 B3DVector aTemp; 406 407 // get ScaleX 408 rScale.setX(aCol0.getLength()); 409 aCol0.normalize(); 410 411 // get ShearXY 412 rShear.setX(aCol0.scalar(aCol1)); 413 414 if(fTools::equalZero(rShear.getX())) 415 { 416 rShear.setX(0.0); 417 } 418 else 419 { 420 aTemp.setX(aCol1.getX() - rShear.getX() * aCol0.getX()); 421 aTemp.setY(aCol1.getY() - rShear.getX() * aCol0.getY()); 422 aTemp.setZ(aCol1.getZ() - rShear.getX() * aCol0.getZ()); 423 aCol1 = aTemp; 424 } 425 426 // get ScaleY 427 rScale.setY(aCol1.getLength()); 428 aCol1.normalize(); 429 430 const double fShearX(rShear.getX()); 431 432 if(!fTools::equalZero(fShearX)) 433 { 434 rShear.setX(rShear.getX() / rScale.getY()); 435 } 436 437 // get ShearXZ 438 rShear.setY(aCol0.scalar(aCol2)); 439 440 if(fTools::equalZero(rShear.getY())) 441 { 442 rShear.setY(0.0); 443 } 444 else 445 { 446 aTemp.setX(aCol2.getX() - rShear.getY() * aCol0.getX()); 447 aTemp.setY(aCol2.getY() - rShear.getY() * aCol0.getY()); 448 aTemp.setZ(aCol2.getZ() - rShear.getY() * aCol0.getZ()); 449 aCol2 = aTemp; 450 } 451 452 // get ShearYZ 453 rShear.setZ(aCol1.scalar(aCol2)); 454 455 if(fTools::equalZero(rShear.getZ())) 456 { 457 rShear.setZ(0.0); 458 } 459 else 460 { 461 aTemp.setX(aCol2.getX() - rShear.getZ() * aCol1.getX()); 462 aTemp.setY(aCol2.getY() - rShear.getZ() * aCol1.getY()); 463 aTemp.setZ(aCol2.getZ() - rShear.getZ() * aCol1.getZ()); 464 aCol2 = aTemp; 465 } 466 467 // get ScaleZ 468 rScale.setZ(aCol2.getLength()); 469 aCol2.normalize(); 470 471 const double fShearY(rShear.getY()); 472 473 if(!fTools::equalZero(fShearY)) 474 { 475 // coverity[copy_paste_error : FALSE] - this is correct getZ, not getY 476 rShear.setY(rShear.getY() / rScale.getZ()); 477 } 478 479 const double fShearZ(rShear.getZ()); 480 481 if(!fTools::equalZero(fShearZ)) 482 { 483 // coverity[original] - this is not an original copy-and-paste source for ^^^ 484 rShear.setZ(rShear.getZ() / rScale.getZ()); 485 } 486 487 // correct shear values 488 rShear.correctValues(); 489 490 // Coordinate system flip? 491 if(0.0 > aCol0.scalar(aCol1.getPerpendicular(aCol2))) 492 { 493 rScale = -rScale; 494 aCol0 = -aCol0; 495 aCol1 = -aCol1; 496 aCol2 = -aCol2; 497 } 498 499 // correct scale values 500 rScale.correctValues(1.0); 501 502 // Get rotations 503 { 504 double fy=0; 505 double cy=0; 506 507 if( ::basegfx::fTools::equal( aCol0.getZ(), 1.0 ) 508 || aCol0.getZ() > 1.0 ) 509 { 510 fy = -F_PI/2.0; 511 cy = 0.0; 512 } 513 else if( ::basegfx::fTools::equal( aCol0.getZ(), -1.0 ) 514 || aCol0.getZ() < -1.0 ) 515 { 516 fy = F_PI/2.0; 517 cy = 0.0; 518 } 519 else 520 { 521 fy = asin( -aCol0.getZ() ); 522 cy = cos(fy); 523 } 524 525 rRotate.setY(fy); 526 if( ::basegfx::fTools::equalZero( cy ) ) 527 { 528 if( aCol0.getZ() > 0.0 ) 529 rRotate.setX(atan2(-1.0*aCol1.getX(), aCol1.getY())); 530 else 531 rRotate.setX(atan2(aCol1.getX(), aCol1.getY())); 532 rRotate.setZ(0.0); 533 } 534 else 535 { 536 rRotate.setX(atan2(aCol1.getZ(), aCol2.getZ())); 537 rRotate.setZ(atan2(aCol0.getY(), aCol0.getX())); 538 } 539 540 // correct rotate values 541 rRotate.correctValues(); 542 } 543 } 544 } // end of namespace basegfx 545 546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 547