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 <basegfx/polygon/b3dpolygontools.hxx> 28 #include <basegfx/polygon/b3dpolygon.hxx> 29 #include <basegfx/numeric/ftools.hxx> 30 #include <basegfx/range/b3drange.hxx> 31 #include <basegfx/point/b2dpoint.hxx> 32 #include <basegfx/matrix/b3dhommatrix.hxx> 33 #include <basegfx/polygon/b2dpolygon.hxx> 34 #include <basegfx/polygon/b2dpolygontools.hxx> 35 #include <basegfx/tuple/b3ituple.hxx> 36 #include <numeric> 37 38 ////////////////////////////////////////////////////////////////////////////// 39 40 namespace basegfx 41 { 42 namespace tools 43 { 44 // B3DPolygon tools checkClosed(B3DPolygon & rCandidate)45 void checkClosed(B3DPolygon& rCandidate) 46 { 47 while(rCandidate.count() > 1L 48 && rCandidate.getB3DPoint(0L).equal(rCandidate.getB3DPoint(rCandidate.count() - 1L))) 49 { 50 rCandidate.setClosed(true); 51 rCandidate.remove(rCandidate.count() - 1L); 52 } 53 } 54 55 // Get successor and predecessor indices. Returning the same index means there 56 // is none. Same for successor. getIndexOfPredecessor(sal_uInt32 nIndex,const B3DPolygon & rCandidate)57 sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate) 58 { 59 OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); 60 61 if(nIndex) 62 { 63 return nIndex - 1L; 64 } 65 else if(rCandidate.count()) 66 { 67 return rCandidate.count() - 1L; 68 } 69 else 70 { 71 return nIndex; 72 } 73 } 74 getIndexOfSuccessor(sal_uInt32 nIndex,const B3DPolygon & rCandidate)75 sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate) 76 { 77 OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); 78 79 if(nIndex + 1L < rCandidate.count()) 80 { 81 return nIndex + 1L; 82 } 83 else 84 { 85 return 0L; 86 } 87 } 88 getRange(const B3DPolygon & rCandidate)89 B3DRange getRange(const B3DPolygon& rCandidate) 90 { 91 B3DRange aRetval; 92 const sal_uInt32 nPointCount(rCandidate.count()); 93 94 for(sal_uInt32 a(0L); a < nPointCount; a++) 95 { 96 const B3DPoint aTestPoint(rCandidate.getB3DPoint(a)); 97 aRetval.expand(aTestPoint); 98 } 99 100 return aRetval; 101 } 102 getNormal(const B3DPolygon & rCandidate)103 B3DVector getNormal(const B3DPolygon& rCandidate) 104 { 105 return rCandidate.getNormal(); 106 } 107 getPositiveOrientedNormal(const B3DPolygon & rCandidate)108 B3DVector getPositiveOrientedNormal(const B3DPolygon& rCandidate) 109 { 110 B3DVector aRetval(rCandidate.getNormal()); 111 112 if(ORIENTATION_NEGATIVE == getOrientation(rCandidate)) 113 { 114 aRetval = -aRetval; 115 } 116 117 return aRetval; 118 } 119 getOrientation(const B3DPolygon & rCandidate)120 B2VectorOrientation getOrientation(const B3DPolygon& rCandidate) 121 { 122 B2VectorOrientation eRetval(ORIENTATION_NEUTRAL); 123 124 if(rCandidate.count() > 2L) 125 { 126 const double fSignedArea(getSignedArea(rCandidate)); 127 128 if(fSignedArea > 0.0) 129 { 130 eRetval = ORIENTATION_POSITIVE; 131 } 132 else if(fSignedArea < 0.0) 133 { 134 eRetval = ORIENTATION_NEGATIVE; 135 } 136 } 137 138 return eRetval; 139 } 140 getSignedArea(const B3DPolygon & rCandidate)141 double getSignedArea(const B3DPolygon& rCandidate) 142 { 143 double fRetval(0.0); 144 const sal_uInt32 nPointCount(rCandidate.count()); 145 146 if(nPointCount > 2) 147 { 148 const B3DVector aAbsNormal(absolute(getNormal(rCandidate))); 149 sal_uInt16 nCase(3); // default: ignore z 150 151 if(aAbsNormal.getX() > aAbsNormal.getY()) 152 { 153 if(aAbsNormal.getX() > aAbsNormal.getZ()) 154 { 155 nCase = 1; // ignore x 156 } 157 } 158 else if(aAbsNormal.getY() > aAbsNormal.getZ()) 159 { 160 nCase = 2; // ignore y 161 } 162 163 B3DPoint aPreviousPoint(rCandidate.getB3DPoint(nPointCount - 1L)); 164 165 for(sal_uInt32 a(0L); a < nPointCount; a++) 166 { 167 const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a)); 168 169 switch(nCase) 170 { 171 case 1: // ignore x 172 fRetval += aPreviousPoint.getZ() * aCurrentPoint.getY(); 173 fRetval -= aPreviousPoint.getY() * aCurrentPoint.getZ(); 174 break; 175 case 2: // ignore y 176 fRetval += aPreviousPoint.getX() * aCurrentPoint.getZ(); 177 fRetval -= aPreviousPoint.getZ() * aCurrentPoint.getX(); 178 break; 179 case 3: // ignore z 180 fRetval += aPreviousPoint.getX() * aCurrentPoint.getY(); 181 fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX(); 182 break; 183 } 184 185 // prepare next step 186 aPreviousPoint = aCurrentPoint; 187 } 188 189 switch(nCase) 190 { 191 case 1: // ignore x 192 fRetval /= 2.0 * aAbsNormal.getX(); 193 break; 194 case 2: // ignore y 195 fRetval /= 2.0 * aAbsNormal.getY(); 196 break; 197 case 3: // ignore z 198 fRetval /= 2.0 * aAbsNormal.getZ(); 199 break; 200 } 201 } 202 203 return fRetval; 204 } 205 getArea(const B3DPolygon & rCandidate)206 double getArea(const B3DPolygon& rCandidate) 207 { 208 double fRetval(0.0); 209 210 if(rCandidate.count() > 2) 211 { 212 fRetval = getSignedArea(rCandidate); 213 const double fZero(0.0); 214 215 if(fTools::less(fRetval, fZero)) 216 { 217 fRetval = -fRetval; 218 } 219 } 220 221 return fRetval; 222 } 223 getEdgeLength(const B3DPolygon & rCandidate,sal_uInt32 nIndex)224 double getEdgeLength(const B3DPolygon& rCandidate, sal_uInt32 nIndex) 225 { 226 OSL_ENSURE(nIndex < rCandidate.count(), "getEdgeLength: Access to polygon out of range (!)"); 227 double fRetval(0.0); 228 const sal_uInt32 nPointCount(rCandidate.count()); 229 230 if(nIndex < nPointCount) 231 { 232 if(rCandidate.isClosed() || ((nIndex + 1L) != nPointCount)) 233 { 234 const sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate)); 235 const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nIndex)); 236 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); 237 const B3DVector aVector(aNextPoint - aCurrentPoint); 238 fRetval = aVector.getLength(); 239 } 240 } 241 242 return fRetval; 243 } 244 getLength(const B3DPolygon & rCandidate)245 double getLength(const B3DPolygon& rCandidate) 246 { 247 double fRetval(0.0); 248 const sal_uInt32 nPointCount(rCandidate.count()); 249 250 if(nPointCount > 1L) 251 { 252 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 253 254 for(sal_uInt32 a(0L); a < nLoopCount; a++) 255 { 256 const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate)); 257 const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a)); 258 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); 259 const B3DVector aVector(aNextPoint - aCurrentPoint); 260 fRetval += aVector.getLength(); 261 } 262 } 263 264 return fRetval; 265 } 266 getPositionAbsolute(const B3DPolygon & rCandidate,double fDistance,double fLength)267 B3DPoint getPositionAbsolute(const B3DPolygon& rCandidate, double fDistance, double fLength) 268 { 269 B3DPoint aRetval; 270 const sal_uInt32 nPointCount(rCandidate.count()); 271 272 if(nPointCount > 1L) 273 { 274 sal_uInt32 nIndex(0L); 275 bool bIndexDone(false); 276 const double fZero(0.0); 277 double fEdgeLength(fZero); 278 279 // get length if not given 280 if(fTools::equalZero(fLength)) 281 { 282 fLength = getLength(rCandidate); 283 } 284 285 // handle fDistance < 0.0 286 if(fTools::less(fDistance, fZero)) 287 { 288 if(rCandidate.isClosed()) 289 { 290 // if fDistance < 0.0 increment with multiple of fLength 291 sal_uInt32 nCount(sal_uInt32(-fDistance / fLength)); 292 fDistance += double(nCount + 1L) * fLength; 293 } 294 else 295 { 296 // crop to polygon start 297 fDistance = fZero; 298 bIndexDone = true; 299 } 300 } 301 302 // handle fDistance >= fLength 303 if(fTools::moreOrEqual(fDistance, fLength)) 304 { 305 if(rCandidate.isClosed()) 306 { 307 // if fDistance >= fLength decrement with multiple of fLength 308 sal_uInt32 nCount(sal_uInt32(fDistance / fLength)); 309 fDistance -= (double)(nCount) * fLength; 310 } 311 else 312 { 313 // crop to polygon end 314 fDistance = fZero; 315 nIndex = nPointCount - 1L; 316 bIndexDone = true; 317 } 318 } 319 320 // look for correct index. fDistance is now [0.0 .. fLength[ 321 if(!bIndexDone) 322 { 323 do 324 { 325 // get length of next edge 326 fEdgeLength = getEdgeLength(rCandidate, nIndex); 327 328 if(fTools::moreOrEqual(fDistance, fEdgeLength)) 329 { 330 // go to next edge 331 fDistance -= fEdgeLength; 332 nIndex++; 333 } 334 else 335 { 336 // it's on this edge, stop 337 bIndexDone = true; 338 } 339 } while (!bIndexDone); 340 } 341 342 // get the point using nIndex 343 aRetval = rCandidate.getB3DPoint(nIndex); 344 345 // if fDistance != 0.0, move that length on the edge. The edge 346 // length is in fEdgeLength. 347 if(!fTools::equalZero(fDistance)) 348 { 349 sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate)); 350 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); 351 double fRelative(fZero); 352 353 if(!fTools::equalZero(fEdgeLength)) 354 { 355 fRelative = fDistance / fEdgeLength; 356 } 357 358 // add calculated average value to the return value 359 aRetval += interpolate(aRetval, aNextPoint, fRelative); 360 } 361 } 362 363 return aRetval; 364 } 365 getPositionRelative(const B3DPolygon & rCandidate,double fDistance,double fLength)366 B3DPoint getPositionRelative(const B3DPolygon& rCandidate, double fDistance, double fLength) 367 { 368 // get length if not given 369 if(fTools::equalZero(fLength)) 370 { 371 fLength = getLength(rCandidate); 372 } 373 374 // multiply fDistance with real length to get absolute position and 375 // use getPositionAbsolute 376 return getPositionAbsolute(rCandidate, fDistance * fLength, fLength); 377 } 378 applyLineDashing(const B3DPolygon & rCandidate,const::std::vector<double> & rDotDashArray,B3DPolyPolygon * pLineTarget,B3DPolyPolygon * pGapTarget,double fDotDashLength)379 void applyLineDashing(const B3DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fDotDashLength) 380 { 381 const sal_uInt32 nPointCount(rCandidate.count()); 382 const sal_uInt32 nDotDashCount(rDotDashArray.size()); 383 384 if(fTools::lessOrEqual(fDotDashLength, 0.0)) 385 { 386 fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0); 387 } 388 389 if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount) 390 { 391 // clear targets 392 if(pLineTarget) 393 { 394 pLineTarget->clear(); 395 } 396 397 if(pGapTarget) 398 { 399 pGapTarget->clear(); 400 } 401 402 // prepare current edge's start 403 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); 404 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 405 406 // prepare DotDashArray iteration and the line/gap switching bool 407 sal_uInt32 nDotDashIndex(0); 408 bool bIsLine(true); 409 double fDotDashMovingLength(rDotDashArray[0]); 410 B3DPolygon aSnippet; 411 412 // iterate over all edges 413 for(sal_uInt32 a(0); a < nEdgeCount; a++) 414 { 415 // update current edge 416 double fLastDotDashMovingLength(0.0); 417 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 418 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); 419 const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength()); 420 421 if(!fTools::equalZero(fEdgeLength)) 422 { 423 while(fTools::less(fDotDashMovingLength, fEdgeLength)) 424 { 425 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] 426 const bool bHandleLine(bIsLine && pLineTarget); 427 const bool bHandleGap(!bIsLine && pGapTarget); 428 429 if(bHandleLine || bHandleGap) 430 { 431 if(!aSnippet.count()) 432 { 433 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); 434 } 435 436 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); 437 438 if(bHandleLine) 439 { 440 pLineTarget->append(aSnippet); 441 } 442 else 443 { 444 pGapTarget->append(aSnippet); 445 } 446 447 aSnippet.clear(); 448 } 449 450 // prepare next DotDashArray step and flip line/gap flag 451 fLastDotDashMovingLength = fDotDashMovingLength; 452 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; 453 bIsLine = !bIsLine; 454 } 455 456 // append snippet [fLastDotDashMovingLength, fEdgeLength] 457 const bool bHandleLine(bIsLine && pLineTarget); 458 const bool bHandleGap(!bIsLine && pGapTarget); 459 460 if(bHandleLine || bHandleGap) 461 { 462 if(!aSnippet.count()) 463 { 464 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); 465 } 466 467 aSnippet.append(aNextPoint); 468 } 469 470 // prepare move to next edge 471 fDotDashMovingLength -= fEdgeLength; 472 } 473 474 // prepare next edge step (end point gets new start point) 475 aCurrentPoint = aNextPoint; 476 } 477 478 // append last intermediate results (if exists) 479 if(aSnippet.count()) 480 { 481 if(bIsLine && pLineTarget) 482 { 483 pLineTarget->append(aSnippet); 484 } 485 else if(!bIsLine && pGapTarget) 486 { 487 pGapTarget->append(aSnippet); 488 } 489 } 490 491 // check if start and end polygon may be merged 492 if(pLineTarget) 493 { 494 const sal_uInt32 nCount(pLineTarget->count()); 495 496 if(nCount > 1) 497 { 498 // these polygons were created above, there exists none with less than two points, 499 // thus dircet point access below is allowed 500 const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0)); 501 B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1)); 502 503 if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1))) 504 { 505 // start of first and end of last are the same -> merge them 506 aLast.append(aFirst); 507 aLast.removeDoublePoints(); 508 pLineTarget->setB3DPolygon(0, aLast); 509 pLineTarget->remove(nCount - 1); 510 } 511 } 512 } 513 514 if(pGapTarget) 515 { 516 const sal_uInt32 nCount(pGapTarget->count()); 517 518 if(nCount > 1) 519 { 520 // these polygons were created above, there exists none with less than two points, 521 // thus dircet point access below is allowed 522 const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0)); 523 B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1)); 524 525 if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1))) 526 { 527 // start of first and end of last are the same -> merge them 528 aLast.append(aFirst); 529 aLast.removeDoublePoints(); 530 pGapTarget->setB3DPolygon(0, aLast); 531 pGapTarget->remove(nCount - 1); 532 } 533 } 534 } 535 } 536 else 537 { 538 // parameters make no sense, just add source to targets 539 if(pLineTarget) 540 { 541 pLineTarget->append(rCandidate); 542 } 543 544 if(pGapTarget) 545 { 546 pGapTarget->append(rCandidate); 547 } 548 } 549 } 550 applyDefaultNormalsSphere(const B3DPolygon & rCandidate,const B3DPoint & rCenter)551 B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter) 552 { 553 B3DPolygon aRetval(rCandidate); 554 555 for(sal_uInt32 a(0L); a < aRetval.count(); a++) 556 { 557 B3DVector aVector(aRetval.getB3DPoint(a) - rCenter); 558 aVector.normalize(); 559 aRetval.setNormal(a, aVector); 560 } 561 562 return aRetval; 563 } 564 invertNormals(const B3DPolygon & rCandidate)565 B3DPolygon invertNormals( const B3DPolygon& rCandidate) 566 { 567 B3DPolygon aRetval(rCandidate); 568 569 if(aRetval.areNormalsUsed()) 570 { 571 for(sal_uInt32 a(0L); a < aRetval.count(); a++) 572 { 573 aRetval.setNormal(a, -aRetval.getNormal(a)); 574 } 575 } 576 577 return aRetval; 578 } 579 applyDefaultTextureCoordinatesParallel(const B3DPolygon & rCandidate,const B3DRange & rRange,bool bChangeX,bool bChangeY)580 B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY) 581 { 582 B3DPolygon aRetval(rCandidate); 583 584 if(bChangeX || bChangeY) 585 { 586 // create projection of standard texture coordinates in (X, Y) onto 587 // the 3d coordinates straight 588 const double fWidth(rRange.getWidth()); 589 const double fHeight(rRange.getHeight()); 590 const bool bWidthSet(!fTools::equalZero(fWidth)); 591 const bool bHeightSet(!fTools::equalZero(fHeight)); 592 const double fOne(1.0); 593 594 for(sal_uInt32 a(0L); a < aRetval.count(); a++) 595 { 596 const B3DPoint aPoint(aRetval.getB3DPoint(a)); 597 B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a)); 598 599 if(bChangeX) 600 { 601 if(bWidthSet) 602 { 603 aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth); 604 } 605 else 606 { 607 aTextureCoordinate.setX(0.0); 608 } 609 } 610 611 if(bChangeY) 612 { 613 if(bHeightSet) 614 { 615 aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight)); 616 } 617 else 618 { 619 aTextureCoordinate.setY(fOne); 620 } 621 } 622 623 aRetval.setTextureCoordinate(a, aTextureCoordinate); 624 } 625 } 626 627 return aRetval; 628 } 629 applyDefaultTextureCoordinatesSphere(const B3DPolygon & rCandidate,const B3DPoint & rCenter,bool bChangeX,bool bChangeY)630 B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY) 631 { 632 B3DPolygon aRetval(rCandidate); 633 634 if(bChangeX || bChangeY) 635 { 636 // create texture coordinates using sphere projection to cartesian coordinates, 637 // use object's center as base 638 const double fOne(1.0); 639 const sal_uInt32 nPointCount(aRetval.count()); 640 bool bPolarPoints(false); 641 sal_uInt32 a; 642 643 // create center cartesian coordinates to have a possibility to decide if on boundary 644 // transitions which value to choose 645 const B3DRange aPlaneRange(getRange(rCandidate)); 646 const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter); 647 const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI)); 648 649 for(a = 0L; a < nPointCount; a++) 650 { 651 const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter); 652 const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI)); 653 B2DPoint aTexCoor(aRetval.getTextureCoordinate(a)); 654 655 if(fTools::equalZero(fY)) 656 { 657 // point is a north polar point, no useful X-coordinate can be created. 658 if(bChangeY) 659 { 660 aTexCoor.setY(0.0); 661 662 if(bChangeX) 663 { 664 bPolarPoints = true; 665 } 666 } 667 } 668 else if(fTools::equal(fY, fOne)) 669 { 670 // point is a south polar point, no useful X-coordinate can be created. Set 671 // Y-coordinte, though 672 if(bChangeY) 673 { 674 aTexCoor.setY(fOne); 675 676 if(bChangeX) 677 { 678 bPolarPoints = true; 679 } 680 } 681 } 682 else 683 { 684 double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI)); 685 686 // correct cartesinan point coordiante dependent from center value 687 if(fX > fXCenter + 0.5) 688 { 689 fX -= fOne; 690 } 691 else if(fX < fXCenter - 0.5) 692 { 693 fX += fOne; 694 } 695 696 if(bChangeX) 697 { 698 aTexCoor.setX(fX); 699 } 700 701 if(bChangeY) 702 { 703 aTexCoor.setY(fY); 704 } 705 } 706 707 aRetval.setTextureCoordinate(a, aTexCoor); 708 } 709 710 if(bPolarPoints) 711 { 712 // correct X-texture coordinates if polar points are contained. Those 713 // coordinates cannot be correct, so use prev or next X-coordinate 714 for(a = 0L; a < nPointCount; a++) 715 { 716 B2DPoint aTexCoor(aRetval.getTextureCoordinate(a)); 717 718 if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne)) 719 { 720 // get prev, next TexCoor and test for pole 721 const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L)); 722 const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount)); 723 const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne)); 724 const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne)); 725 726 if(!bPrevPole && !bNextPole) 727 { 728 // both no poles, mix them 729 aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0); 730 } 731 else if(!bNextPole) 732 { 733 // copy next 734 aTexCoor.setX(aNextTexCoor.getX()); 735 } 736 else 737 { 738 // copy prev, even if it's a pole, hopefully it is already corrected 739 aTexCoor.setX(aPrevTexCoor.getX()); 740 } 741 742 aRetval.setTextureCoordinate(a, aTexCoor); 743 } 744 } 745 } 746 } 747 748 return aRetval; 749 } 750 isInEpsilonRange(const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,const B3DPoint & rTestPosition,double fDistance)751 bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance) 752 { 753 // build edge vector 754 const B3DVector aEdge(rEdgeEnd - rEdgeStart); 755 bool bDoDistanceTestStart(false); 756 bool bDoDistanceTestEnd(false); 757 758 if(aEdge.equalZero()) 759 { 760 // no edge, just a point. Do one of the distance tests. 761 bDoDistanceTestStart = true; 762 } 763 else 764 { 765 // calculate fCut in aEdge 766 const B3DVector aTestEdge(rTestPosition - rEdgeStart); 767 const double fScalarTestEdge(aEdge.scalar(aTestEdge)); 768 const double fScalarStartEdge(aEdge.scalar(rEdgeStart)); 769 const double fScalarEdge(aEdge.scalar(aEdge)); 770 const double fCut((fScalarTestEdge - fScalarStartEdge) / fScalarEdge); 771 const double fZero(0.0); 772 const double fOne(1.0); 773 774 if(fTools::less(fCut, fZero)) 775 { 776 // left of rEdgeStart 777 bDoDistanceTestStart = true; 778 } 779 else if(fTools::more(fCut, fOne)) 780 { 781 // right of rEdgeEnd 782 bDoDistanceTestEnd = true; 783 } 784 else 785 { 786 // inside line [0.0 .. 1.0] 787 const B3DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut)); 788 const B3DVector aDelta(rTestPosition - aCutPoint); 789 const double fDistanceSquare(aDelta.scalar(aDelta)); 790 791 if(fDistanceSquare <= fDistance * fDistance * fDistance) 792 { 793 return true; 794 } 795 else 796 { 797 return false; 798 } 799 } 800 } 801 802 if(bDoDistanceTestStart) 803 { 804 const B3DVector aDelta(rTestPosition - rEdgeStart); 805 const double fDistanceSquare(aDelta.scalar(aDelta)); 806 807 if(fDistanceSquare <= fDistance * fDistance * fDistance) 808 { 809 return true; 810 } 811 } 812 else if(bDoDistanceTestEnd) 813 { 814 const B3DVector aDelta(rTestPosition - rEdgeEnd); 815 const double fDistanceSquare(aDelta.scalar(aDelta)); 816 817 if(fDistanceSquare <= fDistance * fDistance * fDistance) 818 { 819 return true; 820 } 821 } 822 823 return false; 824 } 825 isInEpsilonRange(const B3DPolygon & rCandidate,const B3DPoint & rTestPosition,double fDistance)826 bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance) 827 { 828 const sal_uInt32 nPointCount(rCandidate.count()); 829 830 if(nPointCount) 831 { 832 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 833 B3DPoint aCurrent(rCandidate.getB3DPoint(0)); 834 835 if(nEdgeCount) 836 { 837 // edges 838 for(sal_uInt32 a(0); a < nEdgeCount; a++) 839 { 840 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 841 const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex)); 842 843 if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance)) 844 { 845 return true; 846 } 847 848 // prepare next step 849 aCurrent = aNext; 850 } 851 } 852 else 853 { 854 // no edges, but points -> not closed. Check single point. Just 855 // use isInEpsilonRange with twice the same point, it handles those well 856 if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance)) 857 { 858 return true; 859 } 860 } 861 } 862 863 return false; 864 } 865 isInside(const B3DPolygon & rCandidate,const B3DPoint & rPoint,bool bWithBorder)866 bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder) 867 { 868 if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true)) 869 { 870 return true; 871 } 872 else 873 { 874 bool bRetval(false); 875 const B3DVector aPlaneNormal(rCandidate.getNormal()); 876 877 if(!aPlaneNormal.equalZero()) 878 { 879 const sal_uInt32 nPointCount(rCandidate.count()); 880 881 if(nPointCount) 882 { 883 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1)); 884 const double fAbsX(fabs(aPlaneNormal.getX())); 885 const double fAbsY(fabs(aPlaneNormal.getY())); 886 const double fAbsZ(fabs(aPlaneNormal.getZ())); 887 888 if(fAbsX > fAbsY && fAbsX > fAbsZ) 889 { 890 // normal points mostly in X-Direction, use YZ-Polygon projection for check 891 // x -> y, y -> z 892 for(sal_uInt32 a(0); a < nPointCount; a++) 893 { 894 const B3DPoint aPreviousPoint(aCurrentPoint); 895 aCurrentPoint = rCandidate.getB3DPoint(a); 896 897 // cross-over in Z? 898 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ())); 899 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ())); 900 901 if(bCompZA != bCompZB) 902 { 903 // cross-over in Y? 904 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); 905 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); 906 907 if(bCompYA == bCompYB) 908 { 909 if(bCompYA) 910 { 911 bRetval = !bRetval; 912 } 913 } 914 else 915 { 916 const double fCompare( 917 aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) * 918 (aPreviousPoint.getY() - aCurrentPoint.getY()) / 919 (aPreviousPoint.getZ() - aCurrentPoint.getZ())); 920 921 if(fTools::more(fCompare, rPoint.getY())) 922 { 923 bRetval = !bRetval; 924 } 925 } 926 } 927 } 928 } 929 else if(fAbsY > fAbsX && fAbsY > fAbsZ) 930 { 931 // normal points mostly in Y-Direction, use XZ-Polygon projection for check 932 // x -> x, y -> z 933 for(sal_uInt32 a(0); a < nPointCount; a++) 934 { 935 const B3DPoint aPreviousPoint(aCurrentPoint); 936 aCurrentPoint = rCandidate.getB3DPoint(a); 937 938 // cross-over in Z? 939 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ())); 940 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ())); 941 942 if(bCompZA != bCompZB) 943 { 944 // cross-over in X? 945 const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); 946 const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); 947 948 if(bCompXA == bCompXB) 949 { 950 if(bCompXA) 951 { 952 bRetval = !bRetval; 953 } 954 } 955 else 956 { 957 const double fCompare( 958 aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) * 959 (aPreviousPoint.getX() - aCurrentPoint.getX()) / 960 (aPreviousPoint.getZ() - aCurrentPoint.getZ())); 961 962 if(fTools::more(fCompare, rPoint.getX())) 963 { 964 bRetval = !bRetval; 965 } 966 } 967 } 968 } 969 } 970 else 971 { 972 // normal points mostly in Z-Direction, use XY-Polygon projection for check 973 // x -> x, y -> y 974 for(sal_uInt32 a(0); a < nPointCount; a++) 975 { 976 const B3DPoint aPreviousPoint(aCurrentPoint); 977 aCurrentPoint = rCandidate.getB3DPoint(a); 978 979 // cross-over in Y? 980 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); 981 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); 982 983 if(bCompYA != bCompYB) 984 { 985 // cross-over in X? 986 const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); 987 const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); 988 989 if(bCompXA == bCompXB) 990 { 991 if(bCompXA) 992 { 993 bRetval = !bRetval; 994 } 995 } 996 else 997 { 998 const double fCompare( 999 aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) * 1000 (aPreviousPoint.getX() - aCurrentPoint.getX()) / 1001 (aPreviousPoint.getY() - aCurrentPoint.getY())); 1002 1003 if(fTools::more(fCompare, rPoint.getX())) 1004 { 1005 bRetval = !bRetval; 1006 } 1007 } 1008 } 1009 } 1010 } 1011 } 1012 } 1013 1014 return bRetval; 1015 } 1016 } 1017 isInside(const B3DPolygon & rCandidate,const B3DPolygon & rPolygon,bool bWithBorder)1018 bool isInside(const B3DPolygon& rCandidate, const B3DPolygon& rPolygon, bool bWithBorder) 1019 { 1020 const sal_uInt32 nPointCount(rPolygon.count()); 1021 1022 for(sal_uInt32 a(0L); a < nPointCount; a++) 1023 { 1024 const B3DPoint aTestPoint(rPolygon.getB3DPoint(a)); 1025 1026 if(!isInside(rCandidate, aTestPoint, bWithBorder)) 1027 { 1028 return false; 1029 } 1030 } 1031 1032 return true; 1033 } 1034 isPointOnLine(const B3DPoint & rStart,const B3DPoint & rEnd,const B3DPoint & rCandidate,bool bWithPoints)1035 bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints) 1036 { 1037 if(rCandidate.equal(rStart) || rCandidate.equal(rEnd)) 1038 { 1039 // candidate is in epsilon around start or end -> inside 1040 return bWithPoints; 1041 } 1042 else if(rStart.equal(rEnd)) 1043 { 1044 // start and end are equal, but candidate is outside their epsilon -> outside 1045 return false; 1046 } 1047 else 1048 { 1049 const B3DVector aEdgeVector(rEnd - rStart); 1050 const B3DVector aTestVector(rCandidate - rStart); 1051 1052 if(areParallel(aEdgeVector, aTestVector)) 1053 { 1054 const double fZero(0.0); 1055 const double fOne(1.0); 1056 double fParamTestOnCurr(0.0); 1057 1058 if(aEdgeVector.getX() > aEdgeVector.getY()) 1059 { 1060 if(aEdgeVector.getX() > aEdgeVector.getZ()) 1061 { 1062 // X is biggest 1063 fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX(); 1064 } 1065 else 1066 { 1067 // Z is biggest 1068 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ(); 1069 } 1070 } 1071 else 1072 { 1073 if(aEdgeVector.getY() > aEdgeVector.getZ()) 1074 { 1075 // Y is biggest 1076 fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY(); 1077 } 1078 else 1079 { 1080 // Z is biggest 1081 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ(); 1082 } 1083 } 1084 1085 if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne)) 1086 { 1087 return true; 1088 } 1089 } 1090 1091 return false; 1092 } 1093 } 1094 isPointOnPolygon(const B3DPolygon & rCandidate,const B3DPoint & rPoint,bool bWithPoints)1095 bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints) 1096 { 1097 const sal_uInt32 nPointCount(rCandidate.count()); 1098 1099 if(nPointCount > 1L) 1100 { 1101 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 1102 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); 1103 1104 for(sal_uInt32 a(0); a < nLoopCount; a++) 1105 { 1106 const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount)); 1107 1108 if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints)) 1109 { 1110 return true; 1111 } 1112 1113 aCurrentPoint = aNextPoint; 1114 } 1115 } 1116 else if(nPointCount && bWithPoints) 1117 { 1118 return rPoint.equal(rCandidate.getB3DPoint(0)); 1119 } 1120 1121 return false; 1122 } 1123 getCutBetweenLineAndPlane(const B3DVector & rPlaneNormal,const B3DPoint & rPlanePoint,const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,double & fCut)1124 bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut) 1125 { 1126 if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd)) 1127 { 1128 const B3DVector aTestEdge(rEdgeEnd - rEdgeStart); 1129 const double fScalarEdge(rPlaneNormal.scalar(aTestEdge)); 1130 1131 if(!fTools::equalZero(fScalarEdge)) 1132 { 1133 const B3DVector aCompareEdge(rPlanePoint - rEdgeStart); 1134 const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge)); 1135 1136 fCut = fScalarCompare / fScalarEdge; 1137 return true; 1138 } 1139 } 1140 1141 return false; 1142 } 1143 getCutBetweenLineAndPolygon(const B3DPolygon & rCandidate,const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,double & fCut)1144 bool getCutBetweenLineAndPolygon(const B3DPolygon& rCandidate, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut) 1145 { 1146 const sal_uInt32 nPointCount(rCandidate.count()); 1147 1148 if(nPointCount > 2 && !rEdgeStart.equal(rEdgeEnd)) 1149 { 1150 const B3DVector aPlaneNormal(rCandidate.getNormal()); 1151 1152 if(!aPlaneNormal.equalZero()) 1153 { 1154 const B3DPoint aPointOnPlane(rCandidate.getB3DPoint(0)); 1155 1156 return getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, rEdgeStart, rEdgeEnd, fCut); 1157 } 1158 } 1159 1160 return false; 1161 } 1162 1163 ////////////////////////////////////////////////////////////////////// 1164 // comparators with tolerance for 3D Polygons 1165 equal(const B3DPolygon & rCandidateA,const B3DPolygon & rCandidateB,const double & rfSmallValue)1166 bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue) 1167 { 1168 const sal_uInt32 nPointCount(rCandidateA.count()); 1169 1170 if(nPointCount != rCandidateB.count()) 1171 return false; 1172 1173 const bool bClosed(rCandidateA.isClosed()); 1174 1175 if(bClosed != rCandidateB.isClosed()) 1176 return false; 1177 1178 for(sal_uInt32 a(0); a < nPointCount; a++) 1179 { 1180 const B3DPoint aPoint(rCandidateA.getB3DPoint(a)); 1181 1182 if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue)) 1183 return false; 1184 } 1185 1186 return true; 1187 } 1188 equal(const B3DPolygon & rCandidateA,const B3DPolygon & rCandidateB)1189 bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB) 1190 { 1191 const double fSmallValue(fTools::getSmallValue()); 1192 1193 return equal(rCandidateA, rCandidateB, fSmallValue); 1194 } 1195 1196 // snap points of horizontal or vertical edges to discrete values snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon & rCandidate)1197 B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate) 1198 { 1199 const sal_uInt32 nPointCount(rCandidate.count()); 1200 1201 if(nPointCount > 1) 1202 { 1203 // Start by copying the source polygon to get a writeable copy. The closed state is 1204 // copied by aRetval's initialisation, too, so no need to copy it in this method 1205 B3DPolygon aRetval(rCandidate); 1206 1207 // prepare geometry data. Get rounded from original 1208 B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1))); 1209 B3DPoint aCurrPoint(rCandidate.getB3DPoint(0)); 1210 B3ITuple aCurrTuple(basegfx::fround(aCurrPoint)); 1211 1212 // loop over all points. This will also snap the implicit closing edge 1213 // even when not closed, but that's no problem here 1214 for(sal_uInt32 a(0); a < nPointCount; a++) 1215 { 1216 // get next point. Get rounded from original 1217 const bool bLastRun(a + 1 == nPointCount); 1218 const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1); 1219 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); 1220 const B3ITuple aNextTuple(basegfx::fround(aNextPoint)); 1221 1222 // get the states 1223 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX()); 1224 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX()); 1225 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY()); 1226 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY()); 1227 const bool bSnapX(bPrevVertical || bNextVertical); 1228 const bool bSnapY(bPrevHorizontal || bNextHorizontal); 1229 1230 if(bSnapX || bSnapY) 1231 { 1232 const B3DPoint aSnappedPoint( 1233 bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(), 1234 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(), 1235 aCurrPoint.getZ()); 1236 1237 aRetval.setB3DPoint(a, aSnappedPoint); 1238 } 1239 1240 // prepare next point 1241 if(!bLastRun) 1242 { 1243 aPrevTuple = aCurrTuple; 1244 aCurrPoint = aNextPoint; 1245 aCurrTuple = aNextTuple; 1246 } 1247 } 1248 1249 return aRetval; 1250 } 1251 else 1252 { 1253 return rCandidate; 1254 } 1255 } 1256 1257 } // end of namespace tools 1258 } // end of namespace basegfx 1259 1260 ////////////////////////////////////////////////////////////////////////////// 1261 1262 // eof 1263