00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "precompiled_basegfx.hxx"
00030 #include <basegfx/curve/b2dcubicbezier.hxx>
00031 #include <basegfx/vector/b2dvector.hxx>
00032 #include <basegfx/polygon/b2dpolygon.hxx>
00033 #include <basegfx/numeric/ftools.hxx>
00034
00035 #include <limits>
00036
00037
00038 #define FACTOR_FOR_UNSHARPEN (1.6)
00039 #ifdef DBG_UTIL
00040 static double fMultFactUnsharpen = FACTOR_FOR_UNSHARPEN;
00041 #endif
00042
00044
00045 namespace basegfx
00046 {
00047 namespace
00048 {
00049 void ImpSubDivAngle(
00050 const B2DPoint& rfPA,
00051 const B2DPoint& rfEA,
00052 const B2DPoint& rfEB,
00053 const B2DPoint& rfPB,
00054 B2DPolygon& rTarget,
00055 double fAngleBound,
00056 bool bAllowUnsharpen,
00057 sal_uInt16 nMaxRecursionDepth)
00058 {
00059 if(nMaxRecursionDepth)
00060 {
00061
00062 B2DVector aLeft(rfEA - rfPA);
00063 B2DVector aRight(rfEB - rfPB);
00064
00065
00066 if(aLeft.equalZero())
00067 {
00068 aLeft = rfEB - rfPA;
00069 }
00070
00071 if(aRight.equalZero())
00072 {
00073 aRight = rfEA - rfPB;
00074 }
00075
00076 const double fCurrentAngle(aLeft.angle(aRight));
00077
00078 if(fabs(fCurrentAngle) > (F_PI - fAngleBound))
00079 {
00080
00081 nMaxRecursionDepth = 0;
00082 }
00083 else
00084 {
00085 if(bAllowUnsharpen)
00086 {
00087
00088 #ifdef DBG_UTIL
00089 fAngleBound *= fMultFactUnsharpen;
00090 #else
00091 fAngleBound *= FACTOR_FOR_UNSHARPEN;
00092 #endif
00093 }
00094 }
00095 }
00096
00097 if(nMaxRecursionDepth)
00098 {
00099
00100 const B2DPoint aS1L(average(rfPA, rfEA));
00101 const B2DPoint aS1C(average(rfEA, rfEB));
00102 const B2DPoint aS1R(average(rfEB, rfPB));
00103 const B2DPoint aS2L(average(aS1L, aS1C));
00104 const B2DPoint aS2R(average(aS1C, aS1R));
00105 const B2DPoint aS3C(average(aS2L, aS2R));
00106
00107
00108 ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
00109
00110
00111 ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
00112 }
00113 else
00114 {
00115 rTarget.append(rfPB);
00116 }
00117 }
00118
00119 void ImpSubDivAngleStart(
00120 const B2DPoint& rfPA,
00121 const B2DPoint& rfEA,
00122 const B2DPoint& rfEB,
00123 const B2DPoint& rfPB,
00124 B2DPolygon& rTarget,
00125 const double& rfAngleBound,
00126 bool bAllowUnsharpen)
00127 {
00128 sal_uInt16 nMaxRecursionDepth(8);
00129 const B2DVector aLeft(rfEA - rfPA);
00130 const B2DVector aRight(rfEB - rfPB);
00131 bool bLeftEqualZero(aLeft.equalZero());
00132 bool bRightEqualZero(aRight.equalZero());
00133 bool bAllParallel(false);
00134
00135 if(bLeftEqualZero && bRightEqualZero)
00136 {
00137 nMaxRecursionDepth = 0;
00138 }
00139 else
00140 {
00141 const B2DVector aBase(rfPB - rfPA);
00142 const bool bBaseEqualZero(aBase.equalZero());
00143
00144 if(!bBaseEqualZero)
00145 {
00146 const bool bLeftParallel(bLeftEqualZero ? true : areParallel(aLeft, aBase));
00147 const bool bRightParallel(bRightEqualZero ? true : areParallel(aRight, aBase));
00148
00149 if(bLeftParallel && bRightParallel)
00150 {
00151 bAllParallel = true;
00152
00153 if(!bLeftEqualZero)
00154 {
00155 double fFactor;
00156
00157 if(fabs(aBase.getX()) > fabs(aBase.getY()))
00158 {
00159 fFactor = aLeft.getX() / aBase.getX();
00160 }
00161 else
00162 {
00163 fFactor = aLeft.getY() / aBase.getY();
00164 }
00165
00166 if(fFactor >= 0.0 && fFactor <= 1.0)
00167 {
00168 bLeftEqualZero = true;
00169 }
00170 }
00171
00172 if(!bRightEqualZero)
00173 {
00174 double fFactor;
00175
00176 if(fabs(aBase.getX()) > fabs(aBase.getY()))
00177 {
00178 fFactor = aRight.getX() / -aBase.getX();
00179 }
00180 else
00181 {
00182 fFactor = aRight.getY() / -aBase.getY();
00183 }
00184
00185 if(fFactor >= 0.0 && fFactor <= 1.0)
00186 {
00187 bRightEqualZero = true;
00188 }
00189 }
00190
00191 if(bLeftEqualZero && bRightEqualZero)
00192 {
00193 nMaxRecursionDepth = 0;
00194 }
00195 }
00196 }
00197 }
00198
00199 if(nMaxRecursionDepth)
00200 {
00201
00202 const B2DPoint aS1L(average(rfPA, rfEA));
00203 const B2DPoint aS1C(average(rfEA, rfEB));
00204 const B2DPoint aS1R(average(rfEB, rfPB));
00205 const B2DPoint aS2L(average(aS1L, aS1C));
00206 const B2DPoint aS2R(average(aS1C, aS1R));
00207 const B2DPoint aS3C(average(aS2L, aS2R));
00208
00209
00210 bool bAngleIsSmallerLeft(bAllParallel && bLeftEqualZero);
00211 if(!bAngleIsSmallerLeft)
00212 {
00213 const B2DVector aLeftLeft(bLeftEqualZero ? aS2L - aS1L : aS1L - rfPA);
00214 const B2DVector aRightLeft(aS2L - aS3C);
00215 const double fCurrentAngleLeft(aLeftLeft.angle(aRightLeft));
00216 bAngleIsSmallerLeft = (fabs(fCurrentAngleLeft) > (F_PI - rfAngleBound));
00217 }
00218
00219
00220 bool bAngleIsSmallerRight(bAllParallel && bRightEqualZero);
00221 if(!bAngleIsSmallerRight)
00222 {
00223 const B2DVector aLeftRight(aS2R - aS3C);
00224 const B2DVector aRightRight(bRightEqualZero ? aS2R - aS1R : aS1R - rfPB);
00225 const double fCurrentAngleRight(aLeftRight.angle(aRightRight));
00226 bAngleIsSmallerRight = (fabs(fCurrentAngleRight) > (F_PI - rfAngleBound));
00227 }
00228
00229 if(bAngleIsSmallerLeft && bAngleIsSmallerRight)
00230 {
00231
00232 nMaxRecursionDepth = 0;
00233 }
00234 else
00235 {
00236
00237 if(bAngleIsSmallerLeft)
00238 {
00239 rTarget.append(aS3C);
00240 }
00241 else
00242 {
00243 ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
00244 }
00245
00246
00247 if(bAngleIsSmallerRight)
00248 {
00249 rTarget.append(rfPB);
00250 }
00251 else
00252 {
00253 ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
00254 }
00255 }
00256 }
00257
00258 if(!nMaxRecursionDepth)
00259 {
00260 rTarget.append(rfPB);
00261 }
00262 }
00263
00264 void ImpSubDivDistance(
00265 const B2DPoint& rfPA,
00266 const B2DPoint& rfEA,
00267 const B2DPoint& rfEB,
00268 const B2DPoint& rfPB,
00269 B2DPolygon& rTarget,
00270 double fDistanceBound2,
00271 double fLastDistanceError2,
00272 sal_uInt16 nMaxRecursionDepth)
00273 {
00274 if(nMaxRecursionDepth)
00275 {
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289 const double fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX()));
00290 const double fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY()));
00291 const double fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX()));
00292 const double fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY()));
00293 const double fDistanceError2(::std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y));
00294
00295
00296
00297
00298 const bool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2);
00299
00300 if(bFurtherDivision)
00301 {
00302
00303 fLastDistanceError2 = fDistanceError2;
00304 }
00305 else
00306 {
00307
00308 nMaxRecursionDepth = 0;
00309 }
00310 }
00311
00312 if(nMaxRecursionDepth)
00313 {
00314
00315 const B2DPoint aS1L(average(rfPA, rfEA));
00316 const B2DPoint aS1C(average(rfEA, rfEB));
00317 const B2DPoint aS1R(average(rfEB, rfPB));
00318 const B2DPoint aS2L(average(aS1L, aS1C));
00319 const B2DPoint aS2R(average(aS1C, aS1R));
00320 const B2DPoint aS3C(average(aS2L, aS2R));
00321
00322
00323 ImpSubDivDistance(rfPA, aS1L, aS2L, aS3C, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
00324
00325
00326 ImpSubDivDistance(aS3C, aS2R, aS1R, rfPB, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
00327 }
00328 else
00329 {
00330 rTarget.append(rfPB);
00331 }
00332 }
00333 }
00334 }
00335
00337
00338 namespace basegfx
00339 {
00340 B2DCubicBezier::B2DCubicBezier(const B2DCubicBezier& rBezier)
00341 : maStartPoint(rBezier.maStartPoint),
00342 maEndPoint(rBezier.maEndPoint),
00343 maControlPointA(rBezier.maControlPointA),
00344 maControlPointB(rBezier.maControlPointB)
00345 {
00346 }
00347
00348 B2DCubicBezier::B2DCubicBezier()
00349 {
00350 }
00351
00352 B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rEnd)
00353 : maStartPoint(rStart),
00354 maEndPoint(rEnd),
00355 maControlPointA(rStart),
00356 maControlPointB(rEnd)
00357 {
00358 }
00359
00360 B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd)
00361 : maStartPoint(rStart),
00362 maEndPoint(rEnd),
00363 maControlPointA(rControlPointA),
00364 maControlPointB(rControlPointB)
00365 {
00366 }
00367
00368 B2DCubicBezier::~B2DCubicBezier()
00369 {
00370 }
00371
00372
00373 B2DCubicBezier& B2DCubicBezier::operator=(const B2DCubicBezier& rBezier)
00374 {
00375 maStartPoint = rBezier.maStartPoint;
00376 maEndPoint = rBezier.maEndPoint;
00377 maControlPointA = rBezier.maControlPointA;
00378 maControlPointB = rBezier.maControlPointB;
00379
00380 return *this;
00381 }
00382
00383
00384 bool B2DCubicBezier::operator==(const B2DCubicBezier& rBezier) const
00385 {
00386 return (
00387 maStartPoint == rBezier.maStartPoint
00388 && maEndPoint == rBezier.maEndPoint
00389 && maControlPointA == rBezier.maControlPointA
00390 && maControlPointB == rBezier.maControlPointB
00391 );
00392 }
00393
00394 bool B2DCubicBezier::operator!=(const B2DCubicBezier& rBezier) const
00395 {
00396 return (
00397 maStartPoint != rBezier.maStartPoint
00398 || maEndPoint != rBezier.maEndPoint
00399 || maControlPointA != rBezier.maControlPointA
00400 || maControlPointB != rBezier.maControlPointB
00401 );
00402 }
00403
00404 bool B2DCubicBezier::equal(const B2DCubicBezier& rBezier) const
00405 {
00406 return (
00407 maStartPoint.equal(rBezier.maStartPoint)
00408 && maEndPoint.equal(rBezier.maEndPoint)
00409 && maControlPointA.equal(rBezier.maControlPointA)
00410 && maControlPointB.equal(rBezier.maControlPointB)
00411 );
00412 }
00413
00414
00415 bool B2DCubicBezier::isBezier() const
00416 {
00417 if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
00418 {
00419 return true;
00420 }
00421
00422 return false;
00423 }
00424
00425 void B2DCubicBezier::testAndSolveTrivialBezier()
00426 {
00427 if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
00428 {
00429 const B2DVector aEdge(maEndPoint - maStartPoint);
00430
00431
00432
00433 if(!aEdge.equalZero())
00434 {
00435
00436 const B2DVector aVecA(maControlPointA - maStartPoint);
00437 const B2DVector aVecB(maControlPointB - maEndPoint);
00438
00439
00440 bool bAIsTrivial(aVecA.equalZero());
00441 bool bBIsTrivial(aVecB.equalZero());
00442
00443
00444
00445
00446
00447
00448
00449
00450 const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial
00451 ? 1.0
00452 : 1.0 / aEdge.getLength());
00453
00454
00455 if(!bAIsTrivial)
00456 {
00457
00458
00459 const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength);
00460
00461 if(fTools::equalZero(fCross))
00462 {
00463
00464 const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
00465 ? aVecA.getX() / aEdge.getX()
00466 : aVecA.getY() / aEdge.getY());
00467
00468
00469 if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0))
00470 {
00471 bAIsTrivial = true;
00472 }
00473 }
00474 }
00475
00476
00477
00478 if(bAIsTrivial && !bBIsTrivial)
00479 {
00480
00481 const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength);
00482
00483 if(fTools::equalZero(fCross))
00484 {
00485
00486 const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
00487 ? aVecB.getX() / aEdge.getX()
00488 : aVecB.getY() / aEdge.getY());
00489
00490
00491 if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0))
00492 {
00493 bBIsTrivial = true;
00494 }
00495 }
00496 }
00497
00498
00499
00500 if(bAIsTrivial && bBIsTrivial)
00501 {
00502 maControlPointA = maStartPoint;
00503 maControlPointB = maEndPoint;
00504 }
00505 }
00506 }
00507 }
00508
00509 namespace {
00510 double impGetLength(const B2DCubicBezier& rEdge, double fDeviation, sal_uInt32 nRecursionWatch)
00511 {
00512 const double fEdgeLength(rEdge.getEdgeLength());
00513 const double fControlPolygonLength(rEdge.getControlPolygonLength());
00514 const double fCurrentDeviation(fTools::equalZero(fControlPolygonLength) ? 0.0 : 1.0 - (fEdgeLength / fControlPolygonLength));
00515
00516 if(!nRecursionWatch || fTools:: lessOrEqual(fCurrentDeviation, fDeviation))
00517 {
00518 return (fEdgeLength + fControlPolygonLength) * 0.5;
00519 }
00520 else
00521 {
00522 B2DCubicBezier aLeft, aRight;
00523 const double fNewDeviation(fDeviation * 0.5);
00524 const sal_uInt32 nNewRecursionWatch(nRecursionWatch - 1);
00525
00526 rEdge.split(0.5, &aLeft, &aRight);
00527
00528 return impGetLength(aLeft, fNewDeviation, nNewRecursionWatch)
00529 + impGetLength(aRight, fNewDeviation, nNewRecursionWatch);
00530 }
00531 }
00532 }
00533
00534 double B2DCubicBezier::getLength(double fDeviation) const
00535 {
00536 if(isBezier())
00537 {
00538 if(fDeviation < 0.00000001)
00539 {
00540 fDeviation = 0.00000001;
00541 }
00542
00543 return impGetLength(*this, fDeviation, 6);
00544 }
00545 else
00546 {
00547 return B2DVector(getEndPoint() - getStartPoint()).getLength();
00548 }
00549 }
00550
00551 double B2DCubicBezier::getEdgeLength() const
00552 {
00553 const B2DVector aEdge(maEndPoint - maStartPoint);
00554 return aEdge.getLength();
00555 }
00556
00557 double B2DCubicBezier::getControlPolygonLength() const
00558 {
00559 const B2DVector aVectorA(maControlPointA - maStartPoint);
00560 const B2DVector aVectorB(maEndPoint - maControlPointB);
00561
00562 if(!aVectorA.equalZero() || !aVectorB.equalZero())
00563 {
00564 const B2DVector aTop(maControlPointB - maControlPointA);
00565 return (aVectorA.getLength() + aVectorB.getLength() + aTop.getLength());
00566 }
00567 else
00568 {
00569 return getEdgeLength();
00570 }
00571 }
00572
00573 void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const
00574 {
00575 if(isBezier())
00576 {
00577
00578 ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, fAngleBound * F_PI180, bAllowUnsharpen);
00579 }
00580 else
00581 {
00582 rTarget.append(getEndPoint());
00583 }
00584 }
00585
00586 B2DVector B2DCubicBezier::getTangent(double t) const
00587 {
00588 if(fTools::lessOrEqual(t, 0.0))
00589 {
00590
00591 B2DVector aTangent(getControlPointA() - getStartPoint());
00592
00593 if(!aTangent.equalZero())
00594 {
00595 return aTangent;
00596 }
00597
00598
00599
00600 aTangent = (getControlPointB() - getStartPoint()) * 0.3;
00601
00602 if(!aTangent.equalZero())
00603 {
00604 return aTangent;
00605 }
00606
00607
00608 return (getEndPoint() - getStartPoint()) * 0.3;
00609 }
00610 else if(fTools::moreOrEqual(t, 1.0))
00611 {
00612
00613 B2DVector aTangent(getEndPoint() - getControlPointB());
00614
00615 if(!aTangent.equalZero())
00616 {
00617 return aTangent;
00618 }
00619
00620
00621
00622 aTangent = (getEndPoint() - getControlPointA()) * 0.3;
00623
00624 if(!aTangent.equalZero())
00625 {
00626 return aTangent;
00627 }
00628
00629
00630 return (getEndPoint() - getStartPoint()) * 0.3;
00631 }
00632 else
00633 {
00634
00635 B2DCubicBezier aRight;
00636 split(t, 0, &aRight);
00637
00638 return aRight.getControlPointA() - aRight.getStartPoint();
00639 }
00640 }
00641
00642
00643 void B2DCubicBezier::adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const
00644 {
00645 const double fLenFact(1.0 / static_cast< double >(nCount + 1));
00646
00647 for(sal_uInt32 a(1); a <= nCount; a++)
00648 {
00649 const double fPos(static_cast< double >(a) * fLenFact);
00650 rTarget.append(interpolatePoint(fPos));
00651 }
00652
00653 rTarget.append(getEndPoint());
00654 }
00655
00656
00657 void B2DCubicBezier::adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const
00658 {
00659 if(isBezier())
00660 {
00661 ImpSubDivDistance(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget,
00662 fDistanceBound * fDistanceBound, ::std::numeric_limits<double>::max(), 30);
00663 }
00664 else
00665 {
00666 rTarget.append(getEndPoint());
00667 }
00668 }
00669
00670 B2DPoint B2DCubicBezier::interpolatePoint(double t) const
00671 {
00672 OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)");
00673
00674 if(isBezier())
00675 {
00676 const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
00677 const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
00678 const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
00679 const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
00680 const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
00681
00682 return interpolate(aS2L, aS2R, t);
00683 }
00684 else
00685 {
00686 return interpolate(maStartPoint, maEndPoint, t);
00687 }
00688 }
00689
00690 double B2DCubicBezier::getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const
00691 {
00692 const sal_uInt32 nInitialDivisions(3L);
00693 B2DPolygon aInitialPolygon;
00694
00695
00696 aInitialPolygon.append(getStartPoint());
00697 adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions);
00698
00699
00700 const sal_uInt32 nPointCount(aInitialPolygon.count());
00701 B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0L));
00702 double fQuadDist(aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY());
00703 double fNewQuadDist;
00704 sal_uInt32 nSmallestIndex(0L);
00705
00706 for(sal_uInt32 a(1L); a < nPointCount; a++)
00707 {
00708 aVector = B2DVector(rTestPoint - aInitialPolygon.getB2DPoint(a));
00709 fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
00710
00711 if(fNewQuadDist < fQuadDist)
00712 {
00713 fQuadDist = fNewQuadDist;
00714 nSmallestIndex = a;
00715 }
00716 }
00717
00718
00719 double fStepValue(1.0 / (double)((nPointCount - 1L) * 2L));
00720 double fPosition((double)nSmallestIndex / (double)(nPointCount - 1L));
00721 bool bDone(false);
00722
00723 while(!bDone)
00724 {
00725 if(!bDone)
00726 {
00727
00728 double fPosLeft(fPosition - fStepValue);
00729
00730 if(fPosLeft < 0.0)
00731 {
00732 fPosLeft = 0.0;
00733 aVector = B2DVector(rTestPoint - maStartPoint);
00734 }
00735 else
00736 {
00737 aVector = B2DVector(rTestPoint - interpolatePoint(fPosLeft));
00738 }
00739
00740 fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
00741
00742 if(fTools::less(fNewQuadDist, fQuadDist))
00743 {
00744 fQuadDist = fNewQuadDist;
00745 fPosition = fPosLeft;
00746 }
00747 else
00748 {
00749
00750 double fPosRight(fPosition + fStepValue);
00751
00752 if(fPosRight > 1.0)
00753 {
00754 fPosRight = 1.0;
00755 aVector = B2DVector(rTestPoint - maEndPoint);
00756 }
00757 else
00758 {
00759 aVector = B2DVector(rTestPoint - interpolatePoint(fPosRight));
00760 }
00761
00762 fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
00763
00764 if(fTools::less(fNewQuadDist, fQuadDist))
00765 {
00766 fQuadDist = fNewQuadDist;
00767 fPosition = fPosRight;
00768 }
00769 else
00770 {
00771
00772 bDone = true;
00773 }
00774 }
00775 }
00776
00777 if(0.0 == fPosition || 1.0 == fPosition)
00778 {
00779
00780 bDone = true;
00781 }
00782
00783 if(!bDone)
00784 {
00785
00786 fStepValue /= 2.0;
00787 }
00788 }
00789
00790 rCut = fPosition;
00791 return sqrt(fQuadDist);
00792 }
00793
00794 void B2DCubicBezier::split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const
00795 {
00796 OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::split: Access out of range (!)");
00797
00798 if(!pBezierA && !pBezierB)
00799 {
00800 return;
00801 }
00802
00803 if(isBezier())
00804 {
00805 const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
00806 const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
00807 const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
00808 const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
00809 const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
00810 const B2DPoint aS3C(interpolate(aS2L, aS2R, t));
00811
00812 if(pBezierA)
00813 {
00814 pBezierA->setStartPoint(maStartPoint);
00815 pBezierA->setEndPoint(aS3C);
00816 pBezierA->setControlPointA(aS1L);
00817 pBezierA->setControlPointB(aS2L);
00818 }
00819
00820 if(pBezierB)
00821 {
00822 pBezierB->setStartPoint(aS3C);
00823 pBezierB->setEndPoint(maEndPoint);
00824 pBezierB->setControlPointA(aS2R);
00825 pBezierB->setControlPointB(aS1R);
00826 }
00827 }
00828 else
00829 {
00830 const B2DPoint aSplit(interpolate(maStartPoint, maEndPoint, t));
00831
00832 if(pBezierA)
00833 {
00834 pBezierA->setStartPoint(maStartPoint);
00835 pBezierA->setEndPoint(aSplit);
00836 pBezierA->setControlPointA(maStartPoint);
00837 pBezierA->setControlPointB(aSplit);
00838 }
00839
00840 if(pBezierB)
00841 {
00842 pBezierB->setStartPoint(aSplit);
00843 pBezierB->setEndPoint(maEndPoint);
00844 pBezierB->setControlPointA(aSplit);
00845 pBezierB->setControlPointB(maEndPoint);
00846 }
00847 }
00848 }
00849
00850 B2DCubicBezier B2DCubicBezier::snippet(double fStart, double fEnd) const
00851 {
00852 B2DCubicBezier aRetval;
00853
00854 if(fTools::more(fStart, 1.0))
00855 {
00856 fStart = 1.0;
00857 }
00858 else if(fTools::less(fStart, 0.0))
00859 {
00860 fStart = 0.0;
00861 }
00862
00863 if(fTools::more(fEnd, 1.0))
00864 {
00865 fEnd = 1.0;
00866 }
00867 else if(fTools::less(fEnd, 0.0))
00868 {
00869 fEnd = 0.0;
00870 }
00871
00872 if(fEnd <= fStart)
00873 {
00874
00875 const double fSplit((fEnd + fStart) * 0.5);
00876 const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit));
00877 aRetval.setStartPoint(aPoint);
00878 aRetval.setEndPoint(aPoint);
00879 aRetval.setControlPointA(aPoint);
00880 aRetval.setControlPointB(aPoint);
00881 }
00882 else
00883 {
00884 if(isBezier())
00885 {
00886
00887
00888 const bool bEndIsOne(fTools::equal(fEnd, 1.0));
00889 const bool bStartIsZero(fTools::equalZero(fStart));
00890 aRetval = *this;
00891
00892 if(!bEndIsOne)
00893 {
00894 aRetval.split(fEnd, &aRetval, 0);
00895
00896 if(!bStartIsZero)
00897 {
00898 fStart /= fEnd;
00899 }
00900 }
00901
00902 if(!bStartIsZero)
00903 {
00904 aRetval.split(fStart, 0, &aRetval);
00905 }
00906 }
00907 else
00908 {
00909
00910 const B2DPoint aPointA(interpolate(getStartPoint(), getEndPoint(), fStart));
00911 const B2DPoint aPointB(interpolate(getStartPoint(), getEndPoint(), fEnd));
00912 aRetval.setStartPoint(aPointA);
00913 aRetval.setEndPoint(aPointB);
00914 aRetval.setControlPointA(aPointA);
00915 aRetval.setControlPointB(aPointB);
00916 }
00917 }
00918
00919 return aRetval;
00920 }
00921
00922 B2DRange B2DCubicBezier::getRange() const
00923 {
00924 B2DRange aRetval(maStartPoint, maEndPoint);
00925
00926 aRetval.expand(maControlPointA);
00927 aRetval.expand(maControlPointB);
00928
00929 return aRetval;
00930 }
00931
00932 bool B2DCubicBezier::getMinimumExtremumPosition(double& rfResult) const
00933 {
00934 ::std::vector< double > aAllResults;
00935
00936 aAllResults.reserve(4);
00937 getAllExtremumPositions(aAllResults);
00938
00939 const sal_uInt32 nCount(aAllResults.size());
00940
00941 if(!nCount)
00942 {
00943 return false;
00944 }
00945 else if(1 == nCount)
00946 {
00947 rfResult = aAllResults[0];
00948 return true;
00949 }
00950 else
00951 {
00952 rfResult = *(::std::min_element(aAllResults.begin(), aAllResults.end()));
00953 return true;
00954 }
00955 }
00956
00957 namespace
00958 {
00959 inline void impCheckExtremumResult(double fCandidate, ::std::vector< double >& rResult)
00960 {
00961
00962
00963
00964 if(fCandidate > 0.0 && !fTools::equalZero(fCandidate))
00965 {
00966 if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0))
00967 {
00968 rResult.push_back(fCandidate);
00969 }
00970 }
00971 }
00972 }
00973
00974 void B2DCubicBezier::getAllExtremumPositions(::std::vector< double >& rResults) const
00975 {
00976 rResults.clear();
00977
00978
00979
00980
00981 const B2DPoint aRelativeEndPoint(maEndPoint-maStartPoint);
00982 const double fAX = 3 * (maControlPointA.getX() - maControlPointB.getX()) + aRelativeEndPoint.getX();
00983 const double fBX = 2 * maControlPointA.getX() - maControlPointB.getX() - maStartPoint.getX();
00984 double fCX(maControlPointA.getX() - maStartPoint.getX());
00985
00986 if(fTools::equalZero(fCX))
00987 {
00988
00989 fCX = 0.0;
00990 }
00991
00992 if( !fTools::equalZero(fAX) )
00993 {
00994
00995 const double fD = fBX*fBX - fAX*fCX;
00996 if( fD >= 0.0 )
00997 {
00998 const double fS = sqrt(fD);
00999
01000
01001
01002 const double fQ = fBX + ((fBX >= 0) ? +fS : -fS);
01003 impCheckExtremumResult(fQ / fAX, rResults);
01004 impCheckExtremumResult(fCX / fQ, rResults);
01005 }
01006 }
01007 else if( !fTools::equalZero(fBX) )
01008 {
01009
01010 impCheckExtremumResult(fCX / (2 * fBX), rResults);
01011 }
01012
01013
01014 const double fAY = 3 * (maControlPointA.getY() - maControlPointB.getY()) + aRelativeEndPoint.getY();
01015 const double fBY = 2 * maControlPointA.getY() - maControlPointB.getY() - maStartPoint.getY();
01016 double fCY(maControlPointA.getY() - maStartPoint.getY());
01017
01018 if(fTools::equalZero(fCY))
01019 {
01020
01021 fCY = 0.0;
01022 }
01023
01024 if( !fTools::equalZero(fAY) )
01025 {
01026
01027 const double fD = fBY*fBY - fAY*fCY;
01028 if( fD >= 0 )
01029 {
01030 const double fS = sqrt(fD);
01031
01032
01033
01034 const double fQ = fBY + ((fBY >= 0) ? +fS : -fS);
01035 impCheckExtremumResult(fQ / fAY, rResults);
01036 impCheckExtremumResult(fCY / fQ, rResults);
01037 }
01038 }
01039 else if( !fTools::equalZero(fBY) )
01040 {
01041
01042 impCheckExtremumResult(fCY / (2 * fBY), rResults);
01043 }
01044 }
01045 }
01046
01047