109dbbe93SAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 309dbbe93SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 409dbbe93SAndrew Rist * or more contributor license agreements. See the NOTICE file 509dbbe93SAndrew Rist * distributed with this work for additional information 609dbbe93SAndrew Rist * regarding copyright ownership. The ASF licenses this file 709dbbe93SAndrew Rist * to you under the Apache License, Version 2.0 (the 809dbbe93SAndrew Rist * "License"); you may not use this file except in compliance 909dbbe93SAndrew Rist * with the License. You may obtain a copy of the License at 10cdf0e10cSrcweir * 1109dbbe93SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12cdf0e10cSrcweir * 1309dbbe93SAndrew Rist * Unless required by applicable law or agreed to in writing, 1409dbbe93SAndrew Rist * software distributed under the License is distributed on an 1509dbbe93SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1609dbbe93SAndrew Rist * KIND, either express or implied. See the License for the 1709dbbe93SAndrew Rist * specific language governing permissions and limitations 1809dbbe93SAndrew Rist * under the License. 19cdf0e10cSrcweir * 2009dbbe93SAndrew Rist *************************************************************/ 2109dbbe93SAndrew Rist 22cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 23cdf0e10cSrcweir #include "precompiled_basegfx.hxx" 24cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx> 25cdf0e10cSrcweir #include <basegfx/vector/b2dvector.hxx> 26cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygon.hxx> 27cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx> 28cdf0e10cSrcweir 29cdf0e10cSrcweir #include <limits> 30cdf0e10cSrcweir 31cdf0e10cSrcweir // #i37443# 32cdf0e10cSrcweir #define FACTOR_FOR_UNSHARPEN (1.6) 33cdf0e10cSrcweir #ifdef DBG_UTIL 34cdf0e10cSrcweir static double fMultFactUnsharpen = FACTOR_FOR_UNSHARPEN; 35cdf0e10cSrcweir #endif 36cdf0e10cSrcweir 37cdf0e10cSrcweir namespace basegfx 38cdf0e10cSrcweir { 39cdf0e10cSrcweir namespace 40cdf0e10cSrcweir { ImpSubDivAngle(const B2DPoint & rfPA,const B2DPoint & rfEA,const B2DPoint & rfEB,const B2DPoint & rfPB,B2DPolygon & rTarget,double fAngleBound,bool bAllowUnsharpen,sal_uInt16 nMaxRecursionDepth)41cdf0e10cSrcweir void ImpSubDivAngle( 42cdf0e10cSrcweir const B2DPoint& rfPA, // start point 43cdf0e10cSrcweir const B2DPoint& rfEA, // edge on A 44cdf0e10cSrcweir const B2DPoint& rfEB, // edge on B 45cdf0e10cSrcweir const B2DPoint& rfPB, // end point 46cdf0e10cSrcweir B2DPolygon& rTarget, // target polygon 47cdf0e10cSrcweir double fAngleBound, // angle bound in [0.0 .. 2PI] 48cdf0e10cSrcweir bool bAllowUnsharpen, // #i37443# allow the criteria to get unsharp in recursions 49cdf0e10cSrcweir sal_uInt16 nMaxRecursionDepth) // endless loop protection 50cdf0e10cSrcweir { 51cdf0e10cSrcweir if(nMaxRecursionDepth) 52cdf0e10cSrcweir { 53cdf0e10cSrcweir // do angle test 54cdf0e10cSrcweir B2DVector aLeft(rfEA - rfPA); 55cdf0e10cSrcweir B2DVector aRight(rfEB - rfPB); 56cdf0e10cSrcweir 57cdf0e10cSrcweir // #i72104# 58cdf0e10cSrcweir if(aLeft.equalZero()) 59cdf0e10cSrcweir { 60cdf0e10cSrcweir aLeft = rfEB - rfPA; 61cdf0e10cSrcweir } 62cdf0e10cSrcweir 63cdf0e10cSrcweir if(aRight.equalZero()) 64cdf0e10cSrcweir { 65cdf0e10cSrcweir aRight = rfEA - rfPB; 66cdf0e10cSrcweir } 67cdf0e10cSrcweir 68cdf0e10cSrcweir const double fCurrentAngle(aLeft.angle(aRight)); 69cdf0e10cSrcweir 70cdf0e10cSrcweir if(fabs(fCurrentAngle) > (F_PI - fAngleBound)) 71cdf0e10cSrcweir { 72cdf0e10cSrcweir // end recursion 73cdf0e10cSrcweir nMaxRecursionDepth = 0; 74cdf0e10cSrcweir } 75cdf0e10cSrcweir else 76cdf0e10cSrcweir { 77cdf0e10cSrcweir if(bAllowUnsharpen) 78cdf0e10cSrcweir { 79cdf0e10cSrcweir // #i37443# unsharpen criteria 80cdf0e10cSrcweir #ifdef DBG_UTIL 81cdf0e10cSrcweir fAngleBound *= fMultFactUnsharpen; 82cdf0e10cSrcweir #else 83cdf0e10cSrcweir fAngleBound *= FACTOR_FOR_UNSHARPEN; 84cdf0e10cSrcweir #endif 85cdf0e10cSrcweir } 86cdf0e10cSrcweir } 87cdf0e10cSrcweir } 88cdf0e10cSrcweir 89cdf0e10cSrcweir if(nMaxRecursionDepth) 90cdf0e10cSrcweir { 91cdf0e10cSrcweir // divide at 0.5 92cdf0e10cSrcweir const B2DPoint aS1L(average(rfPA, rfEA)); 93cdf0e10cSrcweir const B2DPoint aS1C(average(rfEA, rfEB)); 94cdf0e10cSrcweir const B2DPoint aS1R(average(rfEB, rfPB)); 95cdf0e10cSrcweir const B2DPoint aS2L(average(aS1L, aS1C)); 96cdf0e10cSrcweir const B2DPoint aS2R(average(aS1C, aS1R)); 97cdf0e10cSrcweir const B2DPoint aS3C(average(aS2L, aS2R)); 98cdf0e10cSrcweir 99cdf0e10cSrcweir // left recursion 100cdf0e10cSrcweir ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1); 101cdf0e10cSrcweir 102cdf0e10cSrcweir // right recursion 103cdf0e10cSrcweir ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1); 104cdf0e10cSrcweir } 105cdf0e10cSrcweir else 106cdf0e10cSrcweir { 107cdf0e10cSrcweir rTarget.append(rfPB); 108cdf0e10cSrcweir } 109cdf0e10cSrcweir } 110cdf0e10cSrcweir ImpSubDivAngleStart(const B2DPoint & rfPA,const B2DPoint & rfEA,const B2DPoint & rfEB,const B2DPoint & rfPB,B2DPolygon & rTarget,const double & rfAngleBound,bool bAllowUnsharpen)111cdf0e10cSrcweir void ImpSubDivAngleStart( 112cdf0e10cSrcweir const B2DPoint& rfPA, // start point 113cdf0e10cSrcweir const B2DPoint& rfEA, // edge on A 114cdf0e10cSrcweir const B2DPoint& rfEB, // edge on B 115cdf0e10cSrcweir const B2DPoint& rfPB, // end point 116cdf0e10cSrcweir B2DPolygon& rTarget, // target polygon 117cdf0e10cSrcweir const double& rfAngleBound, // angle bound in [0.0 .. 2PI] 118cdf0e10cSrcweir bool bAllowUnsharpen) // #i37443# allow the criteria to get unsharp in recursions 119cdf0e10cSrcweir { 120cdf0e10cSrcweir sal_uInt16 nMaxRecursionDepth(8); 121cdf0e10cSrcweir const B2DVector aLeft(rfEA - rfPA); 122cdf0e10cSrcweir const B2DVector aRight(rfEB - rfPB); 123cdf0e10cSrcweir bool bLeftEqualZero(aLeft.equalZero()); 124cdf0e10cSrcweir bool bRightEqualZero(aRight.equalZero()); 125cdf0e10cSrcweir bool bAllParallel(false); 126cdf0e10cSrcweir 127cdf0e10cSrcweir if(bLeftEqualZero && bRightEqualZero) 128cdf0e10cSrcweir { 129cdf0e10cSrcweir nMaxRecursionDepth = 0; 130cdf0e10cSrcweir } 131cdf0e10cSrcweir else 132cdf0e10cSrcweir { 133cdf0e10cSrcweir const B2DVector aBase(rfPB - rfPA); 134cdf0e10cSrcweir const bool bBaseEqualZero(aBase.equalZero()); // #i72104# 135cdf0e10cSrcweir 136cdf0e10cSrcweir if(!bBaseEqualZero) 137cdf0e10cSrcweir { 138cdf0e10cSrcweir const bool bLeftParallel(bLeftEqualZero ? true : areParallel(aLeft, aBase)); 139cdf0e10cSrcweir const bool bRightParallel(bRightEqualZero ? true : areParallel(aRight, aBase)); 140cdf0e10cSrcweir 141cdf0e10cSrcweir if(bLeftParallel && bRightParallel) 142cdf0e10cSrcweir { 143cdf0e10cSrcweir bAllParallel = true; 144cdf0e10cSrcweir 145cdf0e10cSrcweir if(!bLeftEqualZero) 146cdf0e10cSrcweir { 147cdf0e10cSrcweir double fFactor; 148cdf0e10cSrcweir 149cdf0e10cSrcweir if(fabs(aBase.getX()) > fabs(aBase.getY())) 150cdf0e10cSrcweir { 151cdf0e10cSrcweir fFactor = aLeft.getX() / aBase.getX(); 152cdf0e10cSrcweir } 153cdf0e10cSrcweir else 154cdf0e10cSrcweir { 155cdf0e10cSrcweir fFactor = aLeft.getY() / aBase.getY(); 156cdf0e10cSrcweir } 157cdf0e10cSrcweir 158cdf0e10cSrcweir if(fFactor >= 0.0 && fFactor <= 1.0) 159cdf0e10cSrcweir { 160cdf0e10cSrcweir bLeftEqualZero = true; 161cdf0e10cSrcweir } 162cdf0e10cSrcweir } 163cdf0e10cSrcweir 164cdf0e10cSrcweir if(!bRightEqualZero) 165cdf0e10cSrcweir { 166cdf0e10cSrcweir double fFactor; 167cdf0e10cSrcweir 168cdf0e10cSrcweir if(fabs(aBase.getX()) > fabs(aBase.getY())) 169cdf0e10cSrcweir { 170cdf0e10cSrcweir fFactor = aRight.getX() / -aBase.getX(); 171cdf0e10cSrcweir } 172cdf0e10cSrcweir else 173cdf0e10cSrcweir { 174cdf0e10cSrcweir fFactor = aRight.getY() / -aBase.getY(); 175cdf0e10cSrcweir } 176cdf0e10cSrcweir 177cdf0e10cSrcweir if(fFactor >= 0.0 && fFactor <= 1.0) 178cdf0e10cSrcweir { 179cdf0e10cSrcweir bRightEqualZero = true; 180cdf0e10cSrcweir } 181cdf0e10cSrcweir } 182cdf0e10cSrcweir 183cdf0e10cSrcweir if(bLeftEqualZero && bRightEqualZero) 184cdf0e10cSrcweir { 185cdf0e10cSrcweir nMaxRecursionDepth = 0; 186cdf0e10cSrcweir } 187cdf0e10cSrcweir } 188cdf0e10cSrcweir } 189cdf0e10cSrcweir } 190cdf0e10cSrcweir 191cdf0e10cSrcweir if(nMaxRecursionDepth) 192cdf0e10cSrcweir { 193cdf0e10cSrcweir // divide at 0.5 ad test both edges for angle criteria 194cdf0e10cSrcweir const B2DPoint aS1L(average(rfPA, rfEA)); 195cdf0e10cSrcweir const B2DPoint aS1C(average(rfEA, rfEB)); 196cdf0e10cSrcweir const B2DPoint aS1R(average(rfEB, rfPB)); 197cdf0e10cSrcweir const B2DPoint aS2L(average(aS1L, aS1C)); 198cdf0e10cSrcweir const B2DPoint aS2R(average(aS1C, aS1R)); 199cdf0e10cSrcweir const B2DPoint aS3C(average(aS2L, aS2R)); 200cdf0e10cSrcweir 201cdf0e10cSrcweir // test left 202cdf0e10cSrcweir bool bAngleIsSmallerLeft(bAllParallel && bLeftEqualZero); 203cdf0e10cSrcweir if(!bAngleIsSmallerLeft) 204cdf0e10cSrcweir { 205cdf0e10cSrcweir const B2DVector aLeftLeft(bLeftEqualZero ? aS2L - aS1L : aS1L - rfPA); // #i72104# 206cdf0e10cSrcweir const B2DVector aRightLeft(aS2L - aS3C); 207cdf0e10cSrcweir const double fCurrentAngleLeft(aLeftLeft.angle(aRightLeft)); 208cdf0e10cSrcweir bAngleIsSmallerLeft = (fabs(fCurrentAngleLeft) > (F_PI - rfAngleBound)); 209cdf0e10cSrcweir } 210cdf0e10cSrcweir 211cdf0e10cSrcweir // test right 212cdf0e10cSrcweir bool bAngleIsSmallerRight(bAllParallel && bRightEqualZero); 213cdf0e10cSrcweir if(!bAngleIsSmallerRight) 214cdf0e10cSrcweir { 215cdf0e10cSrcweir const B2DVector aLeftRight(aS2R - aS3C); 216cdf0e10cSrcweir const B2DVector aRightRight(bRightEqualZero ? aS2R - aS1R : aS1R - rfPB); // #i72104# 217cdf0e10cSrcweir const double fCurrentAngleRight(aLeftRight.angle(aRightRight)); 218cdf0e10cSrcweir bAngleIsSmallerRight = (fabs(fCurrentAngleRight) > (F_PI - rfAngleBound)); 219cdf0e10cSrcweir } 220cdf0e10cSrcweir 221cdf0e10cSrcweir if(bAngleIsSmallerLeft && bAngleIsSmallerRight) 222cdf0e10cSrcweir { 223cdf0e10cSrcweir // no recursion necessary at all 224cdf0e10cSrcweir nMaxRecursionDepth = 0; 225cdf0e10cSrcweir } 226cdf0e10cSrcweir else 227cdf0e10cSrcweir { 228cdf0e10cSrcweir // left 229cdf0e10cSrcweir if(bAngleIsSmallerLeft) 230cdf0e10cSrcweir { 231cdf0e10cSrcweir rTarget.append(aS3C); 232cdf0e10cSrcweir } 233cdf0e10cSrcweir else 234cdf0e10cSrcweir { 235cdf0e10cSrcweir ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth); 236cdf0e10cSrcweir } 237cdf0e10cSrcweir 238cdf0e10cSrcweir // right 239cdf0e10cSrcweir if(bAngleIsSmallerRight) 240cdf0e10cSrcweir { 241cdf0e10cSrcweir rTarget.append(rfPB); 242cdf0e10cSrcweir } 243cdf0e10cSrcweir else 244cdf0e10cSrcweir { 245cdf0e10cSrcweir ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth); 246cdf0e10cSrcweir } 247cdf0e10cSrcweir } 248cdf0e10cSrcweir } 249cdf0e10cSrcweir 250cdf0e10cSrcweir if(!nMaxRecursionDepth) 251cdf0e10cSrcweir { 252cdf0e10cSrcweir rTarget.append(rfPB); 253cdf0e10cSrcweir } 254cdf0e10cSrcweir } 255cdf0e10cSrcweir ImpSubDivDistance(const B2DPoint & rfPA,const B2DPoint & rfEA,const B2DPoint & rfEB,const B2DPoint & rfPB,B2DPolygon & rTarget,double fDistanceBound2,double fLastDistanceError2,sal_uInt16 nMaxRecursionDepth)256cdf0e10cSrcweir void ImpSubDivDistance( 257cdf0e10cSrcweir const B2DPoint& rfPA, // start point 258cdf0e10cSrcweir const B2DPoint& rfEA, // edge on A 259cdf0e10cSrcweir const B2DPoint& rfEB, // edge on B 260cdf0e10cSrcweir const B2DPoint& rfPB, // end point 261cdf0e10cSrcweir B2DPolygon& rTarget, // target polygon 262cdf0e10cSrcweir double fDistanceBound2, // quadratic distance criteria 263cdf0e10cSrcweir double fLastDistanceError2, // the last quadratic distance error 264cdf0e10cSrcweir sal_uInt16 nMaxRecursionDepth) // endless loop protection 265cdf0e10cSrcweir { 266cdf0e10cSrcweir if(nMaxRecursionDepth) 267cdf0e10cSrcweir { 268cdf0e10cSrcweir // decide if another recursion is needed. If not, set 269cdf0e10cSrcweir // nMaxRecursionDepth to zero 270cdf0e10cSrcweir 271cdf0e10cSrcweir // Perform bezier flatness test (lecture notes from R. Schaback, 272cdf0e10cSrcweir // Mathematics of Computer-Aided Design, Uni Goettingen, 2000) 273cdf0e10cSrcweir // 274cdf0e10cSrcweir // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)|| 275cdf0e10cSrcweir // 0<=j<=n 276cdf0e10cSrcweir // 277cdf0e10cSrcweir // What is calculated here is an upper bound to the distance from 278cdf0e10cSrcweir // a line through b_0 and b_3 (rfPA and P4 in our notation) and the 279cdf0e10cSrcweir // curve. We can drop 0 and n from the running indices, since the 280cdf0e10cSrcweir // argument of max becomes zero for those cases. 281cdf0e10cSrcweir const double fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX())); 282cdf0e10cSrcweir const double fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY())); 283cdf0e10cSrcweir const double fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX())); 284cdf0e10cSrcweir const double fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY())); 285cdf0e10cSrcweir const double fDistanceError2(::std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y)); 286cdf0e10cSrcweir 287cdf0e10cSrcweir // stop if error measure does not improve anymore. This is a 288cdf0e10cSrcweir // safety guard against floating point inaccuracies. 289cdf0e10cSrcweir // stop if distance from line is guaranteed to be bounded by d 290cdf0e10cSrcweir const bool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2); 291cdf0e10cSrcweir 292cdf0e10cSrcweir if(bFurtherDivision) 293cdf0e10cSrcweir { 294cdf0e10cSrcweir // remember last error value 295cdf0e10cSrcweir fLastDistanceError2 = fDistanceError2; 296cdf0e10cSrcweir } 297cdf0e10cSrcweir else 298cdf0e10cSrcweir { 299cdf0e10cSrcweir // stop recustion 300cdf0e10cSrcweir nMaxRecursionDepth = 0; 301cdf0e10cSrcweir } 302cdf0e10cSrcweir } 303cdf0e10cSrcweir 304cdf0e10cSrcweir if(nMaxRecursionDepth) 305cdf0e10cSrcweir { 306cdf0e10cSrcweir // divide at 0.5 307cdf0e10cSrcweir const B2DPoint aS1L(average(rfPA, rfEA)); 308cdf0e10cSrcweir const B2DPoint aS1C(average(rfEA, rfEB)); 309cdf0e10cSrcweir const B2DPoint aS1R(average(rfEB, rfPB)); 310cdf0e10cSrcweir const B2DPoint aS2L(average(aS1L, aS1C)); 311cdf0e10cSrcweir const B2DPoint aS2R(average(aS1C, aS1R)); 312cdf0e10cSrcweir const B2DPoint aS3C(average(aS2L, aS2R)); 313cdf0e10cSrcweir 314cdf0e10cSrcweir // left recursion 315cdf0e10cSrcweir ImpSubDivDistance(rfPA, aS1L, aS2L, aS3C, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1); 316cdf0e10cSrcweir 317cdf0e10cSrcweir // right recursion 318cdf0e10cSrcweir ImpSubDivDistance(aS3C, aS2R, aS1R, rfPB, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1); 319cdf0e10cSrcweir } 320cdf0e10cSrcweir else 321cdf0e10cSrcweir { 322cdf0e10cSrcweir rTarget.append(rfPB); 323cdf0e10cSrcweir } 324cdf0e10cSrcweir } 325cdf0e10cSrcweir } // end of anonymous namespace 326cdf0e10cSrcweir } // end of namespace basegfx 327cdf0e10cSrcweir 328cdf0e10cSrcweir namespace basegfx 329cdf0e10cSrcweir { B2DCubicBezier(const B2DCubicBezier & rBezier)330cdf0e10cSrcweir B2DCubicBezier::B2DCubicBezier(const B2DCubicBezier& rBezier) 331cdf0e10cSrcweir : maStartPoint(rBezier.maStartPoint), 332cdf0e10cSrcweir maEndPoint(rBezier.maEndPoint), 333cdf0e10cSrcweir maControlPointA(rBezier.maControlPointA), 334cdf0e10cSrcweir maControlPointB(rBezier.maControlPointB) 335cdf0e10cSrcweir { 336cdf0e10cSrcweir } 337cdf0e10cSrcweir B2DCubicBezier()338cdf0e10cSrcweir B2DCubicBezier::B2DCubicBezier() 339cdf0e10cSrcweir { 340cdf0e10cSrcweir } 341cdf0e10cSrcweir B2DCubicBezier(const B2DPoint & rStart,const B2DPoint & rEnd)342cdf0e10cSrcweir B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rEnd) 343cdf0e10cSrcweir : maStartPoint(rStart), 344cdf0e10cSrcweir maEndPoint(rEnd), 345cdf0e10cSrcweir maControlPointA(rStart), 346cdf0e10cSrcweir maControlPointB(rEnd) 347cdf0e10cSrcweir { 348cdf0e10cSrcweir } 349cdf0e10cSrcweir B2DCubicBezier(const B2DPoint & rStart,const B2DPoint & rControlPointA,const B2DPoint & rControlPointB,const B2DPoint & rEnd)350cdf0e10cSrcweir B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd) 351cdf0e10cSrcweir : maStartPoint(rStart), 352cdf0e10cSrcweir maEndPoint(rEnd), 353cdf0e10cSrcweir maControlPointA(rControlPointA), 354cdf0e10cSrcweir maControlPointB(rControlPointB) 355cdf0e10cSrcweir { 356cdf0e10cSrcweir } 357cdf0e10cSrcweir ~B2DCubicBezier()358cdf0e10cSrcweir B2DCubicBezier::~B2DCubicBezier() 359cdf0e10cSrcweir { 360cdf0e10cSrcweir } 361cdf0e10cSrcweir 362cdf0e10cSrcweir // assignment operator operator =(const B2DCubicBezier & rBezier)363cdf0e10cSrcweir B2DCubicBezier& B2DCubicBezier::operator=(const B2DCubicBezier& rBezier) 364cdf0e10cSrcweir { 365cdf0e10cSrcweir maStartPoint = rBezier.maStartPoint; 366cdf0e10cSrcweir maEndPoint = rBezier.maEndPoint; 367cdf0e10cSrcweir maControlPointA = rBezier.maControlPointA; 368cdf0e10cSrcweir maControlPointB = rBezier.maControlPointB; 369cdf0e10cSrcweir 370cdf0e10cSrcweir return *this; 371cdf0e10cSrcweir } 372cdf0e10cSrcweir 373cdf0e10cSrcweir // compare operators operator ==(const B2DCubicBezier & rBezier) const374cdf0e10cSrcweir bool B2DCubicBezier::operator==(const B2DCubicBezier& rBezier) const 375cdf0e10cSrcweir { 376cdf0e10cSrcweir return ( 377cdf0e10cSrcweir maStartPoint == rBezier.maStartPoint 378cdf0e10cSrcweir && maEndPoint == rBezier.maEndPoint 379cdf0e10cSrcweir && maControlPointA == rBezier.maControlPointA 380cdf0e10cSrcweir && maControlPointB == rBezier.maControlPointB 381cdf0e10cSrcweir ); 382cdf0e10cSrcweir } 383cdf0e10cSrcweir operator !=(const B2DCubicBezier & rBezier) const384cdf0e10cSrcweir bool B2DCubicBezier::operator!=(const B2DCubicBezier& rBezier) const 385cdf0e10cSrcweir { 386cdf0e10cSrcweir return ( 387cdf0e10cSrcweir maStartPoint != rBezier.maStartPoint 388cdf0e10cSrcweir || maEndPoint != rBezier.maEndPoint 389cdf0e10cSrcweir || maControlPointA != rBezier.maControlPointA 390cdf0e10cSrcweir || maControlPointB != rBezier.maControlPointB 391cdf0e10cSrcweir ); 392cdf0e10cSrcweir } 393cdf0e10cSrcweir equal(const B2DCubicBezier & rBezier) const394cdf0e10cSrcweir bool B2DCubicBezier::equal(const B2DCubicBezier& rBezier) const 395cdf0e10cSrcweir { 396cdf0e10cSrcweir return ( 397cdf0e10cSrcweir maStartPoint.equal(rBezier.maStartPoint) 398cdf0e10cSrcweir && maEndPoint.equal(rBezier.maEndPoint) 399cdf0e10cSrcweir && maControlPointA.equal(rBezier.maControlPointA) 400cdf0e10cSrcweir && maControlPointB.equal(rBezier.maControlPointB) 401cdf0e10cSrcweir ); 402cdf0e10cSrcweir } 403cdf0e10cSrcweir 404cdf0e10cSrcweir // test if vectors are used isBezier() const405cdf0e10cSrcweir bool B2DCubicBezier::isBezier() const 406cdf0e10cSrcweir { 407cdf0e10cSrcweir if(maControlPointA != maStartPoint || maControlPointB != maEndPoint) 408cdf0e10cSrcweir { 409cdf0e10cSrcweir return true; 410cdf0e10cSrcweir } 411cdf0e10cSrcweir 412cdf0e10cSrcweir return false; 413cdf0e10cSrcweir } 414cdf0e10cSrcweir testAndSolveTrivialBezier()415cdf0e10cSrcweir void B2DCubicBezier::testAndSolveTrivialBezier() 416cdf0e10cSrcweir { 417cdf0e10cSrcweir if(maControlPointA != maStartPoint || maControlPointB != maEndPoint) 418cdf0e10cSrcweir { 419cdf0e10cSrcweir const B2DVector aEdge(maEndPoint - maStartPoint); 420cdf0e10cSrcweir 421cdf0e10cSrcweir // controls parallel to edge can be trivial. No edge -> not parallel -> control can 422*42fb6e95Smseidel // still not be trivial (e.g. balloon loop) 423cdf0e10cSrcweir if(!aEdge.equalZero()) 424cdf0e10cSrcweir { 425cdf0e10cSrcweir // get control vectors 426cdf0e10cSrcweir const B2DVector aVecA(maControlPointA - maStartPoint); 427cdf0e10cSrcweir const B2DVector aVecB(maControlPointB - maEndPoint); 428cdf0e10cSrcweir 429cdf0e10cSrcweir // check if trivial per se 430cdf0e10cSrcweir bool bAIsTrivial(aVecA.equalZero()); 431cdf0e10cSrcweir bool bBIsTrivial(aVecB.equalZero()); 432cdf0e10cSrcweir 433cdf0e10cSrcweir // #i102241# prepare inverse edge length to normalize cross values; 434cdf0e10cSrcweir // else the small compare value used in fTools::equalZero 435cdf0e10cSrcweir // will be length dependent and this detection will work as less 436cdf0e10cSrcweir // precise as longer the edge is. In principle, the length of the control 437cdf0e10cSrcweir // vector would need to be used too, but to be trivial it is assumed to 438cdf0e10cSrcweir // be of roughly equal length to the edge, so edge length can be used 439cdf0e10cSrcweir // for both. Only needed when one of both is not trivial per se. 440cdf0e10cSrcweir const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial 441cdf0e10cSrcweir ? 1.0 442cdf0e10cSrcweir : 1.0 / aEdge.getLength()); 443cdf0e10cSrcweir 444cdf0e10cSrcweir // if A is not zero, check if it could be 445cdf0e10cSrcweir if(!bAIsTrivial) 446cdf0e10cSrcweir { 447cdf0e10cSrcweir // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what 448cdf0e10cSrcweir // we need here with the precision we need 449cdf0e10cSrcweir const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength); 450cdf0e10cSrcweir 451cdf0e10cSrcweir if(fTools::equalZero(fCross)) 452cdf0e10cSrcweir { 453cdf0e10cSrcweir // get scale to edge. Use bigger distance for numeric quality 454cdf0e10cSrcweir const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) 455cdf0e10cSrcweir ? aVecA.getX() / aEdge.getX() 456cdf0e10cSrcweir : aVecA.getY() / aEdge.getY()); 457cdf0e10cSrcweir 458cdf0e10cSrcweir // relative end point of vector in edge range? 459cdf0e10cSrcweir if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0)) 460cdf0e10cSrcweir { 461cdf0e10cSrcweir bAIsTrivial = true; 462cdf0e10cSrcweir } 463cdf0e10cSrcweir } 464cdf0e10cSrcweir } 465cdf0e10cSrcweir 466cdf0e10cSrcweir // if B is not zero, check if it could be, but only if A is already trivial; 467cdf0e10cSrcweir // else solve to trivial will not be possible for whole edge 468cdf0e10cSrcweir if(bAIsTrivial && !bBIsTrivial) 469cdf0e10cSrcweir { 470cdf0e10cSrcweir // parallel to edge? Check aVecB, aEdge 471cdf0e10cSrcweir const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength); 472cdf0e10cSrcweir 473cdf0e10cSrcweir if(fTools::equalZero(fCross)) 474cdf0e10cSrcweir { 475cdf0e10cSrcweir // get scale to edge. Use bigger distance for numeric quality 476cdf0e10cSrcweir const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) 477cdf0e10cSrcweir ? aVecB.getX() / aEdge.getX() 478cdf0e10cSrcweir : aVecB.getY() / aEdge.getY()); 479cdf0e10cSrcweir 480cdf0e10cSrcweir // end point of vector in edge range? Caution: controlB is directed AGAINST edge 481cdf0e10cSrcweir if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0)) 482cdf0e10cSrcweir { 483cdf0e10cSrcweir bBIsTrivial = true; 484cdf0e10cSrcweir } 485cdf0e10cSrcweir } 486cdf0e10cSrcweir } 487cdf0e10cSrcweir 488cdf0e10cSrcweir // if both are/can be reduced, do it. 489cdf0e10cSrcweir // Not possible if only one is/can be reduced (!) 490cdf0e10cSrcweir if(bAIsTrivial && bBIsTrivial) 491cdf0e10cSrcweir { 492cdf0e10cSrcweir maControlPointA = maStartPoint; 493cdf0e10cSrcweir maControlPointB = maEndPoint; 494cdf0e10cSrcweir } 495cdf0e10cSrcweir } 496cdf0e10cSrcweir } 497cdf0e10cSrcweir } 498cdf0e10cSrcweir 499cdf0e10cSrcweir namespace { impGetLength(const B2DCubicBezier & rEdge,double fDeviation,sal_uInt32 nRecursionWatch)500cdf0e10cSrcweir double impGetLength(const B2DCubicBezier& rEdge, double fDeviation, sal_uInt32 nRecursionWatch) 501cdf0e10cSrcweir { 502cdf0e10cSrcweir const double fEdgeLength(rEdge.getEdgeLength()); 503cdf0e10cSrcweir const double fControlPolygonLength(rEdge.getControlPolygonLength()); 504cdf0e10cSrcweir const double fCurrentDeviation(fTools::equalZero(fControlPolygonLength) ? 0.0 : 1.0 - (fEdgeLength / fControlPolygonLength)); 505cdf0e10cSrcweir 506cdf0e10cSrcweir if(!nRecursionWatch || fTools:: lessOrEqual(fCurrentDeviation, fDeviation)) 507cdf0e10cSrcweir { 508cdf0e10cSrcweir return (fEdgeLength + fControlPolygonLength) * 0.5; 509cdf0e10cSrcweir } 510cdf0e10cSrcweir else 511cdf0e10cSrcweir { 512cdf0e10cSrcweir B2DCubicBezier aLeft, aRight; 513cdf0e10cSrcweir const double fNewDeviation(fDeviation * 0.5); 514cdf0e10cSrcweir const sal_uInt32 nNewRecursionWatch(nRecursionWatch - 1); 515cdf0e10cSrcweir 516cdf0e10cSrcweir rEdge.split(0.5, &aLeft, &aRight); 517cdf0e10cSrcweir 518cdf0e10cSrcweir return impGetLength(aLeft, fNewDeviation, nNewRecursionWatch) 519cdf0e10cSrcweir + impGetLength(aRight, fNewDeviation, nNewRecursionWatch); 520cdf0e10cSrcweir } 521cdf0e10cSrcweir } 522cdf0e10cSrcweir } 523cdf0e10cSrcweir getLength(double fDeviation) const524cdf0e10cSrcweir double B2DCubicBezier::getLength(double fDeviation) const 525cdf0e10cSrcweir { 526cdf0e10cSrcweir if(isBezier()) 527cdf0e10cSrcweir { 528cdf0e10cSrcweir if(fDeviation < 0.00000001) 529cdf0e10cSrcweir { 530cdf0e10cSrcweir fDeviation = 0.00000001; 531cdf0e10cSrcweir } 532cdf0e10cSrcweir 533cdf0e10cSrcweir return impGetLength(*this, fDeviation, 6); 534cdf0e10cSrcweir } 535cdf0e10cSrcweir else 536cdf0e10cSrcweir { 537cdf0e10cSrcweir return B2DVector(getEndPoint() - getStartPoint()).getLength(); 538cdf0e10cSrcweir } 539cdf0e10cSrcweir } 540cdf0e10cSrcweir getEdgeLength() const541cdf0e10cSrcweir double B2DCubicBezier::getEdgeLength() const 542cdf0e10cSrcweir { 543cdf0e10cSrcweir const B2DVector aEdge(maEndPoint - maStartPoint); 544cdf0e10cSrcweir return aEdge.getLength(); 545cdf0e10cSrcweir } 546cdf0e10cSrcweir getControlPolygonLength() const547cdf0e10cSrcweir double B2DCubicBezier::getControlPolygonLength() const 548cdf0e10cSrcweir { 549cdf0e10cSrcweir const B2DVector aVectorA(maControlPointA - maStartPoint); 550cdf0e10cSrcweir const B2DVector aVectorB(maEndPoint - maControlPointB); 551cdf0e10cSrcweir 552cdf0e10cSrcweir if(!aVectorA.equalZero() || !aVectorB.equalZero()) 553cdf0e10cSrcweir { 554cdf0e10cSrcweir const B2DVector aTop(maControlPointB - maControlPointA); 555cdf0e10cSrcweir return (aVectorA.getLength() + aVectorB.getLength() + aTop.getLength()); 556cdf0e10cSrcweir } 557cdf0e10cSrcweir else 558cdf0e10cSrcweir { 559cdf0e10cSrcweir return getEdgeLength(); 560cdf0e10cSrcweir } 561cdf0e10cSrcweir } 562cdf0e10cSrcweir adaptiveSubdivideByAngle(B2DPolygon & rTarget,double fAngleBound,bool bAllowUnsharpen) const563cdf0e10cSrcweir void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const 564cdf0e10cSrcweir { 565cdf0e10cSrcweir if(isBezier()) 566cdf0e10cSrcweir { 567cdf0e10cSrcweir // use support method #i37443# and allow unsharpen the criteria 568cdf0e10cSrcweir ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, fAngleBound * F_PI180, bAllowUnsharpen); 569cdf0e10cSrcweir } 570cdf0e10cSrcweir else 571cdf0e10cSrcweir { 572cdf0e10cSrcweir rTarget.append(getEndPoint()); 573cdf0e10cSrcweir } 574cdf0e10cSrcweir } 575cdf0e10cSrcweir getTangent(double t) const576cdf0e10cSrcweir B2DVector B2DCubicBezier::getTangent(double t) const 577cdf0e10cSrcweir { 578cdf0e10cSrcweir if(fTools::lessOrEqual(t, 0.0)) 579cdf0e10cSrcweir { 580cdf0e10cSrcweir // tangent in start point 581cdf0e10cSrcweir B2DVector aTangent(getControlPointA() - getStartPoint()); 582cdf0e10cSrcweir 583cdf0e10cSrcweir if(!aTangent.equalZero()) 584cdf0e10cSrcweir { 585cdf0e10cSrcweir return aTangent; 586cdf0e10cSrcweir } 587cdf0e10cSrcweir 588cdf0e10cSrcweir // start point and control vector are the same, fallback 589cdf0e10cSrcweir // to implicit start vector to control point B 590cdf0e10cSrcweir aTangent = (getControlPointB() - getStartPoint()) * 0.3; 591cdf0e10cSrcweir 592cdf0e10cSrcweir if(!aTangent.equalZero()) 593cdf0e10cSrcweir { 594cdf0e10cSrcweir return aTangent; 595cdf0e10cSrcweir } 596cdf0e10cSrcweir 597cdf0e10cSrcweir // not a bezier at all, return edge vector 598cdf0e10cSrcweir return (getEndPoint() - getStartPoint()) * 0.3; 599cdf0e10cSrcweir } 600cdf0e10cSrcweir else if(fTools::moreOrEqual(t, 1.0)) 601cdf0e10cSrcweir { 602cdf0e10cSrcweir // tangent in end point 603cdf0e10cSrcweir B2DVector aTangent(getEndPoint() - getControlPointB()); 604cdf0e10cSrcweir 605cdf0e10cSrcweir if(!aTangent.equalZero()) 606cdf0e10cSrcweir { 607cdf0e10cSrcweir return aTangent; 608cdf0e10cSrcweir } 609cdf0e10cSrcweir 610cdf0e10cSrcweir // end point and control vector are the same, fallback 611cdf0e10cSrcweir // to implicit start vector from control point A 612cdf0e10cSrcweir aTangent = (getEndPoint() - getControlPointA()) * 0.3; 613cdf0e10cSrcweir 614cdf0e10cSrcweir if(!aTangent.equalZero()) 615cdf0e10cSrcweir { 616cdf0e10cSrcweir return aTangent; 617cdf0e10cSrcweir } 618cdf0e10cSrcweir 619cdf0e10cSrcweir // not a bezier at all, return edge vector 620cdf0e10cSrcweir return (getEndPoint() - getStartPoint()) * 0.3; 621cdf0e10cSrcweir } 622cdf0e10cSrcweir else 623cdf0e10cSrcweir { 624cdf0e10cSrcweir // t is in ]0.0 .. 1.0[. Split and extract 625cdf0e10cSrcweir B2DCubicBezier aRight; 626cdf0e10cSrcweir split(t, 0, &aRight); 627cdf0e10cSrcweir 628cdf0e10cSrcweir return aRight.getControlPointA() - aRight.getStartPoint(); 629cdf0e10cSrcweir } 630cdf0e10cSrcweir } 631cdf0e10cSrcweir 632cdf0e10cSrcweir // #i37443# adaptive subdivide by nCount subdivisions adaptiveSubdivideByCount(B2DPolygon & rTarget,sal_uInt32 nCount) const633cdf0e10cSrcweir void B2DCubicBezier::adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const 634cdf0e10cSrcweir { 635cdf0e10cSrcweir const double fLenFact(1.0 / static_cast< double >(nCount + 1)); 636cdf0e10cSrcweir 637cdf0e10cSrcweir for(sal_uInt32 a(1); a <= nCount; a++) 638cdf0e10cSrcweir { 639cdf0e10cSrcweir const double fPos(static_cast< double >(a) * fLenFact); 640cdf0e10cSrcweir rTarget.append(interpolatePoint(fPos)); 641cdf0e10cSrcweir } 642cdf0e10cSrcweir 643cdf0e10cSrcweir rTarget.append(getEndPoint()); 644cdf0e10cSrcweir } 645cdf0e10cSrcweir 646cdf0e10cSrcweir // adaptive subdivide by distance adaptiveSubdivideByDistance(B2DPolygon & rTarget,double fDistanceBound) const647cdf0e10cSrcweir void B2DCubicBezier::adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const 648cdf0e10cSrcweir { 649cdf0e10cSrcweir if(isBezier()) 650cdf0e10cSrcweir { 651cdf0e10cSrcweir ImpSubDivDistance(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, 652cdf0e10cSrcweir fDistanceBound * fDistanceBound, ::std::numeric_limits<double>::max(), 30); 653cdf0e10cSrcweir } 654cdf0e10cSrcweir else 655cdf0e10cSrcweir { 656cdf0e10cSrcweir rTarget.append(getEndPoint()); 657cdf0e10cSrcweir } 658cdf0e10cSrcweir } 659cdf0e10cSrcweir interpolatePoint(double t) const660cdf0e10cSrcweir B2DPoint B2DCubicBezier::interpolatePoint(double t) const 661cdf0e10cSrcweir { 662cdf0e10cSrcweir OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)"); 663cdf0e10cSrcweir 664cdf0e10cSrcweir if(isBezier()) 665cdf0e10cSrcweir { 666cdf0e10cSrcweir const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t)); 667cdf0e10cSrcweir const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t)); 668cdf0e10cSrcweir const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t)); 669cdf0e10cSrcweir const B2DPoint aS2L(interpolate(aS1L, aS1C, t)); 670cdf0e10cSrcweir const B2DPoint aS2R(interpolate(aS1C, aS1R, t)); 671cdf0e10cSrcweir 672cdf0e10cSrcweir return interpolate(aS2L, aS2R, t); 673cdf0e10cSrcweir } 674cdf0e10cSrcweir else 675cdf0e10cSrcweir { 676cdf0e10cSrcweir return interpolate(maStartPoint, maEndPoint, t); 677cdf0e10cSrcweir } 678cdf0e10cSrcweir } 679cdf0e10cSrcweir getSmallestDistancePointToBezierSegment(const B2DPoint & rTestPoint,double & rCut) const680cdf0e10cSrcweir double B2DCubicBezier::getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const 681cdf0e10cSrcweir { 682cdf0e10cSrcweir const sal_uInt32 nInitialDivisions(3L); 683cdf0e10cSrcweir B2DPolygon aInitialPolygon; 684cdf0e10cSrcweir 685cdf0e10cSrcweir // as start make a fix division, creates nInitialDivisions + 2L points 686cdf0e10cSrcweir aInitialPolygon.append(getStartPoint()); 687cdf0e10cSrcweir adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions); 688cdf0e10cSrcweir 689cdf0e10cSrcweir // now look for the closest point 690cdf0e10cSrcweir const sal_uInt32 nPointCount(aInitialPolygon.count()); 691cdf0e10cSrcweir B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0L)); 692cdf0e10cSrcweir double fQuadDist(aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY()); 693cdf0e10cSrcweir double fNewQuadDist; 694cdf0e10cSrcweir sal_uInt32 nSmallestIndex(0L); 695cdf0e10cSrcweir 696cdf0e10cSrcweir for(sal_uInt32 a(1L); a < nPointCount; a++) 697cdf0e10cSrcweir { 698cdf0e10cSrcweir aVector = B2DVector(rTestPoint - aInitialPolygon.getB2DPoint(a)); 699cdf0e10cSrcweir fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY(); 700cdf0e10cSrcweir 701cdf0e10cSrcweir if(fNewQuadDist < fQuadDist) 702cdf0e10cSrcweir { 703cdf0e10cSrcweir fQuadDist = fNewQuadDist; 704cdf0e10cSrcweir nSmallestIndex = a; 705cdf0e10cSrcweir } 706cdf0e10cSrcweir } 707cdf0e10cSrcweir 708cdf0e10cSrcweir // look right and left for even smaller distances 709cdf0e10cSrcweir double fStepValue(1.0 / (double)((nPointCount - 1L) * 2L)); // half the edge step width 710cdf0e10cSrcweir double fPosition((double)nSmallestIndex / (double)(nPointCount - 1L)); 711cdf0e10cSrcweir bool bDone(false); 712cdf0e10cSrcweir 713cdf0e10cSrcweir while(!bDone) 714cdf0e10cSrcweir { 715cdf0e10cSrcweir if(!bDone) 716cdf0e10cSrcweir { 717cdf0e10cSrcweir // test left 718cdf0e10cSrcweir double fPosLeft(fPosition - fStepValue); 719cdf0e10cSrcweir 720cdf0e10cSrcweir if(fPosLeft < 0.0) 721cdf0e10cSrcweir { 722cdf0e10cSrcweir fPosLeft = 0.0; 723cdf0e10cSrcweir aVector = B2DVector(rTestPoint - maStartPoint); 724cdf0e10cSrcweir } 725cdf0e10cSrcweir else 726cdf0e10cSrcweir { 727cdf0e10cSrcweir aVector = B2DVector(rTestPoint - interpolatePoint(fPosLeft)); 728cdf0e10cSrcweir } 729cdf0e10cSrcweir 730cdf0e10cSrcweir fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY(); 731cdf0e10cSrcweir 732cdf0e10cSrcweir if(fTools::less(fNewQuadDist, fQuadDist)) 733cdf0e10cSrcweir { 734cdf0e10cSrcweir fQuadDist = fNewQuadDist; 735cdf0e10cSrcweir fPosition = fPosLeft; 736cdf0e10cSrcweir } 737cdf0e10cSrcweir else 738cdf0e10cSrcweir { 739cdf0e10cSrcweir // test right 740cdf0e10cSrcweir double fPosRight(fPosition + fStepValue); 741cdf0e10cSrcweir 742cdf0e10cSrcweir if(fPosRight > 1.0) 743cdf0e10cSrcweir { 744cdf0e10cSrcweir fPosRight = 1.0; 745cdf0e10cSrcweir aVector = B2DVector(rTestPoint - maEndPoint); 746cdf0e10cSrcweir } 747cdf0e10cSrcweir else 748cdf0e10cSrcweir { 749cdf0e10cSrcweir aVector = B2DVector(rTestPoint - interpolatePoint(fPosRight)); 750cdf0e10cSrcweir } 751cdf0e10cSrcweir 752cdf0e10cSrcweir fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY(); 753cdf0e10cSrcweir 754cdf0e10cSrcweir if(fTools::less(fNewQuadDist, fQuadDist)) 755cdf0e10cSrcweir { 756cdf0e10cSrcweir fQuadDist = fNewQuadDist; 757cdf0e10cSrcweir fPosition = fPosRight; 758cdf0e10cSrcweir } 759cdf0e10cSrcweir else 760cdf0e10cSrcweir { 761cdf0e10cSrcweir // not less left or right, done 762cdf0e10cSrcweir bDone = true; 763cdf0e10cSrcweir } 764cdf0e10cSrcweir } 765cdf0e10cSrcweir } 766cdf0e10cSrcweir 767cdf0e10cSrcweir if(0.0 == fPosition || 1.0 == fPosition) 768cdf0e10cSrcweir { 769cdf0e10cSrcweir // if we are completely left or right, we are done 770cdf0e10cSrcweir bDone = true; 771cdf0e10cSrcweir } 772cdf0e10cSrcweir 773cdf0e10cSrcweir if(!bDone) 774cdf0e10cSrcweir { 775cdf0e10cSrcweir // prepare next step value 776cdf0e10cSrcweir fStepValue /= 2.0; 777cdf0e10cSrcweir } 778cdf0e10cSrcweir } 779cdf0e10cSrcweir 780cdf0e10cSrcweir rCut = fPosition; 781cdf0e10cSrcweir return sqrt(fQuadDist); 782cdf0e10cSrcweir } 783cdf0e10cSrcweir split(double t,B2DCubicBezier * pBezierA,B2DCubicBezier * pBezierB) const784cdf0e10cSrcweir void B2DCubicBezier::split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const 785cdf0e10cSrcweir { 786cdf0e10cSrcweir OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::split: Access out of range (!)"); 787cdf0e10cSrcweir 788cdf0e10cSrcweir if(!pBezierA && !pBezierB) 789cdf0e10cSrcweir { 790cdf0e10cSrcweir return; 791cdf0e10cSrcweir } 792cdf0e10cSrcweir 793cdf0e10cSrcweir if(isBezier()) 794cdf0e10cSrcweir { 795cdf0e10cSrcweir const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t)); 796cdf0e10cSrcweir const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t)); 797cdf0e10cSrcweir const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t)); 798cdf0e10cSrcweir const B2DPoint aS2L(interpolate(aS1L, aS1C, t)); 799cdf0e10cSrcweir const B2DPoint aS2R(interpolate(aS1C, aS1R, t)); 800cdf0e10cSrcweir const B2DPoint aS3C(interpolate(aS2L, aS2R, t)); 801cdf0e10cSrcweir 802cdf0e10cSrcweir if(pBezierA) 803cdf0e10cSrcweir { 804cdf0e10cSrcweir pBezierA->setStartPoint(maStartPoint); 805cdf0e10cSrcweir pBezierA->setEndPoint(aS3C); 806cdf0e10cSrcweir pBezierA->setControlPointA(aS1L); 807cdf0e10cSrcweir pBezierA->setControlPointB(aS2L); 808cdf0e10cSrcweir } 809cdf0e10cSrcweir 810cdf0e10cSrcweir if(pBezierB) 811cdf0e10cSrcweir { 812cdf0e10cSrcweir pBezierB->setStartPoint(aS3C); 813cdf0e10cSrcweir pBezierB->setEndPoint(maEndPoint); 814cdf0e10cSrcweir pBezierB->setControlPointA(aS2R); 815cdf0e10cSrcweir pBezierB->setControlPointB(aS1R); 816cdf0e10cSrcweir } 817cdf0e10cSrcweir } 818cdf0e10cSrcweir else 819cdf0e10cSrcweir { 820cdf0e10cSrcweir const B2DPoint aSplit(interpolate(maStartPoint, maEndPoint, t)); 821cdf0e10cSrcweir 822cdf0e10cSrcweir if(pBezierA) 823cdf0e10cSrcweir { 824cdf0e10cSrcweir pBezierA->setStartPoint(maStartPoint); 825cdf0e10cSrcweir pBezierA->setEndPoint(aSplit); 826cdf0e10cSrcweir pBezierA->setControlPointA(maStartPoint); 827cdf0e10cSrcweir pBezierA->setControlPointB(aSplit); 828cdf0e10cSrcweir } 829cdf0e10cSrcweir 830cdf0e10cSrcweir if(pBezierB) 831cdf0e10cSrcweir { 832cdf0e10cSrcweir pBezierB->setStartPoint(aSplit); 833cdf0e10cSrcweir pBezierB->setEndPoint(maEndPoint); 834cdf0e10cSrcweir pBezierB->setControlPointA(aSplit); 835cdf0e10cSrcweir pBezierB->setControlPointB(maEndPoint); 836cdf0e10cSrcweir } 837cdf0e10cSrcweir } 838cdf0e10cSrcweir } 839cdf0e10cSrcweir snippet(double fStart,double fEnd) const840cdf0e10cSrcweir B2DCubicBezier B2DCubicBezier::snippet(double fStart, double fEnd) const 841cdf0e10cSrcweir { 842cdf0e10cSrcweir B2DCubicBezier aRetval; 843cdf0e10cSrcweir 844cdf0e10cSrcweir if(fTools::more(fStart, 1.0)) 845cdf0e10cSrcweir { 846cdf0e10cSrcweir fStart = 1.0; 847cdf0e10cSrcweir } 848cdf0e10cSrcweir else if(fTools::less(fStart, 0.0)) 849cdf0e10cSrcweir { 850cdf0e10cSrcweir fStart = 0.0; 851cdf0e10cSrcweir } 852cdf0e10cSrcweir 853cdf0e10cSrcweir if(fTools::more(fEnd, 1.0)) 854cdf0e10cSrcweir { 855cdf0e10cSrcweir fEnd = 1.0; 856cdf0e10cSrcweir } 857cdf0e10cSrcweir else if(fTools::less(fEnd, 0.0)) 858cdf0e10cSrcweir { 859cdf0e10cSrcweir fEnd = 0.0; 860cdf0e10cSrcweir } 861cdf0e10cSrcweir 862cdf0e10cSrcweir if(fEnd <= fStart) 863cdf0e10cSrcweir { 864cdf0e10cSrcweir // empty or NULL, create single point at center 865cdf0e10cSrcweir const double fSplit((fEnd + fStart) * 0.5); 866cdf0e10cSrcweir const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit)); 867cdf0e10cSrcweir aRetval.setStartPoint(aPoint); 868cdf0e10cSrcweir aRetval.setEndPoint(aPoint); 869cdf0e10cSrcweir aRetval.setControlPointA(aPoint); 870cdf0e10cSrcweir aRetval.setControlPointB(aPoint); 871cdf0e10cSrcweir } 872cdf0e10cSrcweir else 873cdf0e10cSrcweir { 874cdf0e10cSrcweir if(isBezier()) 875cdf0e10cSrcweir { 876cdf0e10cSrcweir // copy bezier; cut off right, then cut off left. Do not forget to 877cdf0e10cSrcweir // adapt cut value when both cuts happen 878cdf0e10cSrcweir const bool bEndIsOne(fTools::equal(fEnd, 1.0)); 879cdf0e10cSrcweir const bool bStartIsZero(fTools::equalZero(fStart)); 880cdf0e10cSrcweir aRetval = *this; 881cdf0e10cSrcweir 882cdf0e10cSrcweir if(!bEndIsOne) 883cdf0e10cSrcweir { 884cdf0e10cSrcweir aRetval.split(fEnd, &aRetval, 0); 885cdf0e10cSrcweir 886cdf0e10cSrcweir if(!bStartIsZero) 887cdf0e10cSrcweir { 888cdf0e10cSrcweir fStart /= fEnd; 889cdf0e10cSrcweir } 890cdf0e10cSrcweir } 891cdf0e10cSrcweir 892cdf0e10cSrcweir if(!bStartIsZero) 893cdf0e10cSrcweir { 894cdf0e10cSrcweir aRetval.split(fStart, 0, &aRetval); 895cdf0e10cSrcweir } 896cdf0e10cSrcweir } 897cdf0e10cSrcweir else 898cdf0e10cSrcweir { 899cdf0e10cSrcweir // no bezier, create simple edge 900cdf0e10cSrcweir const B2DPoint aPointA(interpolate(getStartPoint(), getEndPoint(), fStart)); 901cdf0e10cSrcweir const B2DPoint aPointB(interpolate(getStartPoint(), getEndPoint(), fEnd)); 902cdf0e10cSrcweir aRetval.setStartPoint(aPointA); 903cdf0e10cSrcweir aRetval.setEndPoint(aPointB); 904cdf0e10cSrcweir aRetval.setControlPointA(aPointA); 905cdf0e10cSrcweir aRetval.setControlPointB(aPointB); 906cdf0e10cSrcweir } 907cdf0e10cSrcweir } 908cdf0e10cSrcweir 909cdf0e10cSrcweir return aRetval; 910cdf0e10cSrcweir } 911cdf0e10cSrcweir getRange() const912cdf0e10cSrcweir B2DRange B2DCubicBezier::getRange() const 913cdf0e10cSrcweir { 914cdf0e10cSrcweir B2DRange aRetval(maStartPoint, maEndPoint); 915cdf0e10cSrcweir 916cdf0e10cSrcweir aRetval.expand(maControlPointA); 917cdf0e10cSrcweir aRetval.expand(maControlPointB); 918cdf0e10cSrcweir 919cdf0e10cSrcweir return aRetval; 920cdf0e10cSrcweir } 921cdf0e10cSrcweir getMinimumExtremumPosition(double & rfResult) const922cdf0e10cSrcweir bool B2DCubicBezier::getMinimumExtremumPosition(double& rfResult) const 923cdf0e10cSrcweir { 924cdf0e10cSrcweir ::std::vector< double > aAllResults; 925cdf0e10cSrcweir 926cdf0e10cSrcweir aAllResults.reserve(4); 927cdf0e10cSrcweir getAllExtremumPositions(aAllResults); 928cdf0e10cSrcweir 929cdf0e10cSrcweir const sal_uInt32 nCount(aAllResults.size()); 930cdf0e10cSrcweir 931cdf0e10cSrcweir if(!nCount) 932cdf0e10cSrcweir { 933cdf0e10cSrcweir return false; 934cdf0e10cSrcweir } 935cdf0e10cSrcweir else if(1 == nCount) 936cdf0e10cSrcweir { 937cdf0e10cSrcweir rfResult = aAllResults[0]; 938cdf0e10cSrcweir return true; 939cdf0e10cSrcweir } 940cdf0e10cSrcweir else 941cdf0e10cSrcweir { 942cdf0e10cSrcweir rfResult = *(::std::min_element(aAllResults.begin(), aAllResults.end())); 943cdf0e10cSrcweir return true; 944cdf0e10cSrcweir } 945cdf0e10cSrcweir } 946cdf0e10cSrcweir 947cdf0e10cSrcweir namespace 948cdf0e10cSrcweir { impCheckExtremumResult(double fCandidate,::std::vector<double> & rResult)949cdf0e10cSrcweir inline void impCheckExtremumResult(double fCandidate, ::std::vector< double >& rResult) 950cdf0e10cSrcweir { 951cdf0e10cSrcweir // check for range ]0.0 .. 1.0[ with excluding 1.0 and 0.0 clearly 952cdf0e10cSrcweir // by using the equalZero test, NOT ::more or ::less which will use the 953cdf0e10cSrcweir // ApproxEqual() which is too exact here 954cdf0e10cSrcweir if(fCandidate > 0.0 && !fTools::equalZero(fCandidate)) 955cdf0e10cSrcweir { 956cdf0e10cSrcweir if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0)) 957cdf0e10cSrcweir { 958cdf0e10cSrcweir rResult.push_back(fCandidate); 959cdf0e10cSrcweir } 960cdf0e10cSrcweir } 961cdf0e10cSrcweir } 962cdf0e10cSrcweir } 963cdf0e10cSrcweir getAllExtremumPositions(::std::vector<double> & rResults) const964cdf0e10cSrcweir void B2DCubicBezier::getAllExtremumPositions(::std::vector< double >& rResults) const 965cdf0e10cSrcweir { 966cdf0e10cSrcweir rResults.clear(); 967cdf0e10cSrcweir 968cdf0e10cSrcweir // calculate the x-extrema parameters by zeroing first x-derivative 969cdf0e10cSrcweir // of the cubic bezier's parametric formula, which results in a 970cdf0e10cSrcweir // quadratic equation: dBezier/dt = t*t*fAX - 2*t*fBX + fCX 971cdf0e10cSrcweir const B2DPoint aControlDiff( maControlPointA - maControlPointB ); 972cdf0e10cSrcweir double fCX = maControlPointA.getX() - maStartPoint.getX(); 973cdf0e10cSrcweir const double fBX = fCX + aControlDiff.getX(); 974cdf0e10cSrcweir const double fAX = 3 * aControlDiff.getX() + (maEndPoint.getX() - maStartPoint.getX()); 975cdf0e10cSrcweir 976cdf0e10cSrcweir if(fTools::equalZero(fCX)) 977cdf0e10cSrcweir { 978cdf0e10cSrcweir // detect fCX equal zero and truncate to real zero value in that case 979cdf0e10cSrcweir fCX = 0.0; 980cdf0e10cSrcweir } 981cdf0e10cSrcweir 982cdf0e10cSrcweir if( !fTools::equalZero(fAX) ) 983cdf0e10cSrcweir { 984cdf0e10cSrcweir // derivative is polynomial of order 2 => use binomial formula 985cdf0e10cSrcweir const double fD = fBX*fBX - fAX*fCX; 986cdf0e10cSrcweir if( fD >= 0.0 ) 987cdf0e10cSrcweir { 988cdf0e10cSrcweir const double fS = sqrt(fD); 989cdf0e10cSrcweir // calculate both roots (avoiding a numerically unstable subtraction) 990cdf0e10cSrcweir const double fQ = fBX + ((fBX >= 0) ? +fS : -fS); 991cdf0e10cSrcweir impCheckExtremumResult(fQ / fAX, rResults); 992cdf0e10cSrcweir if( !fTools::equalZero(fS) ) // ignore root multiplicity 993cdf0e10cSrcweir impCheckExtremumResult(fCX / fQ, rResults); 994cdf0e10cSrcweir } 995cdf0e10cSrcweir } 996cdf0e10cSrcweir else if( !fTools::equalZero(fBX) ) 997cdf0e10cSrcweir { 998cdf0e10cSrcweir // derivative is polynomial of order 1 => one extrema 999cdf0e10cSrcweir impCheckExtremumResult(fCX / (2 * fBX), rResults); 1000cdf0e10cSrcweir } 1001cdf0e10cSrcweir 1002cdf0e10cSrcweir // calculate the y-extrema parameters by zeroing first y-derivative 1003cdf0e10cSrcweir double fCY = maControlPointA.getY() - maStartPoint.getY(); 1004cdf0e10cSrcweir const double fBY = fCY + aControlDiff.getY(); 1005cdf0e10cSrcweir const double fAY = 3 * aControlDiff.getY() + (maEndPoint.getY() - maStartPoint.getY()); 1006cdf0e10cSrcweir 1007cdf0e10cSrcweir if(fTools::equalZero(fCY)) 1008cdf0e10cSrcweir { 1009cdf0e10cSrcweir // detect fCY equal zero and truncate to real zero value in that case 1010cdf0e10cSrcweir fCY = 0.0; 1011cdf0e10cSrcweir } 1012cdf0e10cSrcweir 1013cdf0e10cSrcweir if( !fTools::equalZero(fAY) ) 1014cdf0e10cSrcweir { 1015cdf0e10cSrcweir // derivative is polynomial of order 2 => use binomial formula 1016cdf0e10cSrcweir const double fD = fBY*fBY - fAY*fCY; 1017cdf0e10cSrcweir if( fD >= 0.0 ) 1018cdf0e10cSrcweir { 1019cdf0e10cSrcweir const double fS = sqrt(fD); 1020cdf0e10cSrcweir // calculate both roots (avoiding a numerically unstable subtraction) 1021cdf0e10cSrcweir const double fQ = fBY + ((fBY >= 0) ? +fS : -fS); 1022cdf0e10cSrcweir impCheckExtremumResult(fQ / fAY, rResults); 1023cdf0e10cSrcweir if( !fTools::equalZero(fS) ) // ignore root multiplicity 1024cdf0e10cSrcweir impCheckExtremumResult(fCY / fQ, rResults); 1025cdf0e10cSrcweir } 1026cdf0e10cSrcweir } 1027cdf0e10cSrcweir else if( !fTools::equalZero(fBY) ) 1028cdf0e10cSrcweir { 1029cdf0e10cSrcweir // derivative is polynomial of order 1 => one extrema 1030cdf0e10cSrcweir impCheckExtremumResult(fCY / (2 * fBY), rResults); 1031cdf0e10cSrcweir } 1032cdf0e10cSrcweir } 1033cdf0e10cSrcweir getMaxDistancePositions(double pResult[2]) const1034cdf0e10cSrcweir int B2DCubicBezier::getMaxDistancePositions( double pResult[2]) const 1035cdf0e10cSrcweir { 1036cdf0e10cSrcweir // the distance from the bezier to a line through start and end 1037cdf0e10cSrcweir // is proportional to (ENDx-STARTx,ENDy-STARTy)*(+BEZIERy(t)-STARTy,-BEZIERx(t)-STARTx) 1038cdf0e10cSrcweir // this distance becomes zero for at least t==0 and t==1 1039cdf0e10cSrcweir // its extrema that are between 0..1 are interesting as split candidates 1040cdf0e10cSrcweir // its derived function has the form dD/dt = fA*t^2 + 2*fB*t + fC 1041cdf0e10cSrcweir const B2DPoint aRelativeEndPoint(maEndPoint-maStartPoint); 1042cdf0e10cSrcweir const double fA = (3 * (maControlPointA.getX() - maControlPointB.getX()) + aRelativeEndPoint.getX()) * aRelativeEndPoint.getY() 1043cdf0e10cSrcweir - (3 * (maControlPointA.getY() - maControlPointB.getY()) + aRelativeEndPoint.getY()) * aRelativeEndPoint.getX(); 1044cdf0e10cSrcweir const double fB = (maControlPointB.getX() - 2 * maControlPointA.getX() + maStartPoint.getX()) * aRelativeEndPoint.getY() 1045cdf0e10cSrcweir - (maControlPointB.getY() - 2 * maControlPointA.getY() + maStartPoint.getY()) * aRelativeEndPoint.getX(); 1046cdf0e10cSrcweir const double fC = (maControlPointA.getX() - maStartPoint.getX()) * aRelativeEndPoint.getY() 1047cdf0e10cSrcweir - (maControlPointA.getY() - maStartPoint.getY()) * aRelativeEndPoint.getX(); 1048cdf0e10cSrcweir 1049cdf0e10cSrcweir // test for degenerated case: order<2 1050cdf0e10cSrcweir if( fTools::equalZero(fA) ) 1051cdf0e10cSrcweir { 1052cdf0e10cSrcweir // test for degenerated case: order==0 1053cdf0e10cSrcweir if( fTools::equalZero(fB) ) 1054cdf0e10cSrcweir return 0; 1055cdf0e10cSrcweir 1056cdf0e10cSrcweir // solving the order==1 polynomial is trivial 1057cdf0e10cSrcweir pResult[0] = -fC / (2*fB); 1058cdf0e10cSrcweir 1059cdf0e10cSrcweir // test root and ignore it when it is outside the curve 1060cdf0e10cSrcweir int nCount = ((pResult[0] > 0) && (pResult[0] < 1)); 1061cdf0e10cSrcweir return nCount; 1062cdf0e10cSrcweir } 1063cdf0e10cSrcweir 1064cdf0e10cSrcweir // derivative is polynomial of order 2 1065cdf0e10cSrcweir // check if the polynomial has non-imaginary roots 1066cdf0e10cSrcweir const double fD = fB*fB - fA*fC; 1067cdf0e10cSrcweir if( fD >= 0.0 ) // TODO: is this test needed? geometrically not IMHO 1068cdf0e10cSrcweir { 1069cdf0e10cSrcweir // calculate first root (avoiding a numerically unstable subtraction) 1070cdf0e10cSrcweir const double fS = sqrt(fD); 1071cdf0e10cSrcweir const double fQ = -(fB + ((fB >= 0) ? +fS : -fS)); 1072cdf0e10cSrcweir pResult[0] = fQ / fA; 1073cdf0e10cSrcweir // ignore root when it is outside the curve 1074cdf0e10cSrcweir static const double fEps = 1e-9; 1075cdf0e10cSrcweir int nCount = ((pResult[0] > fEps) && (pResult[0] < fEps)); 1076cdf0e10cSrcweir 1077cdf0e10cSrcweir // ignore root multiplicity 1078cdf0e10cSrcweir if( !fTools::equalZero(fD) ) 1079cdf0e10cSrcweir { 1080cdf0e10cSrcweir // calculate the other root 1081cdf0e10cSrcweir const double fRoot = fC / fQ; 1082cdf0e10cSrcweir // ignore root when it is outside the curve 1083cdf0e10cSrcweir if( (fRoot > fEps) && (fRoot < 1.0-fEps) ) 1084cdf0e10cSrcweir pResult[ nCount++ ] = fRoot; 1085cdf0e10cSrcweir } 1086cdf0e10cSrcweir 1087cdf0e10cSrcweir return nCount; 1088cdf0e10cSrcweir } 1089cdf0e10cSrcweir 1090cdf0e10cSrcweir return 0; 1091cdf0e10cSrcweir } 1092cdf0e10cSrcweir 1093cdf0e10cSrcweir } // end of namespace basegfx 1094cdf0e10cSrcweir 1095*42fb6e95Smseidel /* vim: set noet sw=4 ts=4: */ 1096