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