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/numeric/ftools.hxx> 31*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 32*cdf0e10cSrcweir #include <osl/diagnose.h> 33*cdf0e10cSrcweir #include <rtl/math.hxx> 34*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygon.hxx> 35*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygon.hxx> 36*cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx> 37*cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx> 38*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 39*cdf0e10cSrcweir #include <basegfx/point/b3dpoint.hxx> 40*cdf0e10cSrcweir #include <basegfx/matrix/b3dhommatrix.hxx> 41*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 42*cdf0e10cSrcweir #include <basegfx/curve/b2dbeziertools.hxx> 43*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 44*cdf0e10cSrcweir #include <osl/mutex.hxx> 45*cdf0e10cSrcweir 46*cdf0e10cSrcweir #include <numeric> 47*cdf0e10cSrcweir #include <limits> 48*cdf0e10cSrcweir 49*cdf0e10cSrcweir // #i37443# 50*cdf0e10cSrcweir #define ANGLE_BOUND_START_VALUE (2.25) 51*cdf0e10cSrcweir #define ANGLE_BOUND_MINIMUM_VALUE (0.1) 52*cdf0e10cSrcweir #define COUNT_SUBDIVIDE_DEFAULT (4L) 53*cdf0e10cSrcweir #ifdef DBG_UTIL 54*cdf0e10cSrcweir static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE; 55*cdf0e10cSrcweir #endif 56*cdf0e10cSrcweir #define STEPSPERQUARTER (3) 57*cdf0e10cSrcweir 58*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 59*cdf0e10cSrcweir 60*cdf0e10cSrcweir namespace basegfx 61*cdf0e10cSrcweir { 62*cdf0e10cSrcweir namespace tools 63*cdf0e10cSrcweir { 64*cdf0e10cSrcweir void openWithGeometryChange(B2DPolygon& rCandidate) 65*cdf0e10cSrcweir { 66*cdf0e10cSrcweir if(rCandidate.isClosed()) 67*cdf0e10cSrcweir { 68*cdf0e10cSrcweir if(rCandidate.count()) 69*cdf0e10cSrcweir { 70*cdf0e10cSrcweir rCandidate.append(rCandidate.getB2DPoint(0)); 71*cdf0e10cSrcweir 72*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0)) 73*cdf0e10cSrcweir { 74*cdf0e10cSrcweir rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0)); 75*cdf0e10cSrcweir rCandidate.resetPrevControlPoint(0); 76*cdf0e10cSrcweir } 77*cdf0e10cSrcweir } 78*cdf0e10cSrcweir 79*cdf0e10cSrcweir rCandidate.setClosed(false); 80*cdf0e10cSrcweir } 81*cdf0e10cSrcweir } 82*cdf0e10cSrcweir 83*cdf0e10cSrcweir void closeWithGeometryChange(B2DPolygon& rCandidate) 84*cdf0e10cSrcweir { 85*cdf0e10cSrcweir if(!rCandidate.isClosed()) 86*cdf0e10cSrcweir { 87*cdf0e10cSrcweir while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1)) 88*cdf0e10cSrcweir { 89*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1)) 90*cdf0e10cSrcweir { 91*cdf0e10cSrcweir rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1)); 92*cdf0e10cSrcweir } 93*cdf0e10cSrcweir 94*cdf0e10cSrcweir rCandidate.remove(rCandidate.count() - 1); 95*cdf0e10cSrcweir } 96*cdf0e10cSrcweir 97*cdf0e10cSrcweir rCandidate.setClosed(true); 98*cdf0e10cSrcweir } 99*cdf0e10cSrcweir } 100*cdf0e10cSrcweir 101*cdf0e10cSrcweir void checkClosed(B2DPolygon& rCandidate) 102*cdf0e10cSrcweir { 103*cdf0e10cSrcweir // #i80172# Removed unnecessary assertion 104*cdf0e10cSrcweir // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)"); 105*cdf0e10cSrcweir 106*cdf0e10cSrcweir if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1)) 107*cdf0e10cSrcweir { 108*cdf0e10cSrcweir closeWithGeometryChange(rCandidate); 109*cdf0e10cSrcweir } 110*cdf0e10cSrcweir } 111*cdf0e10cSrcweir 112*cdf0e10cSrcweir // Get successor and predecessor indices. Returning the same index means there 113*cdf0e10cSrcweir // is none. Same for successor. 114*cdf0e10cSrcweir sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate) 115*cdf0e10cSrcweir { 116*cdf0e10cSrcweir OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); 117*cdf0e10cSrcweir 118*cdf0e10cSrcweir if(nIndex) 119*cdf0e10cSrcweir { 120*cdf0e10cSrcweir return nIndex - 1L; 121*cdf0e10cSrcweir } 122*cdf0e10cSrcweir else if(rCandidate.count()) 123*cdf0e10cSrcweir { 124*cdf0e10cSrcweir return rCandidate.count() - 1L; 125*cdf0e10cSrcweir } 126*cdf0e10cSrcweir else 127*cdf0e10cSrcweir { 128*cdf0e10cSrcweir return nIndex; 129*cdf0e10cSrcweir } 130*cdf0e10cSrcweir } 131*cdf0e10cSrcweir 132*cdf0e10cSrcweir sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate) 133*cdf0e10cSrcweir { 134*cdf0e10cSrcweir OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); 135*cdf0e10cSrcweir 136*cdf0e10cSrcweir if(nIndex + 1L < rCandidate.count()) 137*cdf0e10cSrcweir { 138*cdf0e10cSrcweir return nIndex + 1L; 139*cdf0e10cSrcweir } 140*cdf0e10cSrcweir else if(nIndex + 1L == rCandidate.count()) 141*cdf0e10cSrcweir { 142*cdf0e10cSrcweir return 0L; 143*cdf0e10cSrcweir } 144*cdf0e10cSrcweir else 145*cdf0e10cSrcweir { 146*cdf0e10cSrcweir return nIndex; 147*cdf0e10cSrcweir } 148*cdf0e10cSrcweir } 149*cdf0e10cSrcweir 150*cdf0e10cSrcweir B2VectorOrientation getOrientation(const B2DPolygon& rCandidate) 151*cdf0e10cSrcweir { 152*cdf0e10cSrcweir B2VectorOrientation eRetval(ORIENTATION_NEUTRAL); 153*cdf0e10cSrcweir 154*cdf0e10cSrcweir if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed()) 155*cdf0e10cSrcweir { 156*cdf0e10cSrcweir const double fSignedArea(getSignedArea(rCandidate)); 157*cdf0e10cSrcweir 158*cdf0e10cSrcweir if(fTools::equalZero(fSignedArea)) 159*cdf0e10cSrcweir { 160*cdf0e10cSrcweir // ORIENTATION_NEUTRAL, already set 161*cdf0e10cSrcweir } 162*cdf0e10cSrcweir if(fSignedArea > 0.0) 163*cdf0e10cSrcweir { 164*cdf0e10cSrcweir eRetval = ORIENTATION_POSITIVE; 165*cdf0e10cSrcweir } 166*cdf0e10cSrcweir else if(fSignedArea < 0.0) 167*cdf0e10cSrcweir { 168*cdf0e10cSrcweir eRetval = ORIENTATION_NEGATIVE; 169*cdf0e10cSrcweir } 170*cdf0e10cSrcweir } 171*cdf0e10cSrcweir 172*cdf0e10cSrcweir return eRetval; 173*cdf0e10cSrcweir } 174*cdf0e10cSrcweir 175*cdf0e10cSrcweir B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex) 176*cdf0e10cSrcweir { 177*cdf0e10cSrcweir return rCandidate.getContinuityInPoint(nIndex); 178*cdf0e10cSrcweir } 179*cdf0e10cSrcweir 180*cdf0e10cSrcweir B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound) 181*cdf0e10cSrcweir { 182*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 183*cdf0e10cSrcweir { 184*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 185*cdf0e10cSrcweir B2DPolygon aRetval; 186*cdf0e10cSrcweir 187*cdf0e10cSrcweir if(nPointCount) 188*cdf0e10cSrcweir { 189*cdf0e10cSrcweir // prepare edge-oriented loop 190*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 191*cdf0e10cSrcweir B2DCubicBezier aBezier; 192*cdf0e10cSrcweir aBezier.setStartPoint(rCandidate.getB2DPoint(0)); 193*cdf0e10cSrcweir 194*cdf0e10cSrcweir // perf: try to avoid too many realloctions by guessing the result's pointcount 195*cdf0e10cSrcweir aRetval.reserve(nPointCount*4); 196*cdf0e10cSrcweir 197*cdf0e10cSrcweir // add start point (always) 198*cdf0e10cSrcweir aRetval.append(aBezier.getStartPoint()); 199*cdf0e10cSrcweir 200*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 201*cdf0e10cSrcweir { 202*cdf0e10cSrcweir // get next and control points 203*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 204*cdf0e10cSrcweir aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 205*cdf0e10cSrcweir aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); 206*cdf0e10cSrcweir aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 207*cdf0e10cSrcweir aBezier.testAndSolveTrivialBezier(); 208*cdf0e10cSrcweir 209*cdf0e10cSrcweir if(aBezier.isBezier()) 210*cdf0e10cSrcweir { 211*cdf0e10cSrcweir // add curved edge and generate DistanceBound 212*cdf0e10cSrcweir double fBound(0.0); 213*cdf0e10cSrcweir 214*cdf0e10cSrcweir if(0.0 == fDistanceBound) 215*cdf0e10cSrcweir { 216*cdf0e10cSrcweir // If not set, use B2DCubicBezier functionality to guess a rough value 217*cdf0e10cSrcweir const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0); 218*cdf0e10cSrcweir 219*cdf0e10cSrcweir // take 1/100th of the rough curve length 220*cdf0e10cSrcweir fBound = fRoughLength * 0.01; 221*cdf0e10cSrcweir } 222*cdf0e10cSrcweir else 223*cdf0e10cSrcweir { 224*cdf0e10cSrcweir // use given bound value 225*cdf0e10cSrcweir fBound = fDistanceBound; 226*cdf0e10cSrcweir } 227*cdf0e10cSrcweir 228*cdf0e10cSrcweir // make sure bound value is not too small. The base units are 1/100th mm, thus 229*cdf0e10cSrcweir // just make sure it's not smaller then 1/100th of that 230*cdf0e10cSrcweir if(fBound < 0.01) 231*cdf0e10cSrcweir { 232*cdf0e10cSrcweir fBound = 0.01; 233*cdf0e10cSrcweir } 234*cdf0e10cSrcweir 235*cdf0e10cSrcweir // call adaptive subdivide which adds edges to aRetval accordingly 236*cdf0e10cSrcweir aBezier.adaptiveSubdivideByDistance(aRetval, fBound); 237*cdf0e10cSrcweir } 238*cdf0e10cSrcweir else 239*cdf0e10cSrcweir { 240*cdf0e10cSrcweir // add non-curved edge 241*cdf0e10cSrcweir aRetval.append(aBezier.getEndPoint()); 242*cdf0e10cSrcweir } 243*cdf0e10cSrcweir 244*cdf0e10cSrcweir // prepare next step 245*cdf0e10cSrcweir aBezier.setStartPoint(aBezier.getEndPoint()); 246*cdf0e10cSrcweir } 247*cdf0e10cSrcweir 248*cdf0e10cSrcweir if(rCandidate.isClosed()) 249*cdf0e10cSrcweir { 250*cdf0e10cSrcweir // set closed flag and correct last point (which is added double now). 251*cdf0e10cSrcweir closeWithGeometryChange(aRetval); 252*cdf0e10cSrcweir } 253*cdf0e10cSrcweir } 254*cdf0e10cSrcweir 255*cdf0e10cSrcweir return aRetval; 256*cdf0e10cSrcweir } 257*cdf0e10cSrcweir else 258*cdf0e10cSrcweir { 259*cdf0e10cSrcweir return rCandidate; 260*cdf0e10cSrcweir } 261*cdf0e10cSrcweir } 262*cdf0e10cSrcweir 263*cdf0e10cSrcweir B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound) 264*cdf0e10cSrcweir { 265*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 266*cdf0e10cSrcweir { 267*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 268*cdf0e10cSrcweir B2DPolygon aRetval; 269*cdf0e10cSrcweir 270*cdf0e10cSrcweir if(nPointCount) 271*cdf0e10cSrcweir { 272*cdf0e10cSrcweir // prepare edge-oriented loop 273*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 274*cdf0e10cSrcweir B2DCubicBezier aBezier; 275*cdf0e10cSrcweir aBezier.setStartPoint(rCandidate.getB2DPoint(0)); 276*cdf0e10cSrcweir 277*cdf0e10cSrcweir // perf: try to avoid too many realloctions by guessing the result's pointcount 278*cdf0e10cSrcweir aRetval.reserve(nPointCount*4); 279*cdf0e10cSrcweir 280*cdf0e10cSrcweir // add start point (always) 281*cdf0e10cSrcweir aRetval.append(aBezier.getStartPoint()); 282*cdf0e10cSrcweir 283*cdf0e10cSrcweir // #i37443# prepare convenient AngleBound if none was given 284*cdf0e10cSrcweir if(0.0 == fAngleBound) 285*cdf0e10cSrcweir { 286*cdf0e10cSrcweir #ifdef DBG_UTIL 287*cdf0e10cSrcweir fAngleBound = fAngleBoundStartValue; 288*cdf0e10cSrcweir #else 289*cdf0e10cSrcweir fAngleBound = ANGLE_BOUND_START_VALUE; 290*cdf0e10cSrcweir #endif 291*cdf0e10cSrcweir } 292*cdf0e10cSrcweir else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE)) 293*cdf0e10cSrcweir { 294*cdf0e10cSrcweir fAngleBound = 0.1; 295*cdf0e10cSrcweir } 296*cdf0e10cSrcweir 297*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 298*cdf0e10cSrcweir { 299*cdf0e10cSrcweir // get next and control points 300*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 301*cdf0e10cSrcweir aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 302*cdf0e10cSrcweir aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); 303*cdf0e10cSrcweir aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 304*cdf0e10cSrcweir aBezier.testAndSolveTrivialBezier(); 305*cdf0e10cSrcweir 306*cdf0e10cSrcweir if(aBezier.isBezier()) 307*cdf0e10cSrcweir { 308*cdf0e10cSrcweir // call adaptive subdivide 309*cdf0e10cSrcweir aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true); 310*cdf0e10cSrcweir } 311*cdf0e10cSrcweir else 312*cdf0e10cSrcweir { 313*cdf0e10cSrcweir // add non-curved edge 314*cdf0e10cSrcweir aRetval.append(aBezier.getEndPoint()); 315*cdf0e10cSrcweir } 316*cdf0e10cSrcweir 317*cdf0e10cSrcweir // prepare next step 318*cdf0e10cSrcweir aBezier.setStartPoint(aBezier.getEndPoint()); 319*cdf0e10cSrcweir } 320*cdf0e10cSrcweir 321*cdf0e10cSrcweir if(rCandidate.isClosed()) 322*cdf0e10cSrcweir { 323*cdf0e10cSrcweir // set closed flag and correct last point (which is added double now). 324*cdf0e10cSrcweir closeWithGeometryChange(aRetval); 325*cdf0e10cSrcweir } 326*cdf0e10cSrcweir } 327*cdf0e10cSrcweir 328*cdf0e10cSrcweir return aRetval; 329*cdf0e10cSrcweir } 330*cdf0e10cSrcweir else 331*cdf0e10cSrcweir { 332*cdf0e10cSrcweir return rCandidate; 333*cdf0e10cSrcweir } 334*cdf0e10cSrcweir } 335*cdf0e10cSrcweir 336*cdf0e10cSrcweir B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount) 337*cdf0e10cSrcweir { 338*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 339*cdf0e10cSrcweir { 340*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 341*cdf0e10cSrcweir B2DPolygon aRetval; 342*cdf0e10cSrcweir 343*cdf0e10cSrcweir if(nPointCount) 344*cdf0e10cSrcweir { 345*cdf0e10cSrcweir // prepare edge-oriented loop 346*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 347*cdf0e10cSrcweir B2DCubicBezier aBezier; 348*cdf0e10cSrcweir aBezier.setStartPoint(rCandidate.getB2DPoint(0)); 349*cdf0e10cSrcweir 350*cdf0e10cSrcweir // perf: try to avoid too many realloctions by guessing the result's pointcount 351*cdf0e10cSrcweir aRetval.reserve(nPointCount*4); 352*cdf0e10cSrcweir 353*cdf0e10cSrcweir // add start point (always) 354*cdf0e10cSrcweir aRetval.append(aBezier.getStartPoint()); 355*cdf0e10cSrcweir 356*cdf0e10cSrcweir // #i37443# prepare convenient count if none was given 357*cdf0e10cSrcweir if(0L == nCount) 358*cdf0e10cSrcweir { 359*cdf0e10cSrcweir nCount = COUNT_SUBDIVIDE_DEFAULT; 360*cdf0e10cSrcweir } 361*cdf0e10cSrcweir 362*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 363*cdf0e10cSrcweir { 364*cdf0e10cSrcweir // get next and control points 365*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 366*cdf0e10cSrcweir aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 367*cdf0e10cSrcweir aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); 368*cdf0e10cSrcweir aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 369*cdf0e10cSrcweir aBezier.testAndSolveTrivialBezier(); 370*cdf0e10cSrcweir 371*cdf0e10cSrcweir if(aBezier.isBezier()) 372*cdf0e10cSrcweir { 373*cdf0e10cSrcweir // call adaptive subdivide 374*cdf0e10cSrcweir aBezier.adaptiveSubdivideByCount(aRetval, nCount); 375*cdf0e10cSrcweir } 376*cdf0e10cSrcweir else 377*cdf0e10cSrcweir { 378*cdf0e10cSrcweir // add non-curved edge 379*cdf0e10cSrcweir aRetval.append(aBezier.getEndPoint()); 380*cdf0e10cSrcweir } 381*cdf0e10cSrcweir 382*cdf0e10cSrcweir // prepare next step 383*cdf0e10cSrcweir aBezier.setStartPoint(aBezier.getEndPoint()); 384*cdf0e10cSrcweir } 385*cdf0e10cSrcweir 386*cdf0e10cSrcweir if(rCandidate.isClosed()) 387*cdf0e10cSrcweir { 388*cdf0e10cSrcweir // set closed flag and correct last point (which is added double now). 389*cdf0e10cSrcweir closeWithGeometryChange(aRetval); 390*cdf0e10cSrcweir } 391*cdf0e10cSrcweir } 392*cdf0e10cSrcweir 393*cdf0e10cSrcweir return aRetval; 394*cdf0e10cSrcweir } 395*cdf0e10cSrcweir else 396*cdf0e10cSrcweir { 397*cdf0e10cSrcweir return rCandidate; 398*cdf0e10cSrcweir } 399*cdf0e10cSrcweir } 400*cdf0e10cSrcweir 401*cdf0e10cSrcweir bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder) 402*cdf0e10cSrcweir { 403*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate); 404*cdf0e10cSrcweir 405*cdf0e10cSrcweir if(bWithBorder && isPointOnPolygon(aCandidate, rPoint, true)) 406*cdf0e10cSrcweir { 407*cdf0e10cSrcweir return true; 408*cdf0e10cSrcweir } 409*cdf0e10cSrcweir else 410*cdf0e10cSrcweir { 411*cdf0e10cSrcweir bool bRetval(false); 412*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 413*cdf0e10cSrcweir 414*cdf0e10cSrcweir if(nPointCount) 415*cdf0e10cSrcweir { 416*cdf0e10cSrcweir B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1L)); 417*cdf0e10cSrcweir 418*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 419*cdf0e10cSrcweir { 420*cdf0e10cSrcweir const B2DPoint aPreviousPoint(aCurrentPoint); 421*cdf0e10cSrcweir aCurrentPoint = aCandidate.getB2DPoint(a); 422*cdf0e10cSrcweir 423*cdf0e10cSrcweir // cross-over in Y? 424*cdf0e10cSrcweir const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); 425*cdf0e10cSrcweir const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); 426*cdf0e10cSrcweir 427*cdf0e10cSrcweir if(bCompYA != bCompYB) 428*cdf0e10cSrcweir { 429*cdf0e10cSrcweir // cross-over in X? 430*cdf0e10cSrcweir const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); 431*cdf0e10cSrcweir const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); 432*cdf0e10cSrcweir 433*cdf0e10cSrcweir if(bCompXA == bCompXB) 434*cdf0e10cSrcweir { 435*cdf0e10cSrcweir if(bCompXA) 436*cdf0e10cSrcweir { 437*cdf0e10cSrcweir bRetval = !bRetval; 438*cdf0e10cSrcweir } 439*cdf0e10cSrcweir } 440*cdf0e10cSrcweir else 441*cdf0e10cSrcweir { 442*cdf0e10cSrcweir const double fCompare( 443*cdf0e10cSrcweir aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) * 444*cdf0e10cSrcweir (aPreviousPoint.getX() - aCurrentPoint.getX()) / 445*cdf0e10cSrcweir (aPreviousPoint.getY() - aCurrentPoint.getY())); 446*cdf0e10cSrcweir 447*cdf0e10cSrcweir if(fTools::more(fCompare, rPoint.getX())) 448*cdf0e10cSrcweir { 449*cdf0e10cSrcweir bRetval = !bRetval; 450*cdf0e10cSrcweir } 451*cdf0e10cSrcweir } 452*cdf0e10cSrcweir } 453*cdf0e10cSrcweir } 454*cdf0e10cSrcweir } 455*cdf0e10cSrcweir 456*cdf0e10cSrcweir return bRetval; 457*cdf0e10cSrcweir } 458*cdf0e10cSrcweir } 459*cdf0e10cSrcweir 460*cdf0e10cSrcweir bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder) 461*cdf0e10cSrcweir { 462*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate); 463*cdf0e10cSrcweir const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon); 464*cdf0e10cSrcweir const sal_uInt32 nPointCount(aPolygon.count()); 465*cdf0e10cSrcweir 466*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 467*cdf0e10cSrcweir { 468*cdf0e10cSrcweir const B2DPoint aTestPoint(aPolygon.getB2DPoint(a)); 469*cdf0e10cSrcweir 470*cdf0e10cSrcweir if(!isInside(aCandidate, aTestPoint, bWithBorder)) 471*cdf0e10cSrcweir { 472*cdf0e10cSrcweir return false; 473*cdf0e10cSrcweir } 474*cdf0e10cSrcweir } 475*cdf0e10cSrcweir 476*cdf0e10cSrcweir return true; 477*cdf0e10cSrcweir } 478*cdf0e10cSrcweir 479*cdf0e10cSrcweir B2DRange getRangeWithControlPoints(const B2DPolygon& rCandidate) 480*cdf0e10cSrcweir { 481*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 482*cdf0e10cSrcweir B2DRange aRetval; 483*cdf0e10cSrcweir 484*cdf0e10cSrcweir if(nPointCount) 485*cdf0e10cSrcweir { 486*cdf0e10cSrcweir const bool bControlPointsUsed(rCandidate.areControlPointsUsed()); 487*cdf0e10cSrcweir 488*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nPointCount; a++) 489*cdf0e10cSrcweir { 490*cdf0e10cSrcweir aRetval.expand(rCandidate.getB2DPoint(a)); 491*cdf0e10cSrcweir 492*cdf0e10cSrcweir if(bControlPointsUsed) 493*cdf0e10cSrcweir { 494*cdf0e10cSrcweir aRetval.expand(rCandidate.getNextControlPoint(a)); 495*cdf0e10cSrcweir aRetval.expand(rCandidate.getPrevControlPoint(a)); 496*cdf0e10cSrcweir } 497*cdf0e10cSrcweir } 498*cdf0e10cSrcweir } 499*cdf0e10cSrcweir 500*cdf0e10cSrcweir return aRetval; 501*cdf0e10cSrcweir } 502*cdf0e10cSrcweir 503*cdf0e10cSrcweir B2DRange getRange(const B2DPolygon& rCandidate) 504*cdf0e10cSrcweir { 505*cdf0e10cSrcweir // changed to use internally buffered version at B2DPolygon 506*cdf0e10cSrcweir return rCandidate.getB2DRange(); 507*cdf0e10cSrcweir } 508*cdf0e10cSrcweir 509*cdf0e10cSrcweir double getSignedArea(const B2DPolygon& rCandidate) 510*cdf0e10cSrcweir { 511*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate); 512*cdf0e10cSrcweir double fRetval(0.0); 513*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 514*cdf0e10cSrcweir 515*cdf0e10cSrcweir if(nPointCount > 2) 516*cdf0e10cSrcweir { 517*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 518*cdf0e10cSrcweir { 519*cdf0e10cSrcweir const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L)); 520*cdf0e10cSrcweir const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a)); 521*cdf0e10cSrcweir 522*cdf0e10cSrcweir fRetval += aPreviousPoint.getX() * aCurrentPoint.getY(); 523*cdf0e10cSrcweir fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX(); 524*cdf0e10cSrcweir } 525*cdf0e10cSrcweir 526*cdf0e10cSrcweir fRetval /= 2.0; 527*cdf0e10cSrcweir 528*cdf0e10cSrcweir // correct to zero if small enough. Also test the quadratic 529*cdf0e10cSrcweir // of the result since the precision is near quadratic due to 530*cdf0e10cSrcweir // the algorithm 531*cdf0e10cSrcweir if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval)) 532*cdf0e10cSrcweir { 533*cdf0e10cSrcweir fRetval = 0.0; 534*cdf0e10cSrcweir } 535*cdf0e10cSrcweir } 536*cdf0e10cSrcweir 537*cdf0e10cSrcweir return fRetval; 538*cdf0e10cSrcweir } 539*cdf0e10cSrcweir 540*cdf0e10cSrcweir double getArea(const B2DPolygon& rCandidate) 541*cdf0e10cSrcweir { 542*cdf0e10cSrcweir double fRetval(0.0); 543*cdf0e10cSrcweir 544*cdf0e10cSrcweir if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed()) 545*cdf0e10cSrcweir { 546*cdf0e10cSrcweir fRetval = getSignedArea(rCandidate); 547*cdf0e10cSrcweir const double fZero(0.0); 548*cdf0e10cSrcweir 549*cdf0e10cSrcweir if(fTools::less(fRetval, fZero)) 550*cdf0e10cSrcweir { 551*cdf0e10cSrcweir fRetval = -fRetval; 552*cdf0e10cSrcweir } 553*cdf0e10cSrcweir } 554*cdf0e10cSrcweir 555*cdf0e10cSrcweir return fRetval; 556*cdf0e10cSrcweir } 557*cdf0e10cSrcweir 558*cdf0e10cSrcweir double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex) 559*cdf0e10cSrcweir { 560*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 561*cdf0e10cSrcweir OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)"); 562*cdf0e10cSrcweir double fRetval(0.0); 563*cdf0e10cSrcweir 564*cdf0e10cSrcweir if(nPointCount) 565*cdf0e10cSrcweir { 566*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 567*cdf0e10cSrcweir 568*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 569*cdf0e10cSrcweir { 570*cdf0e10cSrcweir B2DCubicBezier aEdge; 571*cdf0e10cSrcweir 572*cdf0e10cSrcweir aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex)); 573*cdf0e10cSrcweir aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex)); 574*cdf0e10cSrcweir aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 575*cdf0e10cSrcweir aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 576*cdf0e10cSrcweir 577*cdf0e10cSrcweir fRetval = aEdge.getLength(); 578*cdf0e10cSrcweir } 579*cdf0e10cSrcweir else 580*cdf0e10cSrcweir { 581*cdf0e10cSrcweir const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex)); 582*cdf0e10cSrcweir const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex)); 583*cdf0e10cSrcweir 584*cdf0e10cSrcweir fRetval = B2DVector(aNext - aCurrent).getLength(); 585*cdf0e10cSrcweir } 586*cdf0e10cSrcweir } 587*cdf0e10cSrcweir 588*cdf0e10cSrcweir return fRetval; 589*cdf0e10cSrcweir } 590*cdf0e10cSrcweir 591*cdf0e10cSrcweir double getLength(const B2DPolygon& rCandidate) 592*cdf0e10cSrcweir { 593*cdf0e10cSrcweir double fRetval(0.0); 594*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 595*cdf0e10cSrcweir 596*cdf0e10cSrcweir if(nPointCount) 597*cdf0e10cSrcweir { 598*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 599*cdf0e10cSrcweir 600*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 601*cdf0e10cSrcweir { 602*cdf0e10cSrcweir B2DCubicBezier aEdge; 603*cdf0e10cSrcweir aEdge.setStartPoint(rCandidate.getB2DPoint(0)); 604*cdf0e10cSrcweir 605*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 606*cdf0e10cSrcweir { 607*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 608*cdf0e10cSrcweir aEdge.setControlPointA(rCandidate.getNextControlPoint(a)); 609*cdf0e10cSrcweir aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 610*cdf0e10cSrcweir aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 611*cdf0e10cSrcweir 612*cdf0e10cSrcweir fRetval += aEdge.getLength(); 613*cdf0e10cSrcweir aEdge.setStartPoint(aEdge.getEndPoint()); 614*cdf0e10cSrcweir } 615*cdf0e10cSrcweir } 616*cdf0e10cSrcweir else 617*cdf0e10cSrcweir { 618*cdf0e10cSrcweir B2DPoint aCurrent(rCandidate.getB2DPoint(0)); 619*cdf0e10cSrcweir 620*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 621*cdf0e10cSrcweir { 622*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 623*cdf0e10cSrcweir const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex)); 624*cdf0e10cSrcweir 625*cdf0e10cSrcweir fRetval += B2DVector(aNext - aCurrent).getLength(); 626*cdf0e10cSrcweir aCurrent = aNext; 627*cdf0e10cSrcweir } 628*cdf0e10cSrcweir } 629*cdf0e10cSrcweir } 630*cdf0e10cSrcweir 631*cdf0e10cSrcweir return fRetval; 632*cdf0e10cSrcweir } 633*cdf0e10cSrcweir 634*cdf0e10cSrcweir B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength) 635*cdf0e10cSrcweir { 636*cdf0e10cSrcweir B2DPoint aRetval; 637*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 638*cdf0e10cSrcweir 639*cdf0e10cSrcweir if( 1L == nPointCount ) 640*cdf0e10cSrcweir { 641*cdf0e10cSrcweir // only one point (i.e. no edge) - simply take that point 642*cdf0e10cSrcweir aRetval = rCandidate.getB2DPoint(0); 643*cdf0e10cSrcweir } 644*cdf0e10cSrcweir else if(nPointCount > 1L) 645*cdf0e10cSrcweir { 646*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 647*cdf0e10cSrcweir sal_uInt32 nIndex(0L); 648*cdf0e10cSrcweir bool bIndexDone(false); 649*cdf0e10cSrcweir 650*cdf0e10cSrcweir // get length if not given 651*cdf0e10cSrcweir if(fTools::equalZero(fLength)) 652*cdf0e10cSrcweir { 653*cdf0e10cSrcweir fLength = getLength(rCandidate); 654*cdf0e10cSrcweir } 655*cdf0e10cSrcweir 656*cdf0e10cSrcweir if(fTools::less(fDistance, 0.0)) 657*cdf0e10cSrcweir { 658*cdf0e10cSrcweir // handle fDistance < 0.0 659*cdf0e10cSrcweir if(rCandidate.isClosed()) 660*cdf0e10cSrcweir { 661*cdf0e10cSrcweir // if fDistance < 0.0 increment with multiple of fLength 662*cdf0e10cSrcweir sal_uInt32 nCount(sal_uInt32(-fDistance / fLength)); 663*cdf0e10cSrcweir fDistance += double(nCount + 1L) * fLength; 664*cdf0e10cSrcweir } 665*cdf0e10cSrcweir else 666*cdf0e10cSrcweir { 667*cdf0e10cSrcweir // crop to polygon start 668*cdf0e10cSrcweir fDistance = 0.0; 669*cdf0e10cSrcweir bIndexDone = true; 670*cdf0e10cSrcweir } 671*cdf0e10cSrcweir } 672*cdf0e10cSrcweir else if(fTools::moreOrEqual(fDistance, fLength)) 673*cdf0e10cSrcweir { 674*cdf0e10cSrcweir // handle fDistance >= fLength 675*cdf0e10cSrcweir if(rCandidate.isClosed()) 676*cdf0e10cSrcweir { 677*cdf0e10cSrcweir // if fDistance >= fLength decrement with multiple of fLength 678*cdf0e10cSrcweir sal_uInt32 nCount(sal_uInt32(fDistance / fLength)); 679*cdf0e10cSrcweir fDistance -= (double)(nCount) * fLength; 680*cdf0e10cSrcweir } 681*cdf0e10cSrcweir else 682*cdf0e10cSrcweir { 683*cdf0e10cSrcweir // crop to polygon end 684*cdf0e10cSrcweir fDistance = 0.0; 685*cdf0e10cSrcweir nIndex = nEdgeCount; 686*cdf0e10cSrcweir bIndexDone = true; 687*cdf0e10cSrcweir } 688*cdf0e10cSrcweir } 689*cdf0e10cSrcweir 690*cdf0e10cSrcweir // look for correct index. fDistance is now [0.0 .. fLength[ 691*cdf0e10cSrcweir double fEdgeLength(getEdgeLength(rCandidate, nIndex)); 692*cdf0e10cSrcweir 693*cdf0e10cSrcweir while(!bIndexDone) 694*cdf0e10cSrcweir { 695*cdf0e10cSrcweir // edge found must be on the half-open range 696*cdf0e10cSrcweir // [0,fEdgeLength). 697*cdf0e10cSrcweir // Note that in theory, we cannot move beyond 698*cdf0e10cSrcweir // the last polygon point, since fDistance>=fLength 699*cdf0e10cSrcweir // is checked above. Unfortunately, with floating- 700*cdf0e10cSrcweir // point calculations, this case might happen. 701*cdf0e10cSrcweir // Handled by nIndex check below 702*cdf0e10cSrcweir if(nIndex < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength)) 703*cdf0e10cSrcweir { 704*cdf0e10cSrcweir // go to next edge 705*cdf0e10cSrcweir fDistance -= fEdgeLength; 706*cdf0e10cSrcweir fEdgeLength = getEdgeLength(rCandidate, ++nIndex); 707*cdf0e10cSrcweir } 708*cdf0e10cSrcweir else 709*cdf0e10cSrcweir { 710*cdf0e10cSrcweir // it's on this edge, stop 711*cdf0e10cSrcweir bIndexDone = true; 712*cdf0e10cSrcweir } 713*cdf0e10cSrcweir } 714*cdf0e10cSrcweir 715*cdf0e10cSrcweir // get the point using nIndex 716*cdf0e10cSrcweir aRetval = rCandidate.getB2DPoint(nIndex); 717*cdf0e10cSrcweir 718*cdf0e10cSrcweir // if fDistance != 0.0, move that length on the edge. The edge 719*cdf0e10cSrcweir // length is in fEdgeLength. 720*cdf0e10cSrcweir if(!fTools::equalZero(fDistance)) 721*cdf0e10cSrcweir { 722*cdf0e10cSrcweir if(fTools::moreOrEqual(fDistance, fEdgeLength)) 723*cdf0e10cSrcweir { 724*cdf0e10cSrcweir // end point of choosen edge 725*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 726*cdf0e10cSrcweir aRetval = rCandidate.getB2DPoint(nNextIndex); 727*cdf0e10cSrcweir } 728*cdf0e10cSrcweir else if(fTools::equalZero(fDistance)) 729*cdf0e10cSrcweir { 730*cdf0e10cSrcweir // start point of choosen edge 731*cdf0e10cSrcweir aRetval = aRetval; 732*cdf0e10cSrcweir } 733*cdf0e10cSrcweir else 734*cdf0e10cSrcweir { 735*cdf0e10cSrcweir // inside edge 736*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 737*cdf0e10cSrcweir const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex)); 738*cdf0e10cSrcweir bool bDone(false); 739*cdf0e10cSrcweir 740*cdf0e10cSrcweir // add calculated average value to the return value 741*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 742*cdf0e10cSrcweir { 743*cdf0e10cSrcweir // get as bezier segment 744*cdf0e10cSrcweir const B2DCubicBezier aBezierSegment( 745*cdf0e10cSrcweir aRetval, rCandidate.getNextControlPoint(nIndex), 746*cdf0e10cSrcweir rCandidate.getPrevControlPoint(nNextIndex), aNextPoint); 747*cdf0e10cSrcweir 748*cdf0e10cSrcweir if(aBezierSegment.isBezier()) 749*cdf0e10cSrcweir { 750*cdf0e10cSrcweir // use B2DCubicBezierHelper to bridge the non-linear gap between 751*cdf0e10cSrcweir // length and bezier distances 752*cdf0e10cSrcweir const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment); 753*cdf0e10cSrcweir const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance)); 754*cdf0e10cSrcweir 755*cdf0e10cSrcweir aRetval = aBezierSegment.interpolatePoint(fBezierDistance); 756*cdf0e10cSrcweir bDone = true; 757*cdf0e10cSrcweir } 758*cdf0e10cSrcweir } 759*cdf0e10cSrcweir 760*cdf0e10cSrcweir if(!bDone) 761*cdf0e10cSrcweir { 762*cdf0e10cSrcweir const double fRelativeInEdge(fDistance / fEdgeLength); 763*cdf0e10cSrcweir aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge); 764*cdf0e10cSrcweir } 765*cdf0e10cSrcweir } 766*cdf0e10cSrcweir } 767*cdf0e10cSrcweir } 768*cdf0e10cSrcweir 769*cdf0e10cSrcweir return aRetval; 770*cdf0e10cSrcweir } 771*cdf0e10cSrcweir 772*cdf0e10cSrcweir B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength) 773*cdf0e10cSrcweir { 774*cdf0e10cSrcweir // get length if not given 775*cdf0e10cSrcweir if(fTools::equalZero(fLength)) 776*cdf0e10cSrcweir { 777*cdf0e10cSrcweir fLength = getLength(rCandidate); 778*cdf0e10cSrcweir } 779*cdf0e10cSrcweir 780*cdf0e10cSrcweir // multiply fDistance with real length to get absolute position and 781*cdf0e10cSrcweir // use getPositionAbsolute 782*cdf0e10cSrcweir return getPositionAbsolute(rCandidate, fDistance * fLength, fLength); 783*cdf0e10cSrcweir } 784*cdf0e10cSrcweir 785*cdf0e10cSrcweir B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength) 786*cdf0e10cSrcweir { 787*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 788*cdf0e10cSrcweir 789*cdf0e10cSrcweir if(nPointCount) 790*cdf0e10cSrcweir { 791*cdf0e10cSrcweir // get length if not given 792*cdf0e10cSrcweir if(fTools::equalZero(fLength)) 793*cdf0e10cSrcweir { 794*cdf0e10cSrcweir fLength = getLength(rCandidate); 795*cdf0e10cSrcweir } 796*cdf0e10cSrcweir 797*cdf0e10cSrcweir // test and correct fFrom 798*cdf0e10cSrcweir if(fTools::less(fFrom, 0.0)) 799*cdf0e10cSrcweir { 800*cdf0e10cSrcweir fFrom = 0.0; 801*cdf0e10cSrcweir } 802*cdf0e10cSrcweir 803*cdf0e10cSrcweir // test and correct fTo 804*cdf0e10cSrcweir if(fTools::more(fTo, fLength)) 805*cdf0e10cSrcweir { 806*cdf0e10cSrcweir fTo = fLength; 807*cdf0e10cSrcweir } 808*cdf0e10cSrcweir 809*cdf0e10cSrcweir // test and correct relationship of fFrom, fTo 810*cdf0e10cSrcweir if(fTools::more(fFrom, fTo)) 811*cdf0e10cSrcweir { 812*cdf0e10cSrcweir fFrom = fTo = (fFrom + fTo) / 2.0; 813*cdf0e10cSrcweir } 814*cdf0e10cSrcweir 815*cdf0e10cSrcweir if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength)) 816*cdf0e10cSrcweir { 817*cdf0e10cSrcweir // no change, result is the whole polygon 818*cdf0e10cSrcweir return rCandidate; 819*cdf0e10cSrcweir } 820*cdf0e10cSrcweir else 821*cdf0e10cSrcweir { 822*cdf0e10cSrcweir B2DPolygon aRetval; 823*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 824*cdf0e10cSrcweir double fPositionOfStart(0.0); 825*cdf0e10cSrcweir bool bStartDone(false); 826*cdf0e10cSrcweir bool bEndDone(false); 827*cdf0e10cSrcweir 828*cdf0e10cSrcweir for(sal_uInt32 a(0L); !(bStartDone && bEndDone) && a < nEdgeCount; a++) 829*cdf0e10cSrcweir { 830*cdf0e10cSrcweir const double fEdgeLength(getEdgeLength(rCandidate, a)); 831*cdf0e10cSrcweir 832*cdf0e10cSrcweir if(!bStartDone) 833*cdf0e10cSrcweir { 834*cdf0e10cSrcweir if(fTools::equalZero(fFrom)) 835*cdf0e10cSrcweir { 836*cdf0e10cSrcweir aRetval.append(rCandidate.getB2DPoint(a)); 837*cdf0e10cSrcweir 838*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 839*cdf0e10cSrcweir { 840*cdf0e10cSrcweir aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a)); 841*cdf0e10cSrcweir } 842*cdf0e10cSrcweir 843*cdf0e10cSrcweir bStartDone = true; 844*cdf0e10cSrcweir } 845*cdf0e10cSrcweir else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength)) 846*cdf0e10cSrcweir { 847*cdf0e10cSrcweir // calculate and add start point 848*cdf0e10cSrcweir if(fTools::equalZero(fEdgeLength)) 849*cdf0e10cSrcweir { 850*cdf0e10cSrcweir aRetval.append(rCandidate.getB2DPoint(a)); 851*cdf0e10cSrcweir 852*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 853*cdf0e10cSrcweir { 854*cdf0e10cSrcweir aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a)); 855*cdf0e10cSrcweir } 856*cdf0e10cSrcweir } 857*cdf0e10cSrcweir else 858*cdf0e10cSrcweir { 859*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 860*cdf0e10cSrcweir const B2DPoint aStart(rCandidate.getB2DPoint(a)); 861*cdf0e10cSrcweir const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex)); 862*cdf0e10cSrcweir bool bDone(false); 863*cdf0e10cSrcweir 864*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 865*cdf0e10cSrcweir { 866*cdf0e10cSrcweir const B2DCubicBezier aBezierSegment( 867*cdf0e10cSrcweir aStart, rCandidate.getNextControlPoint(a), 868*cdf0e10cSrcweir rCandidate.getPrevControlPoint(nNextIndex), aEnd); 869*cdf0e10cSrcweir 870*cdf0e10cSrcweir if(aBezierSegment.isBezier()) 871*cdf0e10cSrcweir { 872*cdf0e10cSrcweir // use B2DCubicBezierHelper to bridge the non-linear gap between 873*cdf0e10cSrcweir // length and bezier distances 874*cdf0e10cSrcweir const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment); 875*cdf0e10cSrcweir const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart)); 876*cdf0e10cSrcweir B2DCubicBezier aRight; 877*cdf0e10cSrcweir 878*cdf0e10cSrcweir aBezierSegment.split(fBezierDistance, 0, &aRight); 879*cdf0e10cSrcweir aRetval.append(aRight.getStartPoint()); 880*cdf0e10cSrcweir aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA()); 881*cdf0e10cSrcweir bDone = true; 882*cdf0e10cSrcweir } 883*cdf0e10cSrcweir } 884*cdf0e10cSrcweir 885*cdf0e10cSrcweir if(!bDone) 886*cdf0e10cSrcweir { 887*cdf0e10cSrcweir const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength); 888*cdf0e10cSrcweir aRetval.append(interpolate(aStart, aEnd, fRelValue)); 889*cdf0e10cSrcweir } 890*cdf0e10cSrcweir } 891*cdf0e10cSrcweir 892*cdf0e10cSrcweir bStartDone = true; 893*cdf0e10cSrcweir 894*cdf0e10cSrcweir // if same point, end is done, too. 895*cdf0e10cSrcweir if(fFrom == fTo) 896*cdf0e10cSrcweir { 897*cdf0e10cSrcweir bEndDone = true; 898*cdf0e10cSrcweir } 899*cdf0e10cSrcweir } 900*cdf0e10cSrcweir } 901*cdf0e10cSrcweir 902*cdf0e10cSrcweir if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength)) 903*cdf0e10cSrcweir { 904*cdf0e10cSrcweir // calculate and add end point 905*cdf0e10cSrcweir if(fTools::equalZero(fEdgeLength)) 906*cdf0e10cSrcweir { 907*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 908*cdf0e10cSrcweir aRetval.append(rCandidate.getB2DPoint(nNextIndex)); 909*cdf0e10cSrcweir 910*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 911*cdf0e10cSrcweir { 912*cdf0e10cSrcweir aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex)); 913*cdf0e10cSrcweir } 914*cdf0e10cSrcweir } 915*cdf0e10cSrcweir else 916*cdf0e10cSrcweir { 917*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 918*cdf0e10cSrcweir const B2DPoint aStart(rCandidate.getB2DPoint(a)); 919*cdf0e10cSrcweir const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex)); 920*cdf0e10cSrcweir bool bDone(false); 921*cdf0e10cSrcweir 922*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 923*cdf0e10cSrcweir { 924*cdf0e10cSrcweir const B2DCubicBezier aBezierSegment( 925*cdf0e10cSrcweir aStart, rCandidate.getNextControlPoint(a), 926*cdf0e10cSrcweir rCandidate.getPrevControlPoint(nNextIndex), aEnd); 927*cdf0e10cSrcweir 928*cdf0e10cSrcweir if(aBezierSegment.isBezier()) 929*cdf0e10cSrcweir { 930*cdf0e10cSrcweir // use B2DCubicBezierHelper to bridge the non-linear gap between 931*cdf0e10cSrcweir // length and bezier distances 932*cdf0e10cSrcweir const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment); 933*cdf0e10cSrcweir const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart)); 934*cdf0e10cSrcweir B2DCubicBezier aLeft; 935*cdf0e10cSrcweir 936*cdf0e10cSrcweir aBezierSegment.split(fBezierDistance, &aLeft, 0); 937*cdf0e10cSrcweir aRetval.append(aLeft.getEndPoint()); 938*cdf0e10cSrcweir aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB()); 939*cdf0e10cSrcweir bDone = true; 940*cdf0e10cSrcweir } 941*cdf0e10cSrcweir } 942*cdf0e10cSrcweir 943*cdf0e10cSrcweir if(!bDone) 944*cdf0e10cSrcweir { 945*cdf0e10cSrcweir const double fRelValue((fTo - fPositionOfStart) / fEdgeLength); 946*cdf0e10cSrcweir aRetval.append(interpolate(aStart, aEnd, fRelValue)); 947*cdf0e10cSrcweir } 948*cdf0e10cSrcweir } 949*cdf0e10cSrcweir 950*cdf0e10cSrcweir bEndDone = true; 951*cdf0e10cSrcweir } 952*cdf0e10cSrcweir 953*cdf0e10cSrcweir if(!bEndDone) 954*cdf0e10cSrcweir { 955*cdf0e10cSrcweir if(bStartDone) 956*cdf0e10cSrcweir { 957*cdf0e10cSrcweir // add segments end point 958*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 959*cdf0e10cSrcweir aRetval.append(rCandidate.getB2DPoint(nNextIndex)); 960*cdf0e10cSrcweir 961*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 962*cdf0e10cSrcweir { 963*cdf0e10cSrcweir aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex)); 964*cdf0e10cSrcweir aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex)); 965*cdf0e10cSrcweir } 966*cdf0e10cSrcweir } 967*cdf0e10cSrcweir 968*cdf0e10cSrcweir // increment fPositionOfStart 969*cdf0e10cSrcweir fPositionOfStart += fEdgeLength; 970*cdf0e10cSrcweir } 971*cdf0e10cSrcweir } 972*cdf0e10cSrcweir return aRetval; 973*cdf0e10cSrcweir } 974*cdf0e10cSrcweir } 975*cdf0e10cSrcweir else 976*cdf0e10cSrcweir { 977*cdf0e10cSrcweir return rCandidate; 978*cdf0e10cSrcweir } 979*cdf0e10cSrcweir } 980*cdf0e10cSrcweir 981*cdf0e10cSrcweir B2DPolygon getSnippetRelative(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength) 982*cdf0e10cSrcweir { 983*cdf0e10cSrcweir // get length if not given 984*cdf0e10cSrcweir if(fTools::equalZero(fLength)) 985*cdf0e10cSrcweir { 986*cdf0e10cSrcweir fLength = getLength(rCandidate); 987*cdf0e10cSrcweir } 988*cdf0e10cSrcweir 989*cdf0e10cSrcweir // multiply distances with real length to get absolute position and 990*cdf0e10cSrcweir // use getSnippetAbsolute 991*cdf0e10cSrcweir return getSnippetAbsolute(rCandidate, fFrom * fLength, fTo * fLength, fLength); 992*cdf0e10cSrcweir } 993*cdf0e10cSrcweir 994*cdf0e10cSrcweir CutFlagValue findCut( 995*cdf0e10cSrcweir const B2DPolygon& rCandidate, 996*cdf0e10cSrcweir sal_uInt32 nIndex1, sal_uInt32 nIndex2, 997*cdf0e10cSrcweir CutFlagValue aCutFlags, 998*cdf0e10cSrcweir double* pCut1, double* pCut2) 999*cdf0e10cSrcweir { 1000*cdf0e10cSrcweir CutFlagValue aRetval(CUTFLAG_NONE); 1001*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 1002*cdf0e10cSrcweir 1003*cdf0e10cSrcweir if(nIndex1 < nPointCount && nIndex2 < nPointCount && nIndex1 != nIndex2) 1004*cdf0e10cSrcweir { 1005*cdf0e10cSrcweir sal_uInt32 nEnd1(getIndexOfSuccessor(nIndex1, rCandidate)); 1006*cdf0e10cSrcweir sal_uInt32 nEnd2(getIndexOfSuccessor(nIndex2, rCandidate)); 1007*cdf0e10cSrcweir 1008*cdf0e10cSrcweir const B2DPoint aStart1(rCandidate.getB2DPoint(nIndex1)); 1009*cdf0e10cSrcweir const B2DPoint aEnd1(rCandidate.getB2DPoint(nEnd1)); 1010*cdf0e10cSrcweir const B2DVector aVector1(aEnd1 - aStart1); 1011*cdf0e10cSrcweir 1012*cdf0e10cSrcweir const B2DPoint aStart2(rCandidate.getB2DPoint(nIndex2)); 1013*cdf0e10cSrcweir const B2DPoint aEnd2(rCandidate.getB2DPoint(nEnd2)); 1014*cdf0e10cSrcweir const B2DVector aVector2(aEnd2 - aStart2); 1015*cdf0e10cSrcweir 1016*cdf0e10cSrcweir aRetval = findCut( 1017*cdf0e10cSrcweir aStart1, aVector1, aStart2, aVector2, 1018*cdf0e10cSrcweir aCutFlags, pCut1, pCut2); 1019*cdf0e10cSrcweir } 1020*cdf0e10cSrcweir 1021*cdf0e10cSrcweir return aRetval; 1022*cdf0e10cSrcweir } 1023*cdf0e10cSrcweir 1024*cdf0e10cSrcweir CutFlagValue findCut( 1025*cdf0e10cSrcweir const B2DPolygon& rCandidate1, sal_uInt32 nIndex1, 1026*cdf0e10cSrcweir const B2DPolygon& rCandidate2, sal_uInt32 nIndex2, 1027*cdf0e10cSrcweir CutFlagValue aCutFlags, 1028*cdf0e10cSrcweir double* pCut1, double* pCut2) 1029*cdf0e10cSrcweir { 1030*cdf0e10cSrcweir CutFlagValue aRetval(CUTFLAG_NONE); 1031*cdf0e10cSrcweir const sal_uInt32 nPointCount1(rCandidate1.count()); 1032*cdf0e10cSrcweir const sal_uInt32 nPointCount2(rCandidate2.count()); 1033*cdf0e10cSrcweir 1034*cdf0e10cSrcweir if(nIndex1 < nPointCount1 && nIndex2 < nPointCount2) 1035*cdf0e10cSrcweir { 1036*cdf0e10cSrcweir sal_uInt32 nEnd1(getIndexOfSuccessor(nIndex1, rCandidate1)); 1037*cdf0e10cSrcweir sal_uInt32 nEnd2(getIndexOfSuccessor(nIndex2, rCandidate2)); 1038*cdf0e10cSrcweir 1039*cdf0e10cSrcweir const B2DPoint aStart1(rCandidate1.getB2DPoint(nIndex1)); 1040*cdf0e10cSrcweir const B2DPoint aEnd1(rCandidate1.getB2DPoint(nEnd1)); 1041*cdf0e10cSrcweir const B2DVector aVector1(aEnd1 - aStart1); 1042*cdf0e10cSrcweir 1043*cdf0e10cSrcweir const B2DPoint aStart2(rCandidate2.getB2DPoint(nIndex2)); 1044*cdf0e10cSrcweir const B2DPoint aEnd2(rCandidate2.getB2DPoint(nEnd2)); 1045*cdf0e10cSrcweir const B2DVector aVector2(aEnd2 - aStart2); 1046*cdf0e10cSrcweir 1047*cdf0e10cSrcweir aRetval = findCut( 1048*cdf0e10cSrcweir aStart1, aVector1, aStart2, aVector2, 1049*cdf0e10cSrcweir aCutFlags, pCut1, pCut2); 1050*cdf0e10cSrcweir } 1051*cdf0e10cSrcweir 1052*cdf0e10cSrcweir return aRetval; 1053*cdf0e10cSrcweir } 1054*cdf0e10cSrcweir 1055*cdf0e10cSrcweir CutFlagValue findCut( 1056*cdf0e10cSrcweir const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta, 1057*cdf0e10cSrcweir const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta, 1058*cdf0e10cSrcweir CutFlagValue aCutFlags, 1059*cdf0e10cSrcweir double* pCut1, double* pCut2) 1060*cdf0e10cSrcweir { 1061*cdf0e10cSrcweir CutFlagValue aRetval(CUTFLAG_NONE); 1062*cdf0e10cSrcweir double fCut1(0.0); 1063*cdf0e10cSrcweir double fCut2(0.0); 1064*cdf0e10cSrcweir bool bFinished(!((bool)(aCutFlags & CUTFLAG_ALL))); 1065*cdf0e10cSrcweir 1066*cdf0e10cSrcweir // test for same points? 1067*cdf0e10cSrcweir if(!bFinished 1068*cdf0e10cSrcweir && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END1)) 1069*cdf0e10cSrcweir && (aCutFlags & (CUTFLAG_START2|CUTFLAG_END2))) 1070*cdf0e10cSrcweir { 1071*cdf0e10cSrcweir // same startpoint? 1072*cdf0e10cSrcweir if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_START2)) == (CUTFLAG_START1|CUTFLAG_START2)) 1073*cdf0e10cSrcweir { 1074*cdf0e10cSrcweir if(rEdge1Start.equal(rEdge2Start)) 1075*cdf0e10cSrcweir { 1076*cdf0e10cSrcweir bFinished = true; 1077*cdf0e10cSrcweir aRetval = (CUTFLAG_START1|CUTFLAG_START2); 1078*cdf0e10cSrcweir } 1079*cdf0e10cSrcweir } 1080*cdf0e10cSrcweir 1081*cdf0e10cSrcweir // same endpoint? 1082*cdf0e10cSrcweir if(!bFinished && (aCutFlags & (CUTFLAG_END1|CUTFLAG_END2)) == (CUTFLAG_END1|CUTFLAG_END2)) 1083*cdf0e10cSrcweir { 1084*cdf0e10cSrcweir const B2DPoint aEnd1(rEdge1Start + rEdge1Delta); 1085*cdf0e10cSrcweir const B2DPoint aEnd2(rEdge2Start + rEdge2Delta); 1086*cdf0e10cSrcweir 1087*cdf0e10cSrcweir if(aEnd1.equal(aEnd2)) 1088*cdf0e10cSrcweir { 1089*cdf0e10cSrcweir bFinished = true; 1090*cdf0e10cSrcweir aRetval = (CUTFLAG_END1|CUTFLAG_END2); 1091*cdf0e10cSrcweir fCut1 = fCut2 = 1.0; 1092*cdf0e10cSrcweir } 1093*cdf0e10cSrcweir } 1094*cdf0e10cSrcweir 1095*cdf0e10cSrcweir // startpoint1 == endpoint2? 1096*cdf0e10cSrcweir if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END2)) == (CUTFLAG_START1|CUTFLAG_END2)) 1097*cdf0e10cSrcweir { 1098*cdf0e10cSrcweir const B2DPoint aEnd2(rEdge2Start + rEdge2Delta); 1099*cdf0e10cSrcweir 1100*cdf0e10cSrcweir if(rEdge1Start.equal(aEnd2)) 1101*cdf0e10cSrcweir { 1102*cdf0e10cSrcweir bFinished = true; 1103*cdf0e10cSrcweir aRetval = (CUTFLAG_START1|CUTFLAG_END2); 1104*cdf0e10cSrcweir fCut1 = 0.0; 1105*cdf0e10cSrcweir fCut2 = 1.0; 1106*cdf0e10cSrcweir } 1107*cdf0e10cSrcweir } 1108*cdf0e10cSrcweir 1109*cdf0e10cSrcweir // startpoint2 == endpoint1? 1110*cdf0e10cSrcweir if(!bFinished&& (aCutFlags & (CUTFLAG_START2|CUTFLAG_END1)) == (CUTFLAG_START2|CUTFLAG_END1)) 1111*cdf0e10cSrcweir { 1112*cdf0e10cSrcweir const B2DPoint aEnd1(rEdge1Start + rEdge1Delta); 1113*cdf0e10cSrcweir 1114*cdf0e10cSrcweir if(rEdge2Start.equal(aEnd1)) 1115*cdf0e10cSrcweir { 1116*cdf0e10cSrcweir bFinished = true; 1117*cdf0e10cSrcweir aRetval = (CUTFLAG_START2|CUTFLAG_END1); 1118*cdf0e10cSrcweir fCut1 = 1.0; 1119*cdf0e10cSrcweir fCut2 = 0.0; 1120*cdf0e10cSrcweir } 1121*cdf0e10cSrcweir } 1122*cdf0e10cSrcweir } 1123*cdf0e10cSrcweir 1124*cdf0e10cSrcweir if(!bFinished && (aCutFlags & CUTFLAG_LINE)) 1125*cdf0e10cSrcweir { 1126*cdf0e10cSrcweir if(!bFinished && (aCutFlags & CUTFLAG_START1)) 1127*cdf0e10cSrcweir { 1128*cdf0e10cSrcweir // start1 on line 2 ? 1129*cdf0e10cSrcweir if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2)) 1130*cdf0e10cSrcweir { 1131*cdf0e10cSrcweir bFinished = true; 1132*cdf0e10cSrcweir aRetval = (CUTFLAG_LINE|CUTFLAG_START1); 1133*cdf0e10cSrcweir } 1134*cdf0e10cSrcweir } 1135*cdf0e10cSrcweir 1136*cdf0e10cSrcweir if(!bFinished && (aCutFlags & CUTFLAG_START2)) 1137*cdf0e10cSrcweir { 1138*cdf0e10cSrcweir // start2 on line 1 ? 1139*cdf0e10cSrcweir if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1)) 1140*cdf0e10cSrcweir { 1141*cdf0e10cSrcweir bFinished = true; 1142*cdf0e10cSrcweir aRetval = (CUTFLAG_LINE|CUTFLAG_START2); 1143*cdf0e10cSrcweir } 1144*cdf0e10cSrcweir } 1145*cdf0e10cSrcweir 1146*cdf0e10cSrcweir if(!bFinished && (aCutFlags & CUTFLAG_END1)) 1147*cdf0e10cSrcweir { 1148*cdf0e10cSrcweir // end1 on line 2 ? 1149*cdf0e10cSrcweir const B2DPoint aEnd1(rEdge1Start + rEdge1Delta); 1150*cdf0e10cSrcweir 1151*cdf0e10cSrcweir if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2)) 1152*cdf0e10cSrcweir { 1153*cdf0e10cSrcweir bFinished = true; 1154*cdf0e10cSrcweir aRetval = (CUTFLAG_LINE|CUTFLAG_END1); 1155*cdf0e10cSrcweir } 1156*cdf0e10cSrcweir } 1157*cdf0e10cSrcweir 1158*cdf0e10cSrcweir if(!bFinished && (aCutFlags & CUTFLAG_END2)) 1159*cdf0e10cSrcweir { 1160*cdf0e10cSrcweir // end2 on line 1 ? 1161*cdf0e10cSrcweir const B2DPoint aEnd2(rEdge2Start + rEdge2Delta); 1162*cdf0e10cSrcweir 1163*cdf0e10cSrcweir if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1)) 1164*cdf0e10cSrcweir { 1165*cdf0e10cSrcweir bFinished = true; 1166*cdf0e10cSrcweir aRetval = (CUTFLAG_LINE|CUTFLAG_END2); 1167*cdf0e10cSrcweir } 1168*cdf0e10cSrcweir } 1169*cdf0e10cSrcweir 1170*cdf0e10cSrcweir if(!bFinished) 1171*cdf0e10cSrcweir { 1172*cdf0e10cSrcweir // cut in line1, line2 ? 1173*cdf0e10cSrcweir fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX()); 1174*cdf0e10cSrcweir 1175*cdf0e10cSrcweir if(!fTools::equalZero(fCut1)) 1176*cdf0e10cSrcweir { 1177*cdf0e10cSrcweir fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX()) 1178*cdf0e10cSrcweir + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1; 1179*cdf0e10cSrcweir 1180*cdf0e10cSrcweir const double fZero(0.0); 1181*cdf0e10cSrcweir const double fOne(1.0); 1182*cdf0e10cSrcweir 1183*cdf0e10cSrcweir // inside parameter range edge1 AND fCut2 is calcable 1184*cdf0e10cSrcweir if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne) 1185*cdf0e10cSrcweir && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY()))) 1186*cdf0e10cSrcweir { 1187*cdf0e10cSrcweir // take the mopre precise calculation of the two possible 1188*cdf0e10cSrcweir if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY())) 1189*cdf0e10cSrcweir { 1190*cdf0e10cSrcweir fCut2 = (rEdge1Start.getX() + fCut1 1191*cdf0e10cSrcweir * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX(); 1192*cdf0e10cSrcweir } 1193*cdf0e10cSrcweir else 1194*cdf0e10cSrcweir { 1195*cdf0e10cSrcweir fCut2 = (rEdge1Start.getY() + fCut1 1196*cdf0e10cSrcweir * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY(); 1197*cdf0e10cSrcweir } 1198*cdf0e10cSrcweir 1199*cdf0e10cSrcweir // inside parameter range edge2, too 1200*cdf0e10cSrcweir if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne)) 1201*cdf0e10cSrcweir { 1202*cdf0e10cSrcweir bFinished = true; 1203*cdf0e10cSrcweir aRetval = CUTFLAG_LINE; 1204*cdf0e10cSrcweir } 1205*cdf0e10cSrcweir } 1206*cdf0e10cSrcweir } 1207*cdf0e10cSrcweir } 1208*cdf0e10cSrcweir } 1209*cdf0e10cSrcweir 1210*cdf0e10cSrcweir // copy values if wanted 1211*cdf0e10cSrcweir if(pCut1) 1212*cdf0e10cSrcweir { 1213*cdf0e10cSrcweir *pCut1 = fCut1; 1214*cdf0e10cSrcweir } 1215*cdf0e10cSrcweir 1216*cdf0e10cSrcweir if(pCut2) 1217*cdf0e10cSrcweir { 1218*cdf0e10cSrcweir *pCut2 = fCut2; 1219*cdf0e10cSrcweir } 1220*cdf0e10cSrcweir 1221*cdf0e10cSrcweir return aRetval; 1222*cdf0e10cSrcweir } 1223*cdf0e10cSrcweir 1224*cdf0e10cSrcweir bool isPointOnEdge( 1225*cdf0e10cSrcweir const B2DPoint& rPoint, 1226*cdf0e10cSrcweir const B2DPoint& rEdgeStart, 1227*cdf0e10cSrcweir const B2DVector& rEdgeDelta, 1228*cdf0e10cSrcweir double* pCut) 1229*cdf0e10cSrcweir { 1230*cdf0e10cSrcweir bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX())); 1231*cdf0e10cSrcweir bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY())); 1232*cdf0e10cSrcweir const double fZero(0.0); 1233*cdf0e10cSrcweir const double fOne(1.0); 1234*cdf0e10cSrcweir 1235*cdf0e10cSrcweir if(bDeltaXIsZero && bDeltaYIsZero) 1236*cdf0e10cSrcweir { 1237*cdf0e10cSrcweir // no line, just a point 1238*cdf0e10cSrcweir return false; 1239*cdf0e10cSrcweir } 1240*cdf0e10cSrcweir else if(bDeltaXIsZero) 1241*cdf0e10cSrcweir { 1242*cdf0e10cSrcweir // vertical line 1243*cdf0e10cSrcweir if(fTools::equal(rPoint.getX(), rEdgeStart.getX())) 1244*cdf0e10cSrcweir { 1245*cdf0e10cSrcweir double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY(); 1246*cdf0e10cSrcweir 1247*cdf0e10cSrcweir if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne)) 1248*cdf0e10cSrcweir { 1249*cdf0e10cSrcweir if(pCut) 1250*cdf0e10cSrcweir { 1251*cdf0e10cSrcweir *pCut = fValue; 1252*cdf0e10cSrcweir } 1253*cdf0e10cSrcweir 1254*cdf0e10cSrcweir return true; 1255*cdf0e10cSrcweir } 1256*cdf0e10cSrcweir } 1257*cdf0e10cSrcweir } 1258*cdf0e10cSrcweir else if(bDeltaYIsZero) 1259*cdf0e10cSrcweir { 1260*cdf0e10cSrcweir // horizontal line 1261*cdf0e10cSrcweir if(fTools::equal(rPoint.getY(), rEdgeStart.getY())) 1262*cdf0e10cSrcweir { 1263*cdf0e10cSrcweir double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX(); 1264*cdf0e10cSrcweir 1265*cdf0e10cSrcweir if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne)) 1266*cdf0e10cSrcweir { 1267*cdf0e10cSrcweir if(pCut) 1268*cdf0e10cSrcweir { 1269*cdf0e10cSrcweir *pCut = fValue; 1270*cdf0e10cSrcweir } 1271*cdf0e10cSrcweir 1272*cdf0e10cSrcweir return true; 1273*cdf0e10cSrcweir } 1274*cdf0e10cSrcweir } 1275*cdf0e10cSrcweir } 1276*cdf0e10cSrcweir else 1277*cdf0e10cSrcweir { 1278*cdf0e10cSrcweir // any angle line 1279*cdf0e10cSrcweir double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX(); 1280*cdf0e10cSrcweir double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY(); 1281*cdf0e10cSrcweir 1282*cdf0e10cSrcweir if(fTools::equal(fTOne, fTTwo)) 1283*cdf0e10cSrcweir { 1284*cdf0e10cSrcweir // same parameter representation, point is on line. Take 1285*cdf0e10cSrcweir // middle value for better results 1286*cdf0e10cSrcweir double fValue = (fTOne + fTTwo) / 2.0; 1287*cdf0e10cSrcweir 1288*cdf0e10cSrcweir if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne)) 1289*cdf0e10cSrcweir { 1290*cdf0e10cSrcweir // point is inside line bounds, too 1291*cdf0e10cSrcweir if(pCut) 1292*cdf0e10cSrcweir { 1293*cdf0e10cSrcweir *pCut = fValue; 1294*cdf0e10cSrcweir } 1295*cdf0e10cSrcweir 1296*cdf0e10cSrcweir return true; 1297*cdf0e10cSrcweir } 1298*cdf0e10cSrcweir } 1299*cdf0e10cSrcweir } 1300*cdf0e10cSrcweir 1301*cdf0e10cSrcweir return false; 1302*cdf0e10cSrcweir } 1303*cdf0e10cSrcweir 1304*cdf0e10cSrcweir void applyLineDashing(const B2DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength) 1305*cdf0e10cSrcweir { 1306*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 1307*cdf0e10cSrcweir const sal_uInt32 nDotDashCount(rDotDashArray.size()); 1308*cdf0e10cSrcweir 1309*cdf0e10cSrcweir if(fTools::lessOrEqual(fDotDashLength, 0.0)) 1310*cdf0e10cSrcweir { 1311*cdf0e10cSrcweir fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0); 1312*cdf0e10cSrcweir } 1313*cdf0e10cSrcweir 1314*cdf0e10cSrcweir if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount) 1315*cdf0e10cSrcweir { 1316*cdf0e10cSrcweir // clear targets 1317*cdf0e10cSrcweir if(pLineTarget) 1318*cdf0e10cSrcweir { 1319*cdf0e10cSrcweir pLineTarget->clear(); 1320*cdf0e10cSrcweir } 1321*cdf0e10cSrcweir 1322*cdf0e10cSrcweir if(pGapTarget) 1323*cdf0e10cSrcweir { 1324*cdf0e10cSrcweir pGapTarget->clear(); 1325*cdf0e10cSrcweir } 1326*cdf0e10cSrcweir 1327*cdf0e10cSrcweir // prepare current edge's start 1328*cdf0e10cSrcweir B2DCubicBezier aCurrentEdge; 1329*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 1330*cdf0e10cSrcweir aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0)); 1331*cdf0e10cSrcweir 1332*cdf0e10cSrcweir // prepare DotDashArray iteration and the line/gap switching bool 1333*cdf0e10cSrcweir sal_uInt32 nDotDashIndex(0); 1334*cdf0e10cSrcweir bool bIsLine(true); 1335*cdf0e10cSrcweir double fDotDashMovingLength(rDotDashArray[0]); 1336*cdf0e10cSrcweir B2DPolygon aSnippet; 1337*cdf0e10cSrcweir 1338*cdf0e10cSrcweir // iterate over all edges 1339*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 1340*cdf0e10cSrcweir { 1341*cdf0e10cSrcweir // update current edge (fill in C1, C2 and end point) 1342*cdf0e10cSrcweir double fLastDotDashMovingLength(0.0); 1343*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 1344*cdf0e10cSrcweir aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a)); 1345*cdf0e10cSrcweir aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 1346*cdf0e10cSrcweir aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 1347*cdf0e10cSrcweir 1348*cdf0e10cSrcweir // check if we have a trivial bezier segment -> possible fallback to edge 1349*cdf0e10cSrcweir aCurrentEdge.testAndSolveTrivialBezier(); 1350*cdf0e10cSrcweir 1351*cdf0e10cSrcweir if(aCurrentEdge.isBezier()) 1352*cdf0e10cSrcweir { 1353*cdf0e10cSrcweir // bezier segment 1354*cdf0e10cSrcweir const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge); 1355*cdf0e10cSrcweir const double fEdgeLength(aCubicBezierHelper.getLength()); 1356*cdf0e10cSrcweir 1357*cdf0e10cSrcweir if(!fTools::equalZero(fEdgeLength)) 1358*cdf0e10cSrcweir { 1359*cdf0e10cSrcweir while(fTools::less(fDotDashMovingLength, fEdgeLength)) 1360*cdf0e10cSrcweir { 1361*cdf0e10cSrcweir // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] 1362*cdf0e10cSrcweir const bool bHandleLine(bIsLine && pLineTarget); 1363*cdf0e10cSrcweir const bool bHandleGap(!bIsLine && pGapTarget); 1364*cdf0e10cSrcweir 1365*cdf0e10cSrcweir if(bHandleLine || bHandleGap) 1366*cdf0e10cSrcweir { 1367*cdf0e10cSrcweir const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); 1368*cdf0e10cSrcweir const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength)); 1369*cdf0e10cSrcweir B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd)); 1370*cdf0e10cSrcweir 1371*cdf0e10cSrcweir if(!aSnippet.count()) 1372*cdf0e10cSrcweir { 1373*cdf0e10cSrcweir aSnippet.append(aBezierSnippet.getStartPoint()); 1374*cdf0e10cSrcweir } 1375*cdf0e10cSrcweir 1376*cdf0e10cSrcweir aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint()); 1377*cdf0e10cSrcweir 1378*cdf0e10cSrcweir if(bHandleLine) 1379*cdf0e10cSrcweir { 1380*cdf0e10cSrcweir pLineTarget->append(aSnippet); 1381*cdf0e10cSrcweir } 1382*cdf0e10cSrcweir else 1383*cdf0e10cSrcweir { 1384*cdf0e10cSrcweir pGapTarget->append(aSnippet); 1385*cdf0e10cSrcweir } 1386*cdf0e10cSrcweir 1387*cdf0e10cSrcweir aSnippet.clear(); 1388*cdf0e10cSrcweir } 1389*cdf0e10cSrcweir 1390*cdf0e10cSrcweir // prepare next DotDashArray step and flip line/gap flag 1391*cdf0e10cSrcweir fLastDotDashMovingLength = fDotDashMovingLength; 1392*cdf0e10cSrcweir fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; 1393*cdf0e10cSrcweir bIsLine = !bIsLine; 1394*cdf0e10cSrcweir } 1395*cdf0e10cSrcweir 1396*cdf0e10cSrcweir // append closing snippet [fLastDotDashMovingLength, fEdgeLength] 1397*cdf0e10cSrcweir const bool bHandleLine(bIsLine && pLineTarget); 1398*cdf0e10cSrcweir const bool bHandleGap(!bIsLine && pGapTarget); 1399*cdf0e10cSrcweir 1400*cdf0e10cSrcweir if(bHandleLine || bHandleGap) 1401*cdf0e10cSrcweir { 1402*cdf0e10cSrcweir B2DCubicBezier aRight; 1403*cdf0e10cSrcweir const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); 1404*cdf0e10cSrcweir 1405*cdf0e10cSrcweir aCurrentEdge.split(fBezierSplit, 0, &aRight); 1406*cdf0e10cSrcweir 1407*cdf0e10cSrcweir if(!aSnippet.count()) 1408*cdf0e10cSrcweir { 1409*cdf0e10cSrcweir aSnippet.append(aRight.getStartPoint()); 1410*cdf0e10cSrcweir } 1411*cdf0e10cSrcweir 1412*cdf0e10cSrcweir aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint()); 1413*cdf0e10cSrcweir } 1414*cdf0e10cSrcweir 1415*cdf0e10cSrcweir // prepare move to next edge 1416*cdf0e10cSrcweir fDotDashMovingLength -= fEdgeLength; 1417*cdf0e10cSrcweir } 1418*cdf0e10cSrcweir } 1419*cdf0e10cSrcweir else 1420*cdf0e10cSrcweir { 1421*cdf0e10cSrcweir // simple edge 1422*cdf0e10cSrcweir const double fEdgeLength(aCurrentEdge.getEdgeLength()); 1423*cdf0e10cSrcweir 1424*cdf0e10cSrcweir if(!fTools::equalZero(fEdgeLength)) 1425*cdf0e10cSrcweir { 1426*cdf0e10cSrcweir while(fTools::less(fDotDashMovingLength, fEdgeLength)) 1427*cdf0e10cSrcweir { 1428*cdf0e10cSrcweir // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] 1429*cdf0e10cSrcweir const bool bHandleLine(bIsLine && pLineTarget); 1430*cdf0e10cSrcweir const bool bHandleGap(!bIsLine && pGapTarget); 1431*cdf0e10cSrcweir 1432*cdf0e10cSrcweir if(bHandleLine || bHandleGap) 1433*cdf0e10cSrcweir { 1434*cdf0e10cSrcweir if(!aSnippet.count()) 1435*cdf0e10cSrcweir { 1436*cdf0e10cSrcweir aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); 1437*cdf0e10cSrcweir } 1438*cdf0e10cSrcweir 1439*cdf0e10cSrcweir aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength)); 1440*cdf0e10cSrcweir 1441*cdf0e10cSrcweir if(bHandleLine) 1442*cdf0e10cSrcweir { 1443*cdf0e10cSrcweir pLineTarget->append(aSnippet); 1444*cdf0e10cSrcweir } 1445*cdf0e10cSrcweir else 1446*cdf0e10cSrcweir { 1447*cdf0e10cSrcweir pGapTarget->append(aSnippet); 1448*cdf0e10cSrcweir } 1449*cdf0e10cSrcweir 1450*cdf0e10cSrcweir aSnippet.clear(); 1451*cdf0e10cSrcweir } 1452*cdf0e10cSrcweir 1453*cdf0e10cSrcweir // prepare next DotDashArray step and flip line/gap flag 1454*cdf0e10cSrcweir fLastDotDashMovingLength = fDotDashMovingLength; 1455*cdf0e10cSrcweir fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; 1456*cdf0e10cSrcweir bIsLine = !bIsLine; 1457*cdf0e10cSrcweir } 1458*cdf0e10cSrcweir 1459*cdf0e10cSrcweir // append snippet [fLastDotDashMovingLength, fEdgeLength] 1460*cdf0e10cSrcweir const bool bHandleLine(bIsLine && pLineTarget); 1461*cdf0e10cSrcweir const bool bHandleGap(!bIsLine && pGapTarget); 1462*cdf0e10cSrcweir 1463*cdf0e10cSrcweir if(bHandleLine || bHandleGap) 1464*cdf0e10cSrcweir { 1465*cdf0e10cSrcweir if(!aSnippet.count()) 1466*cdf0e10cSrcweir { 1467*cdf0e10cSrcweir aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); 1468*cdf0e10cSrcweir } 1469*cdf0e10cSrcweir 1470*cdf0e10cSrcweir aSnippet.append(aCurrentEdge.getEndPoint()); 1471*cdf0e10cSrcweir } 1472*cdf0e10cSrcweir 1473*cdf0e10cSrcweir // prepare move to next edge 1474*cdf0e10cSrcweir fDotDashMovingLength -= fEdgeLength; 1475*cdf0e10cSrcweir } 1476*cdf0e10cSrcweir } 1477*cdf0e10cSrcweir 1478*cdf0e10cSrcweir // prepare next edge step (end point gets new start point) 1479*cdf0e10cSrcweir aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint()); 1480*cdf0e10cSrcweir } 1481*cdf0e10cSrcweir 1482*cdf0e10cSrcweir // append last intermediate results (if exists) 1483*cdf0e10cSrcweir if(aSnippet.count()) 1484*cdf0e10cSrcweir { 1485*cdf0e10cSrcweir if(bIsLine && pLineTarget) 1486*cdf0e10cSrcweir { 1487*cdf0e10cSrcweir pLineTarget->append(aSnippet); 1488*cdf0e10cSrcweir } 1489*cdf0e10cSrcweir else if(!bIsLine && pGapTarget) 1490*cdf0e10cSrcweir { 1491*cdf0e10cSrcweir pGapTarget->append(aSnippet); 1492*cdf0e10cSrcweir } 1493*cdf0e10cSrcweir } 1494*cdf0e10cSrcweir 1495*cdf0e10cSrcweir // check if start and end polygon may be merged 1496*cdf0e10cSrcweir if(pLineTarget) 1497*cdf0e10cSrcweir { 1498*cdf0e10cSrcweir const sal_uInt32 nCount(pLineTarget->count()); 1499*cdf0e10cSrcweir 1500*cdf0e10cSrcweir if(nCount > 1) 1501*cdf0e10cSrcweir { 1502*cdf0e10cSrcweir // these polygons were created above, there exists none with less than two points, 1503*cdf0e10cSrcweir // thus dircet point access below is allowed 1504*cdf0e10cSrcweir const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0)); 1505*cdf0e10cSrcweir B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1)); 1506*cdf0e10cSrcweir 1507*cdf0e10cSrcweir if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1))) 1508*cdf0e10cSrcweir { 1509*cdf0e10cSrcweir // start of first and end of last are the same -> merge them 1510*cdf0e10cSrcweir aLast.append(aFirst); 1511*cdf0e10cSrcweir aLast.removeDoublePoints(); 1512*cdf0e10cSrcweir pLineTarget->setB2DPolygon(0, aLast); 1513*cdf0e10cSrcweir pLineTarget->remove(nCount - 1); 1514*cdf0e10cSrcweir } 1515*cdf0e10cSrcweir } 1516*cdf0e10cSrcweir } 1517*cdf0e10cSrcweir 1518*cdf0e10cSrcweir if(pGapTarget) 1519*cdf0e10cSrcweir { 1520*cdf0e10cSrcweir const sal_uInt32 nCount(pGapTarget->count()); 1521*cdf0e10cSrcweir 1522*cdf0e10cSrcweir if(nCount > 1) 1523*cdf0e10cSrcweir { 1524*cdf0e10cSrcweir // these polygons were created above, there exists none with less than two points, 1525*cdf0e10cSrcweir // thus dircet point access below is allowed 1526*cdf0e10cSrcweir const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0)); 1527*cdf0e10cSrcweir B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1)); 1528*cdf0e10cSrcweir 1529*cdf0e10cSrcweir if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1))) 1530*cdf0e10cSrcweir { 1531*cdf0e10cSrcweir // start of first and end of last are the same -> merge them 1532*cdf0e10cSrcweir aLast.append(aFirst); 1533*cdf0e10cSrcweir aLast.removeDoublePoints(); 1534*cdf0e10cSrcweir pGapTarget->setB2DPolygon(0, aLast); 1535*cdf0e10cSrcweir pGapTarget->remove(nCount - 1); 1536*cdf0e10cSrcweir } 1537*cdf0e10cSrcweir } 1538*cdf0e10cSrcweir } 1539*cdf0e10cSrcweir } 1540*cdf0e10cSrcweir else 1541*cdf0e10cSrcweir { 1542*cdf0e10cSrcweir // parameters make no sense, just add source to targets 1543*cdf0e10cSrcweir if(pLineTarget) 1544*cdf0e10cSrcweir { 1545*cdf0e10cSrcweir pLineTarget->append(rCandidate); 1546*cdf0e10cSrcweir } 1547*cdf0e10cSrcweir 1548*cdf0e10cSrcweir if(pGapTarget) 1549*cdf0e10cSrcweir { 1550*cdf0e10cSrcweir pGapTarget->append(rCandidate); 1551*cdf0e10cSrcweir } 1552*cdf0e10cSrcweir } 1553*cdf0e10cSrcweir } 1554*cdf0e10cSrcweir 1555*cdf0e10cSrcweir // test if point is inside epsilon-range around an edge defined 1556*cdf0e10cSrcweir // by the two given points. Can be used for HitTesting. The epsilon-range 1557*cdf0e10cSrcweir // is defined to be the rectangle centered to the given edge, using height 1558*cdf0e10cSrcweir // 2 x fDistance, and the circle around both points with radius fDistance. 1559*cdf0e10cSrcweir bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance) 1560*cdf0e10cSrcweir { 1561*cdf0e10cSrcweir // build edge vector 1562*cdf0e10cSrcweir const B2DVector aEdge(rEdgeEnd - rEdgeStart); 1563*cdf0e10cSrcweir bool bDoDistanceTestStart(false); 1564*cdf0e10cSrcweir bool bDoDistanceTestEnd(false); 1565*cdf0e10cSrcweir 1566*cdf0e10cSrcweir if(aEdge.equalZero()) 1567*cdf0e10cSrcweir { 1568*cdf0e10cSrcweir // no edge, just a point. Do one of the distance tests. 1569*cdf0e10cSrcweir bDoDistanceTestStart = true; 1570*cdf0e10cSrcweir } 1571*cdf0e10cSrcweir else 1572*cdf0e10cSrcweir { 1573*cdf0e10cSrcweir // edge has a length. Create perpendicular vector. 1574*cdf0e10cSrcweir const B2DVector aPerpend(getPerpendicular(aEdge)); 1575*cdf0e10cSrcweir double fCut( 1576*cdf0e10cSrcweir (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX()) 1577*cdf0e10cSrcweir + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) / 1578*cdf0e10cSrcweir (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY())); 1579*cdf0e10cSrcweir const double fZero(0.0); 1580*cdf0e10cSrcweir const double fOne(1.0); 1581*cdf0e10cSrcweir 1582*cdf0e10cSrcweir if(fTools::less(fCut, fZero)) 1583*cdf0e10cSrcweir { 1584*cdf0e10cSrcweir // left of rEdgeStart 1585*cdf0e10cSrcweir bDoDistanceTestStart = true; 1586*cdf0e10cSrcweir } 1587*cdf0e10cSrcweir else if(fTools::more(fCut, fOne)) 1588*cdf0e10cSrcweir { 1589*cdf0e10cSrcweir // right of rEdgeEnd 1590*cdf0e10cSrcweir bDoDistanceTestEnd = true; 1591*cdf0e10cSrcweir } 1592*cdf0e10cSrcweir else 1593*cdf0e10cSrcweir { 1594*cdf0e10cSrcweir // inside line [0.0 .. 1.0] 1595*cdf0e10cSrcweir const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut)); 1596*cdf0e10cSrcweir const B2DVector aDelta(rTestPosition - aCutPoint); 1597*cdf0e10cSrcweir const double fDistanceSquare(aDelta.scalar(aDelta)); 1598*cdf0e10cSrcweir 1599*cdf0e10cSrcweir if(fDistanceSquare <= fDistance * fDistance) 1600*cdf0e10cSrcweir { 1601*cdf0e10cSrcweir return true; 1602*cdf0e10cSrcweir } 1603*cdf0e10cSrcweir else 1604*cdf0e10cSrcweir { 1605*cdf0e10cSrcweir return false; 1606*cdf0e10cSrcweir } 1607*cdf0e10cSrcweir } 1608*cdf0e10cSrcweir } 1609*cdf0e10cSrcweir 1610*cdf0e10cSrcweir if(bDoDistanceTestStart) 1611*cdf0e10cSrcweir { 1612*cdf0e10cSrcweir const B2DVector aDelta(rTestPosition - rEdgeStart); 1613*cdf0e10cSrcweir const double fDistanceSquare(aDelta.scalar(aDelta)); 1614*cdf0e10cSrcweir 1615*cdf0e10cSrcweir if(fDistanceSquare <= fDistance * fDistance) 1616*cdf0e10cSrcweir { 1617*cdf0e10cSrcweir return true; 1618*cdf0e10cSrcweir } 1619*cdf0e10cSrcweir } 1620*cdf0e10cSrcweir else if(bDoDistanceTestEnd) 1621*cdf0e10cSrcweir { 1622*cdf0e10cSrcweir const B2DVector aDelta(rTestPosition - rEdgeEnd); 1623*cdf0e10cSrcweir const double fDistanceSquare(aDelta.scalar(aDelta)); 1624*cdf0e10cSrcweir 1625*cdf0e10cSrcweir if(fDistanceSquare <= fDistance * fDistance) 1626*cdf0e10cSrcweir { 1627*cdf0e10cSrcweir return true; 1628*cdf0e10cSrcweir } 1629*cdf0e10cSrcweir } 1630*cdf0e10cSrcweir 1631*cdf0e10cSrcweir return false; 1632*cdf0e10cSrcweir } 1633*cdf0e10cSrcweir 1634*cdf0e10cSrcweir // test if point is inside epsilon-range around the given Polygon. Can be used 1635*cdf0e10cSrcweir // for HitTesting. The epsilon-range is defined to be the tube around the polygon 1636*cdf0e10cSrcweir // with distance fDistance and rounded edges (start and end point). 1637*cdf0e10cSrcweir bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance) 1638*cdf0e10cSrcweir { 1639*cdf0e10cSrcweir // force to non-bezier polygon 1640*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.getDefaultAdaptiveSubdivision()); 1641*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 1642*cdf0e10cSrcweir 1643*cdf0e10cSrcweir if(nPointCount) 1644*cdf0e10cSrcweir { 1645*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L); 1646*cdf0e10cSrcweir B2DPoint aCurrent(aCandidate.getB2DPoint(0)); 1647*cdf0e10cSrcweir 1648*cdf0e10cSrcweir if(nEdgeCount) 1649*cdf0e10cSrcweir { 1650*cdf0e10cSrcweir // edges 1651*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 1652*cdf0e10cSrcweir { 1653*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 1654*cdf0e10cSrcweir const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex)); 1655*cdf0e10cSrcweir 1656*cdf0e10cSrcweir if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance)) 1657*cdf0e10cSrcweir { 1658*cdf0e10cSrcweir return true; 1659*cdf0e10cSrcweir } 1660*cdf0e10cSrcweir 1661*cdf0e10cSrcweir // prepare next step 1662*cdf0e10cSrcweir aCurrent = aNext; 1663*cdf0e10cSrcweir } 1664*cdf0e10cSrcweir } 1665*cdf0e10cSrcweir else 1666*cdf0e10cSrcweir { 1667*cdf0e10cSrcweir // no edges, but points -> not closed. Check single point. Just 1668*cdf0e10cSrcweir // use isInEpsilonRange with twice the same point, it handles those well 1669*cdf0e10cSrcweir if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance)) 1670*cdf0e10cSrcweir { 1671*cdf0e10cSrcweir return true; 1672*cdf0e10cSrcweir } 1673*cdf0e10cSrcweir } 1674*cdf0e10cSrcweir } 1675*cdf0e10cSrcweir 1676*cdf0e10cSrcweir return false; 1677*cdf0e10cSrcweir } 1678*cdf0e10cSrcweir 1679*cdf0e10cSrcweir B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadius ) 1680*cdf0e10cSrcweir { 1681*cdf0e10cSrcweir const double fZero(0.0); 1682*cdf0e10cSrcweir const double fOne(1.0); 1683*cdf0e10cSrcweir 1684*cdf0e10cSrcweir if(fTools::lessOrEqual(fRadius, fZero)) 1685*cdf0e10cSrcweir { 1686*cdf0e10cSrcweir // no radius, use rectangle 1687*cdf0e10cSrcweir return createPolygonFromRect( rRect ); 1688*cdf0e10cSrcweir } 1689*cdf0e10cSrcweir else if(fTools::moreOrEqual(fRadius, fOne)) 1690*cdf0e10cSrcweir { 1691*cdf0e10cSrcweir // full radius, use ellipse 1692*cdf0e10cSrcweir const B2DPoint aCenter(rRect.getCenter()); 1693*cdf0e10cSrcweir const double fRadiusX(rRect.getWidth() / 2.0); 1694*cdf0e10cSrcweir const double fRadiusY(rRect.getHeight() / 2.0); 1695*cdf0e10cSrcweir 1696*cdf0e10cSrcweir return createPolygonFromEllipse( aCenter, fRadiusX, fRadiusY ); 1697*cdf0e10cSrcweir } 1698*cdf0e10cSrcweir else 1699*cdf0e10cSrcweir { 1700*cdf0e10cSrcweir // create rectangle with two radii between ]0.0 .. 1.0[ 1701*cdf0e10cSrcweir return createPolygonFromRect( rRect, fRadius, fRadius ); 1702*cdf0e10cSrcweir } 1703*cdf0e10cSrcweir } 1704*cdf0e10cSrcweir 1705*cdf0e10cSrcweir B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY ) 1706*cdf0e10cSrcweir { 1707*cdf0e10cSrcweir const double fZero(0.0); 1708*cdf0e10cSrcweir const double fOne(1.0); 1709*cdf0e10cSrcweir 1710*cdf0e10cSrcweir // crop to useful values 1711*cdf0e10cSrcweir if(fTools::less(fRadiusX, fZero)) 1712*cdf0e10cSrcweir { 1713*cdf0e10cSrcweir fRadiusX = fZero; 1714*cdf0e10cSrcweir } 1715*cdf0e10cSrcweir else if(fTools::more(fRadiusX, fOne)) 1716*cdf0e10cSrcweir { 1717*cdf0e10cSrcweir fRadiusX = fOne; 1718*cdf0e10cSrcweir } 1719*cdf0e10cSrcweir 1720*cdf0e10cSrcweir if(fTools::less(fRadiusY, fZero)) 1721*cdf0e10cSrcweir { 1722*cdf0e10cSrcweir fRadiusY = fZero; 1723*cdf0e10cSrcweir } 1724*cdf0e10cSrcweir else if(fTools::more(fRadiusY, fOne)) 1725*cdf0e10cSrcweir { 1726*cdf0e10cSrcweir fRadiusY = fOne; 1727*cdf0e10cSrcweir } 1728*cdf0e10cSrcweir 1729*cdf0e10cSrcweir if(fZero == fRadiusX || fZero == fRadiusY) 1730*cdf0e10cSrcweir { 1731*cdf0e10cSrcweir B2DPolygon aRetval; 1732*cdf0e10cSrcweir 1733*cdf0e10cSrcweir // at least in one direction no radius, use rectangle. 1734*cdf0e10cSrcweir // Do not use createPolygonFromRect() here since original 1735*cdf0e10cSrcweir // creator (historical reasons) still creates a start point at the 1736*cdf0e10cSrcweir // bottom center, so do the same here to get the same line patterns. 1737*cdf0e10cSrcweir // Due to this the order of points is different, too. 1738*cdf0e10cSrcweir const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY()); 1739*cdf0e10cSrcweir aRetval.append(aBottomCenter); 1740*cdf0e10cSrcweir 1741*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) ); 1742*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) ); 1743*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) ); 1744*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) ); 1745*cdf0e10cSrcweir 1746*cdf0e10cSrcweir // close 1747*cdf0e10cSrcweir aRetval.setClosed( true ); 1748*cdf0e10cSrcweir 1749*cdf0e10cSrcweir return aRetval; 1750*cdf0e10cSrcweir } 1751*cdf0e10cSrcweir else if(fOne == fRadiusX && fOne == fRadiusY) 1752*cdf0e10cSrcweir { 1753*cdf0e10cSrcweir // in both directions full radius, use ellipse 1754*cdf0e10cSrcweir const B2DPoint aCenter(rRect.getCenter()); 1755*cdf0e10cSrcweir const double fRectRadiusX(rRect.getWidth() / 2.0); 1756*cdf0e10cSrcweir const double fRectRadiusY(rRect.getHeight() / 2.0); 1757*cdf0e10cSrcweir 1758*cdf0e10cSrcweir return createPolygonFromEllipse( aCenter, fRectRadiusX, fRectRadiusY ); 1759*cdf0e10cSrcweir } 1760*cdf0e10cSrcweir else 1761*cdf0e10cSrcweir { 1762*cdf0e10cSrcweir B2DPolygon aRetval; 1763*cdf0e10cSrcweir const double fBowX((rRect.getWidth() / 2.0) * fRadiusX); 1764*cdf0e10cSrcweir const double fBowY((rRect.getHeight() / 2.0) * fRadiusY); 1765*cdf0e10cSrcweir const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); 1766*cdf0e10cSrcweir 1767*cdf0e10cSrcweir // create start point at bottom center 1768*cdf0e10cSrcweir if(fOne != fRadiusX) 1769*cdf0e10cSrcweir { 1770*cdf0e10cSrcweir const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY()); 1771*cdf0e10cSrcweir aRetval.append(aBottomCenter); 1772*cdf0e10cSrcweir } 1773*cdf0e10cSrcweir 1774*cdf0e10cSrcweir // create first bow 1775*cdf0e10cSrcweir { 1776*cdf0e10cSrcweir const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY()); 1777*cdf0e10cSrcweir const B2DPoint aStart(aBottomRight + B2DPoint(-fBowX, 0.0)); 1778*cdf0e10cSrcweir const B2DPoint aStop(aBottomRight + B2DPoint(0.0, -fBowY)); 1779*cdf0e10cSrcweir aRetval.append(aStart); 1780*cdf0e10cSrcweir aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop); 1781*cdf0e10cSrcweir } 1782*cdf0e10cSrcweir 1783*cdf0e10cSrcweir // create second bow 1784*cdf0e10cSrcweir { 1785*cdf0e10cSrcweir const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY()); 1786*cdf0e10cSrcweir const B2DPoint aStart(aTopRight + B2DPoint(0.0, fBowY)); 1787*cdf0e10cSrcweir const B2DPoint aStop(aTopRight + B2DPoint(-fBowX, 0.0)); 1788*cdf0e10cSrcweir aRetval.append(aStart); 1789*cdf0e10cSrcweir aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop); 1790*cdf0e10cSrcweir } 1791*cdf0e10cSrcweir 1792*cdf0e10cSrcweir // create third bow 1793*cdf0e10cSrcweir { 1794*cdf0e10cSrcweir const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY()); 1795*cdf0e10cSrcweir const B2DPoint aStart(aTopLeft + B2DPoint(fBowX, 0.0)); 1796*cdf0e10cSrcweir const B2DPoint aStop(aTopLeft + B2DPoint(0.0, fBowY)); 1797*cdf0e10cSrcweir aRetval.append(aStart); 1798*cdf0e10cSrcweir aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop); 1799*cdf0e10cSrcweir } 1800*cdf0e10cSrcweir 1801*cdf0e10cSrcweir // create forth bow 1802*cdf0e10cSrcweir { 1803*cdf0e10cSrcweir const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY()); 1804*cdf0e10cSrcweir const B2DPoint aStart(aBottomLeft + B2DPoint(0.0, -fBowY)); 1805*cdf0e10cSrcweir const B2DPoint aStop(aBottomLeft + B2DPoint(fBowX, 0.0)); 1806*cdf0e10cSrcweir aRetval.append(aStart); 1807*cdf0e10cSrcweir aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop); 1808*cdf0e10cSrcweir } 1809*cdf0e10cSrcweir 1810*cdf0e10cSrcweir // close 1811*cdf0e10cSrcweir aRetval.setClosed( true ); 1812*cdf0e10cSrcweir 1813*cdf0e10cSrcweir // remove double created points if there are extreme radii envolved 1814*cdf0e10cSrcweir if(fOne == fRadiusX || fOne == fRadiusY) 1815*cdf0e10cSrcweir { 1816*cdf0e10cSrcweir aRetval.removeDoublePoints(); 1817*cdf0e10cSrcweir } 1818*cdf0e10cSrcweir 1819*cdf0e10cSrcweir return aRetval; 1820*cdf0e10cSrcweir } 1821*cdf0e10cSrcweir } 1822*cdf0e10cSrcweir 1823*cdf0e10cSrcweir B2DPolygon createPolygonFromRect( const B2DRectangle& rRect ) 1824*cdf0e10cSrcweir { 1825*cdf0e10cSrcweir B2DPolygon aRetval; 1826*cdf0e10cSrcweir 1827*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) ); 1828*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) ); 1829*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) ); 1830*cdf0e10cSrcweir aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) ); 1831*cdf0e10cSrcweir 1832*cdf0e10cSrcweir // close 1833*cdf0e10cSrcweir aRetval.setClosed( true ); 1834*cdf0e10cSrcweir 1835*cdf0e10cSrcweir return aRetval; 1836*cdf0e10cSrcweir } 1837*cdf0e10cSrcweir 1838*cdf0e10cSrcweir B2DPolygon createUnitPolygon() 1839*cdf0e10cSrcweir { 1840*cdf0e10cSrcweir static B2DPolygon aRetval; 1841*cdf0e10cSrcweir 1842*cdf0e10cSrcweir if(!aRetval.count()) 1843*cdf0e10cSrcweir { 1844*cdf0e10cSrcweir aRetval.append( B2DPoint( 0.0, 0.0 ) ); 1845*cdf0e10cSrcweir aRetval.append( B2DPoint( 1.0, 0.0 ) ); 1846*cdf0e10cSrcweir aRetval.append( B2DPoint( 1.0, 1.0 ) ); 1847*cdf0e10cSrcweir aRetval.append( B2DPoint( 0.0, 1.0 ) ); 1848*cdf0e10cSrcweir 1849*cdf0e10cSrcweir // close 1850*cdf0e10cSrcweir aRetval.setClosed( true ); 1851*cdf0e10cSrcweir } 1852*cdf0e10cSrcweir 1853*cdf0e10cSrcweir return aRetval; 1854*cdf0e10cSrcweir } 1855*cdf0e10cSrcweir 1856*cdf0e10cSrcweir B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius ) 1857*cdf0e10cSrcweir { 1858*cdf0e10cSrcweir return createPolygonFromEllipse( rCenter, fRadius, fRadius ); 1859*cdf0e10cSrcweir } 1860*cdf0e10cSrcweir 1861*cdf0e10cSrcweir B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant) 1862*cdf0e10cSrcweir { 1863*cdf0e10cSrcweir B2DPolygon aUnitCircle; 1864*cdf0e10cSrcweir const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); 1865*cdf0e10cSrcweir const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER)); 1866*cdf0e10cSrcweir const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER)); 1867*cdf0e10cSrcweir 1868*cdf0e10cSrcweir B2DPoint aPoint(1.0, 0.0); 1869*cdf0e10cSrcweir B2DPoint aForward(1.0, fScaledKappa); 1870*cdf0e10cSrcweir B2DPoint aBackward(1.0, -fScaledKappa); 1871*cdf0e10cSrcweir 1872*cdf0e10cSrcweir if(0 != nStartQuadrant) 1873*cdf0e10cSrcweir { 1874*cdf0e10cSrcweir const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4))); 1875*cdf0e10cSrcweir aPoint *= aQuadrantMatrix; 1876*cdf0e10cSrcweir aBackward *= aQuadrantMatrix; 1877*cdf0e10cSrcweir aForward *= aQuadrantMatrix; 1878*cdf0e10cSrcweir } 1879*cdf0e10cSrcweir 1880*cdf0e10cSrcweir aUnitCircle.append(aPoint); 1881*cdf0e10cSrcweir 1882*cdf0e10cSrcweir for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++) 1883*cdf0e10cSrcweir { 1884*cdf0e10cSrcweir aPoint *= aRotateMatrix; 1885*cdf0e10cSrcweir aBackward *= aRotateMatrix; 1886*cdf0e10cSrcweir aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint); 1887*cdf0e10cSrcweir aForward *= aRotateMatrix; 1888*cdf0e10cSrcweir } 1889*cdf0e10cSrcweir 1890*cdf0e10cSrcweir aUnitCircle.setClosed(true); 1891*cdf0e10cSrcweir aUnitCircle.removeDoublePoints(); 1892*cdf0e10cSrcweir 1893*cdf0e10cSrcweir return aUnitCircle; 1894*cdf0e10cSrcweir } 1895*cdf0e10cSrcweir 1896*cdf0e10cSrcweir B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant) 1897*cdf0e10cSrcweir { 1898*cdf0e10cSrcweir switch(nStartQuadrant % 4) 1899*cdf0e10cSrcweir { 1900*cdf0e10cSrcweir case 1 : 1901*cdf0e10cSrcweir { 1902*cdf0e10cSrcweir static B2DPolygon aUnitCircleStartQuadrantOne; 1903*cdf0e10cSrcweir 1904*cdf0e10cSrcweir if(!aUnitCircleStartQuadrantOne.count()) 1905*cdf0e10cSrcweir { 1906*cdf0e10cSrcweir ::osl::Mutex m_mutex; 1907*cdf0e10cSrcweir aUnitCircleStartQuadrantOne = impCreateUnitCircle(1); 1908*cdf0e10cSrcweir } 1909*cdf0e10cSrcweir 1910*cdf0e10cSrcweir return aUnitCircleStartQuadrantOne; 1911*cdf0e10cSrcweir } 1912*cdf0e10cSrcweir case 2 : 1913*cdf0e10cSrcweir { 1914*cdf0e10cSrcweir static B2DPolygon aUnitCircleStartQuadrantTwo; 1915*cdf0e10cSrcweir 1916*cdf0e10cSrcweir if(!aUnitCircleStartQuadrantTwo.count()) 1917*cdf0e10cSrcweir { 1918*cdf0e10cSrcweir ::osl::Mutex m_mutex; 1919*cdf0e10cSrcweir aUnitCircleStartQuadrantTwo = impCreateUnitCircle(2); 1920*cdf0e10cSrcweir } 1921*cdf0e10cSrcweir 1922*cdf0e10cSrcweir return aUnitCircleStartQuadrantTwo; 1923*cdf0e10cSrcweir } 1924*cdf0e10cSrcweir case 3 : 1925*cdf0e10cSrcweir { 1926*cdf0e10cSrcweir static B2DPolygon aUnitCircleStartQuadrantThree; 1927*cdf0e10cSrcweir 1928*cdf0e10cSrcweir if(!aUnitCircleStartQuadrantThree.count()) 1929*cdf0e10cSrcweir { 1930*cdf0e10cSrcweir ::osl::Mutex m_mutex; 1931*cdf0e10cSrcweir aUnitCircleStartQuadrantThree = impCreateUnitCircle(3); 1932*cdf0e10cSrcweir } 1933*cdf0e10cSrcweir 1934*cdf0e10cSrcweir return aUnitCircleStartQuadrantThree; 1935*cdf0e10cSrcweir } 1936*cdf0e10cSrcweir default : // case 0 : 1937*cdf0e10cSrcweir { 1938*cdf0e10cSrcweir static B2DPolygon aUnitCircleStartQuadrantZero; 1939*cdf0e10cSrcweir 1940*cdf0e10cSrcweir if(!aUnitCircleStartQuadrantZero.count()) 1941*cdf0e10cSrcweir { 1942*cdf0e10cSrcweir ::osl::Mutex m_mutex; 1943*cdf0e10cSrcweir aUnitCircleStartQuadrantZero = impCreateUnitCircle(0); 1944*cdf0e10cSrcweir } 1945*cdf0e10cSrcweir 1946*cdf0e10cSrcweir return aUnitCircleStartQuadrantZero; 1947*cdf0e10cSrcweir } 1948*cdf0e10cSrcweir } 1949*cdf0e10cSrcweir } 1950*cdf0e10cSrcweir 1951*cdf0e10cSrcweir B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ) 1952*cdf0e10cSrcweir { 1953*cdf0e10cSrcweir B2DPolygon aRetval(createPolygonFromUnitCircle()); 1954*cdf0e10cSrcweir const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY())); 1955*cdf0e10cSrcweir 1956*cdf0e10cSrcweir aRetval.transform(aMatrix); 1957*cdf0e10cSrcweir 1958*cdf0e10cSrcweir return aRetval; 1959*cdf0e10cSrcweir } 1960*cdf0e10cSrcweir 1961*cdf0e10cSrcweir B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd ) 1962*cdf0e10cSrcweir { 1963*cdf0e10cSrcweir B2DPolygon aRetval; 1964*cdf0e10cSrcweir 1965*cdf0e10cSrcweir // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI 1966*cdf0e10cSrcweir // falls back to 0.0 to ensure a unique definition 1967*cdf0e10cSrcweir if(fTools::less(fStart, 0.0)) 1968*cdf0e10cSrcweir { 1969*cdf0e10cSrcweir fStart = 0.0; 1970*cdf0e10cSrcweir } 1971*cdf0e10cSrcweir 1972*cdf0e10cSrcweir if(fTools::moreOrEqual(fStart, F_2PI)) 1973*cdf0e10cSrcweir { 1974*cdf0e10cSrcweir fStart = 0.0; 1975*cdf0e10cSrcweir } 1976*cdf0e10cSrcweir 1977*cdf0e10cSrcweir if(fTools::less(fEnd, 0.0)) 1978*cdf0e10cSrcweir { 1979*cdf0e10cSrcweir fEnd = 0.0; 1980*cdf0e10cSrcweir } 1981*cdf0e10cSrcweir 1982*cdf0e10cSrcweir if(fTools::moreOrEqual(fEnd, F_2PI)) 1983*cdf0e10cSrcweir { 1984*cdf0e10cSrcweir fEnd = 0.0; 1985*cdf0e10cSrcweir } 1986*cdf0e10cSrcweir 1987*cdf0e10cSrcweir if(fTools::equal(fStart, fEnd)) 1988*cdf0e10cSrcweir { 1989*cdf0e10cSrcweir // same start and end angle, add single point 1990*cdf0e10cSrcweir aRetval.append(B2DPoint(cos(fStart), sin(fStart))); 1991*cdf0e10cSrcweir } 1992*cdf0e10cSrcweir else 1993*cdf0e10cSrcweir { 1994*cdf0e10cSrcweir const sal_uInt32 nSegments(STEPSPERQUARTER * 4); 1995*cdf0e10cSrcweir const double fAnglePerSegment(F_PI2 / STEPSPERQUARTER); 1996*cdf0e10cSrcweir const sal_uInt32 nStartSegment(sal_uInt32(fStart / fAnglePerSegment) % nSegments); 1997*cdf0e10cSrcweir const sal_uInt32 nEndSegment(sal_uInt32(fEnd / fAnglePerSegment) % nSegments); 1998*cdf0e10cSrcweir const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); 1999*cdf0e10cSrcweir const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER)); 2000*cdf0e10cSrcweir 2001*cdf0e10cSrcweir B2DPoint aSegStart(cos(fStart), sin(fStart)); 2002*cdf0e10cSrcweir aRetval.append(aSegStart); 2003*cdf0e10cSrcweir 2004*cdf0e10cSrcweir if(nStartSegment == nEndSegment && fTools::more(fEnd, fStart)) 2005*cdf0e10cSrcweir { 2006*cdf0e10cSrcweir // start and end in one sector and in the right order, create in one segment 2007*cdf0e10cSrcweir const B2DPoint aSegEnd(cos(fEnd), sin(fEnd)); 2008*cdf0e10cSrcweir const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment)); 2009*cdf0e10cSrcweir 2010*cdf0e10cSrcweir aRetval.appendBezierSegment( 2011*cdf0e10cSrcweir aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), 2012*cdf0e10cSrcweir aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), 2013*cdf0e10cSrcweir aSegEnd); 2014*cdf0e10cSrcweir } 2015*cdf0e10cSrcweir else 2016*cdf0e10cSrcweir { 2017*cdf0e10cSrcweir double fSegEndRad((nStartSegment + 1) * fAnglePerSegment); 2018*cdf0e10cSrcweir double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment)); 2019*cdf0e10cSrcweir B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad)); 2020*cdf0e10cSrcweir 2021*cdf0e10cSrcweir aRetval.appendBezierSegment( 2022*cdf0e10cSrcweir aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), 2023*cdf0e10cSrcweir aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), 2024*cdf0e10cSrcweir aSegEnd); 2025*cdf0e10cSrcweir 2026*cdf0e10cSrcweir sal_uInt32 nSegment((nStartSegment + 1) % nSegments); 2027*cdf0e10cSrcweir aSegStart = aSegEnd; 2028*cdf0e10cSrcweir 2029*cdf0e10cSrcweir while(nSegment != nEndSegment) 2030*cdf0e10cSrcweir { 2031*cdf0e10cSrcweir // No end in this sector, add full sector. 2032*cdf0e10cSrcweir fSegEndRad = (nSegment + 1) * fAnglePerSegment; 2033*cdf0e10cSrcweir aSegEnd = B2DPoint(cos(fSegEndRad), sin(fSegEndRad)); 2034*cdf0e10cSrcweir 2035*cdf0e10cSrcweir aRetval.appendBezierSegment( 2036*cdf0e10cSrcweir aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa), 2037*cdf0e10cSrcweir aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa), 2038*cdf0e10cSrcweir aSegEnd); 2039*cdf0e10cSrcweir 2040*cdf0e10cSrcweir nSegment = (nSegment + 1) % nSegments; 2041*cdf0e10cSrcweir aSegStart = aSegEnd; 2042*cdf0e10cSrcweir } 2043*cdf0e10cSrcweir 2044*cdf0e10cSrcweir // End in this sector 2045*cdf0e10cSrcweir const double fSegStartRad(nSegment * fAnglePerSegment); 2046*cdf0e10cSrcweir fFactor = fScaledKappa * ((fEnd - fSegStartRad) / fAnglePerSegment); 2047*cdf0e10cSrcweir aSegEnd = B2DPoint(cos(fEnd), sin(fEnd)); 2048*cdf0e10cSrcweir 2049*cdf0e10cSrcweir aRetval.appendBezierSegment( 2050*cdf0e10cSrcweir aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), 2051*cdf0e10cSrcweir aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), 2052*cdf0e10cSrcweir aSegEnd); 2053*cdf0e10cSrcweir } 2054*cdf0e10cSrcweir } 2055*cdf0e10cSrcweir 2056*cdf0e10cSrcweir // remove double points between segments created by segmented creation 2057*cdf0e10cSrcweir aRetval.removeDoublePoints(); 2058*cdf0e10cSrcweir 2059*cdf0e10cSrcweir return aRetval; 2060*cdf0e10cSrcweir } 2061*cdf0e10cSrcweir 2062*cdf0e10cSrcweir B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd ) 2063*cdf0e10cSrcweir { 2064*cdf0e10cSrcweir B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd)); 2065*cdf0e10cSrcweir const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY())); 2066*cdf0e10cSrcweir 2067*cdf0e10cSrcweir aRetval.transform(aMatrix); 2068*cdf0e10cSrcweir 2069*cdf0e10cSrcweir return aRetval; 2070*cdf0e10cSrcweir } 2071*cdf0e10cSrcweir 2072*cdf0e10cSrcweir bool hasNeutralPoints(const B2DPolygon& rCandidate) 2073*cdf0e10cSrcweir { 2074*cdf0e10cSrcweir OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)"); 2075*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2076*cdf0e10cSrcweir 2077*cdf0e10cSrcweir if(nPointCount > 2L) 2078*cdf0e10cSrcweir { 2079*cdf0e10cSrcweir B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); 2080*cdf0e10cSrcweir B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); 2081*cdf0e10cSrcweir 2082*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 2083*cdf0e10cSrcweir { 2084*cdf0e10cSrcweir const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); 2085*cdf0e10cSrcweir const B2DVector aPrevVec(aPrevPoint - aCurrPoint); 2086*cdf0e10cSrcweir const B2DVector aNextVec(aNextPoint - aCurrPoint); 2087*cdf0e10cSrcweir const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec)); 2088*cdf0e10cSrcweir 2089*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation) 2090*cdf0e10cSrcweir { 2091*cdf0e10cSrcweir // current has neutral orientation 2092*cdf0e10cSrcweir return true; 2093*cdf0e10cSrcweir } 2094*cdf0e10cSrcweir else 2095*cdf0e10cSrcweir { 2096*cdf0e10cSrcweir // prepare next 2097*cdf0e10cSrcweir aPrevPoint = aCurrPoint; 2098*cdf0e10cSrcweir aCurrPoint = aNextPoint; 2099*cdf0e10cSrcweir } 2100*cdf0e10cSrcweir } 2101*cdf0e10cSrcweir } 2102*cdf0e10cSrcweir 2103*cdf0e10cSrcweir return false; 2104*cdf0e10cSrcweir } 2105*cdf0e10cSrcweir 2106*cdf0e10cSrcweir B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate) 2107*cdf0e10cSrcweir { 2108*cdf0e10cSrcweir if(hasNeutralPoints(rCandidate)) 2109*cdf0e10cSrcweir { 2110*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2111*cdf0e10cSrcweir B2DPolygon aRetval; 2112*cdf0e10cSrcweir B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); 2113*cdf0e10cSrcweir B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); 2114*cdf0e10cSrcweir 2115*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 2116*cdf0e10cSrcweir { 2117*cdf0e10cSrcweir const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); 2118*cdf0e10cSrcweir const B2DVector aPrevVec(aPrevPoint - aCurrPoint); 2119*cdf0e10cSrcweir const B2DVector aNextVec(aNextPoint - aCurrPoint); 2120*cdf0e10cSrcweir const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec)); 2121*cdf0e10cSrcweir 2122*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation) 2123*cdf0e10cSrcweir { 2124*cdf0e10cSrcweir // current has neutral orientation, leave it out and prepare next 2125*cdf0e10cSrcweir aCurrPoint = aNextPoint; 2126*cdf0e10cSrcweir } 2127*cdf0e10cSrcweir else 2128*cdf0e10cSrcweir { 2129*cdf0e10cSrcweir // add current point 2130*cdf0e10cSrcweir aRetval.append(aCurrPoint); 2131*cdf0e10cSrcweir 2132*cdf0e10cSrcweir // prepare next 2133*cdf0e10cSrcweir aPrevPoint = aCurrPoint; 2134*cdf0e10cSrcweir aCurrPoint = aNextPoint; 2135*cdf0e10cSrcweir } 2136*cdf0e10cSrcweir } 2137*cdf0e10cSrcweir 2138*cdf0e10cSrcweir while(aRetval.count() && ORIENTATION_NEUTRAL == getOrientationForIndex(aRetval, 0L)) 2139*cdf0e10cSrcweir { 2140*cdf0e10cSrcweir aRetval.remove(0L); 2141*cdf0e10cSrcweir } 2142*cdf0e10cSrcweir 2143*cdf0e10cSrcweir // copy closed state 2144*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 2145*cdf0e10cSrcweir 2146*cdf0e10cSrcweir return aRetval; 2147*cdf0e10cSrcweir } 2148*cdf0e10cSrcweir else 2149*cdf0e10cSrcweir { 2150*cdf0e10cSrcweir return rCandidate; 2151*cdf0e10cSrcweir } 2152*cdf0e10cSrcweir } 2153*cdf0e10cSrcweir 2154*cdf0e10cSrcweir bool isConvex(const B2DPolygon& rCandidate) 2155*cdf0e10cSrcweir { 2156*cdf0e10cSrcweir OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)"); 2157*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2158*cdf0e10cSrcweir 2159*cdf0e10cSrcweir if(nPointCount > 2L) 2160*cdf0e10cSrcweir { 2161*cdf0e10cSrcweir const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); 2162*cdf0e10cSrcweir B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); 2163*cdf0e10cSrcweir B2DVector aCurrVec(aPrevPoint - aCurrPoint); 2164*cdf0e10cSrcweir B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL); 2165*cdf0e10cSrcweir 2166*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 2167*cdf0e10cSrcweir { 2168*cdf0e10cSrcweir const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); 2169*cdf0e10cSrcweir const B2DVector aNextVec(aNextPoint - aCurrPoint); 2170*cdf0e10cSrcweir const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec)); 2171*cdf0e10cSrcweir 2172*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation) 2173*cdf0e10cSrcweir { 2174*cdf0e10cSrcweir // set start value, maybe neutral again 2175*cdf0e10cSrcweir aOrientation = aCurrentOrientation; 2176*cdf0e10cSrcweir } 2177*cdf0e10cSrcweir else 2178*cdf0e10cSrcweir { 2179*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL != aCurrentOrientation && aCurrentOrientation != aOrientation) 2180*cdf0e10cSrcweir { 2181*cdf0e10cSrcweir // different orientations found, that's it 2182*cdf0e10cSrcweir return false; 2183*cdf0e10cSrcweir } 2184*cdf0e10cSrcweir } 2185*cdf0e10cSrcweir 2186*cdf0e10cSrcweir // prepare next 2187*cdf0e10cSrcweir aCurrPoint = aNextPoint; 2188*cdf0e10cSrcweir aCurrVec = -aNextVec; 2189*cdf0e10cSrcweir } 2190*cdf0e10cSrcweir } 2191*cdf0e10cSrcweir 2192*cdf0e10cSrcweir return true; 2193*cdf0e10cSrcweir } 2194*cdf0e10cSrcweir 2195*cdf0e10cSrcweir B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex) 2196*cdf0e10cSrcweir { 2197*cdf0e10cSrcweir OSL_ENSURE(nIndex < rCandidate.count(), "getOrientationForIndex: index out of range (!)"); 2198*cdf0e10cSrcweir const B2DPoint aPrev(rCandidate.getB2DPoint(getIndexOfPredecessor(nIndex, rCandidate))); 2199*cdf0e10cSrcweir const B2DPoint aCurr(rCandidate.getB2DPoint(nIndex)); 2200*cdf0e10cSrcweir const B2DPoint aNext(rCandidate.getB2DPoint(getIndexOfSuccessor(nIndex, rCandidate))); 2201*cdf0e10cSrcweir const B2DVector aBack(aPrev - aCurr); 2202*cdf0e10cSrcweir const B2DVector aForw(aNext - aCurr); 2203*cdf0e10cSrcweir 2204*cdf0e10cSrcweir return getOrientation(aForw, aBack); 2205*cdf0e10cSrcweir } 2206*cdf0e10cSrcweir 2207*cdf0e10cSrcweir bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints) 2208*cdf0e10cSrcweir { 2209*cdf0e10cSrcweir if(rCandidate.equal(rStart) || rCandidate.equal(rEnd)) 2210*cdf0e10cSrcweir { 2211*cdf0e10cSrcweir // candidate is in epsilon around start or end -> inside 2212*cdf0e10cSrcweir return bWithPoints; 2213*cdf0e10cSrcweir } 2214*cdf0e10cSrcweir else if(rStart.equal(rEnd)) 2215*cdf0e10cSrcweir { 2216*cdf0e10cSrcweir // start and end are equal, but candidate is outside their epsilon -> outside 2217*cdf0e10cSrcweir return false; 2218*cdf0e10cSrcweir } 2219*cdf0e10cSrcweir else 2220*cdf0e10cSrcweir { 2221*cdf0e10cSrcweir const B2DVector aEdgeVector(rEnd - rStart); 2222*cdf0e10cSrcweir const B2DVector aTestVector(rCandidate - rStart); 2223*cdf0e10cSrcweir 2224*cdf0e10cSrcweir if(areParallel(aEdgeVector, aTestVector)) 2225*cdf0e10cSrcweir { 2226*cdf0e10cSrcweir const double fZero(0.0); 2227*cdf0e10cSrcweir const double fOne(1.0); 2228*cdf0e10cSrcweir const double fParamTestOnCurr(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY()) 2229*cdf0e10cSrcweir ? aTestVector.getX() / aEdgeVector.getX() 2230*cdf0e10cSrcweir : aTestVector.getY() / aEdgeVector.getY()); 2231*cdf0e10cSrcweir 2232*cdf0e10cSrcweir if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne)) 2233*cdf0e10cSrcweir { 2234*cdf0e10cSrcweir return true; 2235*cdf0e10cSrcweir } 2236*cdf0e10cSrcweir } 2237*cdf0e10cSrcweir 2238*cdf0e10cSrcweir return false; 2239*cdf0e10cSrcweir } 2240*cdf0e10cSrcweir } 2241*cdf0e10cSrcweir 2242*cdf0e10cSrcweir bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints) 2243*cdf0e10cSrcweir { 2244*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate); 2245*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 2246*cdf0e10cSrcweir 2247*cdf0e10cSrcweir if(nPointCount > 1L) 2248*cdf0e10cSrcweir { 2249*cdf0e10cSrcweir const sal_uInt32 nLoopCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L); 2250*cdf0e10cSrcweir B2DPoint aCurrentPoint(aCandidate.getB2DPoint(0L)); 2251*cdf0e10cSrcweir 2252*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nLoopCount; a++) 2253*cdf0e10cSrcweir { 2254*cdf0e10cSrcweir const B2DPoint aNextPoint(aCandidate.getB2DPoint((a + 1L) % nPointCount)); 2255*cdf0e10cSrcweir 2256*cdf0e10cSrcweir if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints)) 2257*cdf0e10cSrcweir { 2258*cdf0e10cSrcweir return true; 2259*cdf0e10cSrcweir } 2260*cdf0e10cSrcweir 2261*cdf0e10cSrcweir aCurrentPoint = aNextPoint; 2262*cdf0e10cSrcweir } 2263*cdf0e10cSrcweir } 2264*cdf0e10cSrcweir else if(nPointCount && bWithPoints) 2265*cdf0e10cSrcweir { 2266*cdf0e10cSrcweir return rPoint.equal(aCandidate.getB2DPoint(0L)); 2267*cdf0e10cSrcweir } 2268*cdf0e10cSrcweir 2269*cdf0e10cSrcweir return false; 2270*cdf0e10cSrcweir } 2271*cdf0e10cSrcweir 2272*cdf0e10cSrcweir bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder) 2273*cdf0e10cSrcweir { 2274*cdf0e10cSrcweir if(arePointsOnSameSideOfLine(rA, rB, rC, rCandidate, bWithBorder)) 2275*cdf0e10cSrcweir { 2276*cdf0e10cSrcweir if(arePointsOnSameSideOfLine(rB, rC, rA, rCandidate, bWithBorder)) 2277*cdf0e10cSrcweir { 2278*cdf0e10cSrcweir if(arePointsOnSameSideOfLine(rC, rA, rB, rCandidate, bWithBorder)) 2279*cdf0e10cSrcweir { 2280*cdf0e10cSrcweir return true; 2281*cdf0e10cSrcweir } 2282*cdf0e10cSrcweir } 2283*cdf0e10cSrcweir } 2284*cdf0e10cSrcweir 2285*cdf0e10cSrcweir return false; 2286*cdf0e10cSrcweir } 2287*cdf0e10cSrcweir 2288*cdf0e10cSrcweir bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine) 2289*cdf0e10cSrcweir { 2290*cdf0e10cSrcweir const B2DVector aLineVector(rEnd - rStart); 2291*cdf0e10cSrcweir const B2DVector aVectorToA(rEnd - rCandidateA); 2292*cdf0e10cSrcweir const double fCrossA(aLineVector.cross(aVectorToA)); 2293*cdf0e10cSrcweir 2294*cdf0e10cSrcweir if(fTools::equalZero(fCrossA)) 2295*cdf0e10cSrcweir { 2296*cdf0e10cSrcweir // one point on the line 2297*cdf0e10cSrcweir return bWithLine; 2298*cdf0e10cSrcweir } 2299*cdf0e10cSrcweir 2300*cdf0e10cSrcweir const B2DVector aVectorToB(rEnd - rCandidateB); 2301*cdf0e10cSrcweir const double fCrossB(aLineVector.cross(aVectorToB)); 2302*cdf0e10cSrcweir 2303*cdf0e10cSrcweir if(fTools::equalZero(fCrossB)) 2304*cdf0e10cSrcweir { 2305*cdf0e10cSrcweir // one point on the line 2306*cdf0e10cSrcweir return bWithLine; 2307*cdf0e10cSrcweir } 2308*cdf0e10cSrcweir 2309*cdf0e10cSrcweir // return true if they both have the same sign 2310*cdf0e10cSrcweir return ((fCrossA > 0.0) == (fCrossB > 0.0)); 2311*cdf0e10cSrcweir } 2312*cdf0e10cSrcweir 2313*cdf0e10cSrcweir void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget) 2314*cdf0e10cSrcweir { 2315*cdf0e10cSrcweir const sal_uInt32 nCount(rCandidate.count()); 2316*cdf0e10cSrcweir 2317*cdf0e10cSrcweir if(nCount > 2L) 2318*cdf0e10cSrcweir { 2319*cdf0e10cSrcweir const B2DPoint aStart(rCandidate.getB2DPoint(0L)); 2320*cdf0e10cSrcweir B2DPoint aLast(rCandidate.getB2DPoint(1L)); 2321*cdf0e10cSrcweir 2322*cdf0e10cSrcweir for(sal_uInt32 a(2L); a < nCount; a++) 2323*cdf0e10cSrcweir { 2324*cdf0e10cSrcweir const B2DPoint aCurrent(rCandidate.getB2DPoint(a)); 2325*cdf0e10cSrcweir rTarget.append(aStart); 2326*cdf0e10cSrcweir rTarget.append(aLast); 2327*cdf0e10cSrcweir rTarget.append(aCurrent); 2328*cdf0e10cSrcweir 2329*cdf0e10cSrcweir // prepare next 2330*cdf0e10cSrcweir aLast = aCurrent; 2331*cdf0e10cSrcweir } 2332*cdf0e10cSrcweir } 2333*cdf0e10cSrcweir } 2334*cdf0e10cSrcweir 2335*cdf0e10cSrcweir namespace 2336*cdf0e10cSrcweir { 2337*cdf0e10cSrcweir /// return 0 for input of 0, -1 for negative and 1 for positive input 2338*cdf0e10cSrcweir inline int lcl_sgn( const double n ) 2339*cdf0e10cSrcweir { 2340*cdf0e10cSrcweir return n == 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n); 2341*cdf0e10cSrcweir } 2342*cdf0e10cSrcweir } 2343*cdf0e10cSrcweir 2344*cdf0e10cSrcweir bool isRectangle( const B2DPolygon& rPoly ) 2345*cdf0e10cSrcweir { 2346*cdf0e10cSrcweir // polygon must be closed to resemble a rect, and contain 2347*cdf0e10cSrcweir // at least four points. 2348*cdf0e10cSrcweir if( !rPoly.isClosed() || 2349*cdf0e10cSrcweir rPoly.count() < 4 || 2350*cdf0e10cSrcweir rPoly.areControlPointsUsed() ) 2351*cdf0e10cSrcweir { 2352*cdf0e10cSrcweir return false; 2353*cdf0e10cSrcweir } 2354*cdf0e10cSrcweir 2355*cdf0e10cSrcweir // number of 90 degree turns the polygon has taken 2356*cdf0e10cSrcweir int nNumTurns(0); 2357*cdf0e10cSrcweir 2358*cdf0e10cSrcweir int nVerticalEdgeType=0; 2359*cdf0e10cSrcweir int nHorizontalEdgeType=0; 2360*cdf0e10cSrcweir bool bNullVertex(true); 2361*cdf0e10cSrcweir bool bCWPolygon(false); // when true, polygon is CW 2362*cdf0e10cSrcweir // oriented, when false, CCW 2363*cdf0e10cSrcweir bool bOrientationSet(false); // when false, polygon 2364*cdf0e10cSrcweir // orientation has not yet 2365*cdf0e10cSrcweir // been determined. 2366*cdf0e10cSrcweir 2367*cdf0e10cSrcweir // scan all _edges_ (which involves coming back to point 0 2368*cdf0e10cSrcweir // for the last edge - thus the modulo operation below) 2369*cdf0e10cSrcweir const sal_Int32 nCount( rPoly.count() ); 2370*cdf0e10cSrcweir for( sal_Int32 i=0; i<nCount; ++i ) 2371*cdf0e10cSrcweir { 2372*cdf0e10cSrcweir const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) ); 2373*cdf0e10cSrcweir const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) ); 2374*cdf0e10cSrcweir 2375*cdf0e10cSrcweir // is 0 for zero direction vector, 1 for south edge and -1 2376*cdf0e10cSrcweir // for north edge (standard screen coordinate system) 2377*cdf0e10cSrcweir int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) ); 2378*cdf0e10cSrcweir 2379*cdf0e10cSrcweir // is 0 for zero direction vector, 1 for east edge and -1 2380*cdf0e10cSrcweir // for west edge (standard screen coordinate system) 2381*cdf0e10cSrcweir int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) ); 2382*cdf0e10cSrcweir 2383*cdf0e10cSrcweir if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType ) 2384*cdf0e10cSrcweir return false; // oblique edge - for sure no rect 2385*cdf0e10cSrcweir 2386*cdf0e10cSrcweir const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType ); 2387*cdf0e10cSrcweir 2388*cdf0e10cSrcweir // current vertex is equal to previous - just skip, 2389*cdf0e10cSrcweir // until we have a real edge 2390*cdf0e10cSrcweir if( bCurrNullVertex ) 2391*cdf0e10cSrcweir continue; 2392*cdf0e10cSrcweir 2393*cdf0e10cSrcweir // if previous edge has two identical points, because 2394*cdf0e10cSrcweir // no previous edge direction was available, simply 2395*cdf0e10cSrcweir // take this first non-null edge as the start 2396*cdf0e10cSrcweir // direction. That's what will happen here, if 2397*cdf0e10cSrcweir // bNullVertex is false 2398*cdf0e10cSrcweir if( !bNullVertex ) 2399*cdf0e10cSrcweir { 2400*cdf0e10cSrcweir // 2D cross product - is 1 for CW and -1 for CCW turns 2401*cdf0e10cSrcweir const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType - 2402*cdf0e10cSrcweir nVerticalEdgeType*nCurrHorizontalEdgeType ); 2403*cdf0e10cSrcweir 2404*cdf0e10cSrcweir if( !nCrossProduct ) 2405*cdf0e10cSrcweir continue; // no change in orientation - 2406*cdf0e10cSrcweir // collinear edges - just go on 2407*cdf0e10cSrcweir 2408*cdf0e10cSrcweir // if polygon orientation is not set, we'll 2409*cdf0e10cSrcweir // determine it now 2410*cdf0e10cSrcweir if( !bOrientationSet ) 2411*cdf0e10cSrcweir { 2412*cdf0e10cSrcweir bCWPolygon = nCrossProduct == 1; 2413*cdf0e10cSrcweir bOrientationSet = true; 2414*cdf0e10cSrcweir } 2415*cdf0e10cSrcweir else 2416*cdf0e10cSrcweir { 2417*cdf0e10cSrcweir // if current turn orientation is not equal 2418*cdf0e10cSrcweir // initial orientation, this is not a 2419*cdf0e10cSrcweir // rectangle (as rectangles have consistent 2420*cdf0e10cSrcweir // orientation). 2421*cdf0e10cSrcweir if( (nCrossProduct == 1) != bCWPolygon ) 2422*cdf0e10cSrcweir return false; 2423*cdf0e10cSrcweir } 2424*cdf0e10cSrcweir 2425*cdf0e10cSrcweir ++nNumTurns; 2426*cdf0e10cSrcweir 2427*cdf0e10cSrcweir // More than four 90 degree turns are an 2428*cdf0e10cSrcweir // indication that this must not be a rectangle. 2429*cdf0e10cSrcweir if( nNumTurns > 4 ) 2430*cdf0e10cSrcweir return false; 2431*cdf0e10cSrcweir } 2432*cdf0e10cSrcweir 2433*cdf0e10cSrcweir // store current state for the next turn 2434*cdf0e10cSrcweir nVerticalEdgeType = nCurrVerticalEdgeType; 2435*cdf0e10cSrcweir nHorizontalEdgeType = nCurrHorizontalEdgeType; 2436*cdf0e10cSrcweir bNullVertex = false; // won't reach this line, 2437*cdf0e10cSrcweir // if bCurrNullVertex is 2438*cdf0e10cSrcweir // true - see above 2439*cdf0e10cSrcweir } 2440*cdf0e10cSrcweir 2441*cdf0e10cSrcweir return true; 2442*cdf0e10cSrcweir } 2443*cdf0e10cSrcweir 2444*cdf0e10cSrcweir B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate) 2445*cdf0e10cSrcweir { 2446*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 2447*cdf0e10cSrcweir { 2448*cdf0e10cSrcweir // call myself recursively with subdivided input 2449*cdf0e10cSrcweir const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate)); 2450*cdf0e10cSrcweir return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate); 2451*cdf0e10cSrcweir } 2452*cdf0e10cSrcweir else 2453*cdf0e10cSrcweir { 2454*cdf0e10cSrcweir B3DPolygon aRetval; 2455*cdf0e10cSrcweir 2456*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < rCandidate.count(); a++) 2457*cdf0e10cSrcweir { 2458*cdf0e10cSrcweir B2DPoint aPoint(rCandidate.getB2DPoint(a)); 2459*cdf0e10cSrcweir aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate)); 2460*cdf0e10cSrcweir } 2461*cdf0e10cSrcweir 2462*cdf0e10cSrcweir // copy closed state 2463*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 2464*cdf0e10cSrcweir 2465*cdf0e10cSrcweir return aRetval; 2466*cdf0e10cSrcweir } 2467*cdf0e10cSrcweir } 2468*cdf0e10cSrcweir 2469*cdf0e10cSrcweir B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat) 2470*cdf0e10cSrcweir { 2471*cdf0e10cSrcweir B2DPolygon aRetval; 2472*cdf0e10cSrcweir const sal_uInt32 nCount(rCandidate.count()); 2473*cdf0e10cSrcweir const bool bIsIdentity(rMat.isIdentity()); 2474*cdf0e10cSrcweir 2475*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nCount; a++) 2476*cdf0e10cSrcweir { 2477*cdf0e10cSrcweir B3DPoint aCandidate(rCandidate.getB3DPoint(a)); 2478*cdf0e10cSrcweir 2479*cdf0e10cSrcweir if(!bIsIdentity) 2480*cdf0e10cSrcweir { 2481*cdf0e10cSrcweir aCandidate *= rMat; 2482*cdf0e10cSrcweir } 2483*cdf0e10cSrcweir 2484*cdf0e10cSrcweir aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY())); 2485*cdf0e10cSrcweir } 2486*cdf0e10cSrcweir 2487*cdf0e10cSrcweir // copy closed state 2488*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 2489*cdf0e10cSrcweir 2490*cdf0e10cSrcweir return aRetval; 2491*cdf0e10cSrcweir } 2492*cdf0e10cSrcweir 2493*cdf0e10cSrcweir double getDistancePointToEndlessRay(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut) 2494*cdf0e10cSrcweir { 2495*cdf0e10cSrcweir if(rPointA.equal(rPointB)) 2496*cdf0e10cSrcweir { 2497*cdf0e10cSrcweir rCut = 0.0; 2498*cdf0e10cSrcweir const B2DVector aVector(rTestPoint - rPointA); 2499*cdf0e10cSrcweir return aVector.getLength(); 2500*cdf0e10cSrcweir } 2501*cdf0e10cSrcweir else 2502*cdf0e10cSrcweir { 2503*cdf0e10cSrcweir // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint 2504*cdf0e10cSrcweir const B2DVector aVector1(rPointB - rPointA); 2505*cdf0e10cSrcweir const B2DVector aVector2(rTestPoint - rPointA); 2506*cdf0e10cSrcweir const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY())); 2507*cdf0e10cSrcweir const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY())); 2508*cdf0e10cSrcweir 2509*cdf0e10cSrcweir rCut = fDividend / fDivisor; 2510*cdf0e10cSrcweir 2511*cdf0e10cSrcweir const B2DPoint aCutPoint(rPointA + rCut * aVector1); 2512*cdf0e10cSrcweir const B2DVector aVector(rTestPoint - aCutPoint); 2513*cdf0e10cSrcweir return aVector.getLength(); 2514*cdf0e10cSrcweir } 2515*cdf0e10cSrcweir } 2516*cdf0e10cSrcweir 2517*cdf0e10cSrcweir double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut) 2518*cdf0e10cSrcweir { 2519*cdf0e10cSrcweir if(rPointA.equal(rPointB)) 2520*cdf0e10cSrcweir { 2521*cdf0e10cSrcweir rCut = 0.0; 2522*cdf0e10cSrcweir const B2DVector aVector(rTestPoint - rPointA); 2523*cdf0e10cSrcweir return aVector.getLength(); 2524*cdf0e10cSrcweir } 2525*cdf0e10cSrcweir else 2526*cdf0e10cSrcweir { 2527*cdf0e10cSrcweir // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint 2528*cdf0e10cSrcweir const B2DVector aVector1(rPointB - rPointA); 2529*cdf0e10cSrcweir const B2DVector aVector2(rTestPoint - rPointA); 2530*cdf0e10cSrcweir const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY())); 2531*cdf0e10cSrcweir const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY())); 2532*cdf0e10cSrcweir const double fCut(fDividend / fDivisor); 2533*cdf0e10cSrcweir 2534*cdf0e10cSrcweir if(fCut < 0.0) 2535*cdf0e10cSrcweir { 2536*cdf0e10cSrcweir // not in line range, get distance to PointA 2537*cdf0e10cSrcweir rCut = 0.0; 2538*cdf0e10cSrcweir return aVector2.getLength(); 2539*cdf0e10cSrcweir } 2540*cdf0e10cSrcweir else if(fCut > 1.0) 2541*cdf0e10cSrcweir { 2542*cdf0e10cSrcweir // not in line range, get distance to PointB 2543*cdf0e10cSrcweir rCut = 1.0; 2544*cdf0e10cSrcweir const B2DVector aVector(rTestPoint - rPointB); 2545*cdf0e10cSrcweir return aVector.getLength(); 2546*cdf0e10cSrcweir } 2547*cdf0e10cSrcweir else 2548*cdf0e10cSrcweir { 2549*cdf0e10cSrcweir // in line range 2550*cdf0e10cSrcweir const B2DPoint aCutPoint(rPointA + fCut * aVector1); 2551*cdf0e10cSrcweir const B2DVector aVector(rTestPoint - aCutPoint); 2552*cdf0e10cSrcweir rCut = fCut; 2553*cdf0e10cSrcweir return aVector.getLength(); 2554*cdf0e10cSrcweir } 2555*cdf0e10cSrcweir } 2556*cdf0e10cSrcweir } 2557*cdf0e10cSrcweir 2558*cdf0e10cSrcweir double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut) 2559*cdf0e10cSrcweir { 2560*cdf0e10cSrcweir double fRetval(DBL_MAX); 2561*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2562*cdf0e10cSrcweir 2563*cdf0e10cSrcweir if(nPointCount > 1L) 2564*cdf0e10cSrcweir { 2565*cdf0e10cSrcweir const double fZero(0.0); 2566*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 2567*cdf0e10cSrcweir B2DCubicBezier aBezier; 2568*cdf0e10cSrcweir aBezier.setStartPoint(rCandidate.getB2DPoint(0)); 2569*cdf0e10cSrcweir 2570*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 2571*cdf0e10cSrcweir { 2572*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 2573*cdf0e10cSrcweir aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 2574*cdf0e10cSrcweir double fEdgeDist; 2575*cdf0e10cSrcweir double fNewCut; 2576*cdf0e10cSrcweir bool bEdgeIsCurve(false); 2577*cdf0e10cSrcweir 2578*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 2579*cdf0e10cSrcweir { 2580*cdf0e10cSrcweir aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); 2581*cdf0e10cSrcweir aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 2582*cdf0e10cSrcweir aBezier.testAndSolveTrivialBezier(); 2583*cdf0e10cSrcweir bEdgeIsCurve = aBezier.isBezier(); 2584*cdf0e10cSrcweir } 2585*cdf0e10cSrcweir 2586*cdf0e10cSrcweir if(bEdgeIsCurve) 2587*cdf0e10cSrcweir { 2588*cdf0e10cSrcweir fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut); 2589*cdf0e10cSrcweir } 2590*cdf0e10cSrcweir else 2591*cdf0e10cSrcweir { 2592*cdf0e10cSrcweir fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut); 2593*cdf0e10cSrcweir } 2594*cdf0e10cSrcweir 2595*cdf0e10cSrcweir if(DBL_MAX == fRetval || fEdgeDist < fRetval) 2596*cdf0e10cSrcweir { 2597*cdf0e10cSrcweir fRetval = fEdgeDist; 2598*cdf0e10cSrcweir rEdgeIndex = a; 2599*cdf0e10cSrcweir rCut = fNewCut; 2600*cdf0e10cSrcweir 2601*cdf0e10cSrcweir if(fTools::equal(fRetval, fZero)) 2602*cdf0e10cSrcweir { 2603*cdf0e10cSrcweir // already found zero distance, cannot get better. Ensure numerical zero value and end loop. 2604*cdf0e10cSrcweir fRetval = 0.0; 2605*cdf0e10cSrcweir break; 2606*cdf0e10cSrcweir } 2607*cdf0e10cSrcweir } 2608*cdf0e10cSrcweir 2609*cdf0e10cSrcweir // prepare next step 2610*cdf0e10cSrcweir aBezier.setStartPoint(aBezier.getEndPoint()); 2611*cdf0e10cSrcweir } 2612*cdf0e10cSrcweir 2613*cdf0e10cSrcweir if(1.0 == rCut) 2614*cdf0e10cSrcweir { 2615*cdf0e10cSrcweir // correct rEdgeIndex when not last point 2616*cdf0e10cSrcweir if(rCandidate.isClosed()) 2617*cdf0e10cSrcweir { 2618*cdf0e10cSrcweir rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate); 2619*cdf0e10cSrcweir rCut = 0.0; 2620*cdf0e10cSrcweir } 2621*cdf0e10cSrcweir else 2622*cdf0e10cSrcweir { 2623*cdf0e10cSrcweir if(rEdgeIndex != nEdgeCount - 1L) 2624*cdf0e10cSrcweir { 2625*cdf0e10cSrcweir rEdgeIndex++; 2626*cdf0e10cSrcweir rCut = 0.0; 2627*cdf0e10cSrcweir } 2628*cdf0e10cSrcweir } 2629*cdf0e10cSrcweir } 2630*cdf0e10cSrcweir } 2631*cdf0e10cSrcweir 2632*cdf0e10cSrcweir return fRetval; 2633*cdf0e10cSrcweir } 2634*cdf0e10cSrcweir 2635*cdf0e10cSrcweir B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight) 2636*cdf0e10cSrcweir { 2637*cdf0e10cSrcweir if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight())) 2638*cdf0e10cSrcweir { 2639*cdf0e10cSrcweir return rCandidate; 2640*cdf0e10cSrcweir } 2641*cdf0e10cSrcweir else 2642*cdf0e10cSrcweir { 2643*cdf0e10cSrcweir const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth()); 2644*cdf0e10cSrcweir const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight()); 2645*cdf0e10cSrcweir const double fOneMinusRelativeX(1.0 - fRelativeX); 2646*cdf0e10cSrcweir const double fOneMinusRelativeY(1.0 - fRelativeY); 2647*cdf0e10cSrcweir const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) + 2648*cdf0e10cSrcweir fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX())); 2649*cdf0e10cSrcweir const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) + 2650*cdf0e10cSrcweir fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY())); 2651*cdf0e10cSrcweir 2652*cdf0e10cSrcweir return B2DPoint(fNewX, fNewY); 2653*cdf0e10cSrcweir } 2654*cdf0e10cSrcweir } 2655*cdf0e10cSrcweir 2656*cdf0e10cSrcweir B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight) 2657*cdf0e10cSrcweir { 2658*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2659*cdf0e10cSrcweir 2660*cdf0e10cSrcweir if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight()) 2661*cdf0e10cSrcweir { 2662*cdf0e10cSrcweir B2DPolygon aRetval; 2663*cdf0e10cSrcweir 2664*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 2665*cdf0e10cSrcweir { 2666*cdf0e10cSrcweir aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); 2667*cdf0e10cSrcweir 2668*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 2669*cdf0e10cSrcweir { 2670*cdf0e10cSrcweir if(!rCandidate.getPrevControlPoint(a).equalZero()) 2671*cdf0e10cSrcweir { 2672*cdf0e10cSrcweir aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); 2673*cdf0e10cSrcweir } 2674*cdf0e10cSrcweir 2675*cdf0e10cSrcweir if(!rCandidate.getNextControlPoint(a).equalZero()) 2676*cdf0e10cSrcweir { 2677*cdf0e10cSrcweir aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); 2678*cdf0e10cSrcweir } 2679*cdf0e10cSrcweir } 2680*cdf0e10cSrcweir } 2681*cdf0e10cSrcweir 2682*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 2683*cdf0e10cSrcweir return aRetval; 2684*cdf0e10cSrcweir } 2685*cdf0e10cSrcweir else 2686*cdf0e10cSrcweir { 2687*cdf0e10cSrcweir return rCandidate; 2688*cdf0e10cSrcweir } 2689*cdf0e10cSrcweir } 2690*cdf0e10cSrcweir 2691*cdf0e10cSrcweir B2DPolygon rotateAroundPoint(const B2DPolygon& rCandidate, const B2DPoint& rCenter, double fAngle) 2692*cdf0e10cSrcweir { 2693*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2694*cdf0e10cSrcweir B2DPolygon aRetval(rCandidate); 2695*cdf0e10cSrcweir 2696*cdf0e10cSrcweir if(nPointCount) 2697*cdf0e10cSrcweir { 2698*cdf0e10cSrcweir const B2DHomMatrix aMatrix(basegfx::tools::createRotateAroundPoint(rCenter, fAngle)); 2699*cdf0e10cSrcweir 2700*cdf0e10cSrcweir aRetval.transform(aMatrix); 2701*cdf0e10cSrcweir } 2702*cdf0e10cSrcweir 2703*cdf0e10cSrcweir return aRetval; 2704*cdf0e10cSrcweir } 2705*cdf0e10cSrcweir 2706*cdf0e10cSrcweir B2DPolygon expandToCurve(const B2DPolygon& rCandidate) 2707*cdf0e10cSrcweir { 2708*cdf0e10cSrcweir B2DPolygon aRetval(rCandidate); 2709*cdf0e10cSrcweir 2710*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < rCandidate.count(); a++) 2711*cdf0e10cSrcweir { 2712*cdf0e10cSrcweir expandToCurveInPoint(aRetval, a); 2713*cdf0e10cSrcweir } 2714*cdf0e10cSrcweir 2715*cdf0e10cSrcweir return aRetval; 2716*cdf0e10cSrcweir } 2717*cdf0e10cSrcweir 2718*cdf0e10cSrcweir bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex) 2719*cdf0e10cSrcweir { 2720*cdf0e10cSrcweir OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)"); 2721*cdf0e10cSrcweir bool bRetval(false); 2722*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2723*cdf0e10cSrcweir 2724*cdf0e10cSrcweir if(nPointCount) 2725*cdf0e10cSrcweir { 2726*cdf0e10cSrcweir // predecessor 2727*cdf0e10cSrcweir if(!rCandidate.isPrevControlPointUsed(nIndex)) 2728*cdf0e10cSrcweir { 2729*cdf0e10cSrcweir if(!rCandidate.isClosed() && 0 == nIndex) 2730*cdf0e10cSrcweir { 2731*cdf0e10cSrcweir // do not create previous vector for start point of open polygon 2732*cdf0e10cSrcweir } 2733*cdf0e10cSrcweir else 2734*cdf0e10cSrcweir { 2735*cdf0e10cSrcweir const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); 2736*cdf0e10cSrcweir rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0)); 2737*cdf0e10cSrcweir bRetval = true; 2738*cdf0e10cSrcweir } 2739*cdf0e10cSrcweir } 2740*cdf0e10cSrcweir 2741*cdf0e10cSrcweir // successor 2742*cdf0e10cSrcweir if(!rCandidate.isNextControlPointUsed(nIndex)) 2743*cdf0e10cSrcweir { 2744*cdf0e10cSrcweir if(!rCandidate.isClosed() && nIndex + 1 == nPointCount) 2745*cdf0e10cSrcweir { 2746*cdf0e10cSrcweir // do not create next vector for end point of open polygon 2747*cdf0e10cSrcweir } 2748*cdf0e10cSrcweir else 2749*cdf0e10cSrcweir { 2750*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 2751*cdf0e10cSrcweir rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0)); 2752*cdf0e10cSrcweir bRetval = true; 2753*cdf0e10cSrcweir } 2754*cdf0e10cSrcweir } 2755*cdf0e10cSrcweir } 2756*cdf0e10cSrcweir 2757*cdf0e10cSrcweir return bRetval; 2758*cdf0e10cSrcweir } 2759*cdf0e10cSrcweir 2760*cdf0e10cSrcweir B2DPolygon setContinuity(const B2DPolygon& rCandidate, B2VectorContinuity eContinuity) 2761*cdf0e10cSrcweir { 2762*cdf0e10cSrcweir B2DPolygon aRetval(rCandidate); 2763*cdf0e10cSrcweir 2764*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < rCandidate.count(); a++) 2765*cdf0e10cSrcweir { 2766*cdf0e10cSrcweir setContinuityInPoint(aRetval, a, eContinuity); 2767*cdf0e10cSrcweir } 2768*cdf0e10cSrcweir 2769*cdf0e10cSrcweir return aRetval; 2770*cdf0e10cSrcweir } 2771*cdf0e10cSrcweir 2772*cdf0e10cSrcweir bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity) 2773*cdf0e10cSrcweir { 2774*cdf0e10cSrcweir OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)"); 2775*cdf0e10cSrcweir bool bRetval(false); 2776*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2777*cdf0e10cSrcweir 2778*cdf0e10cSrcweir if(nPointCount) 2779*cdf0e10cSrcweir { 2780*cdf0e10cSrcweir const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex)); 2781*cdf0e10cSrcweir 2782*cdf0e10cSrcweir switch(eContinuity) 2783*cdf0e10cSrcweir { 2784*cdf0e10cSrcweir case CONTINUITY_NONE : 2785*cdf0e10cSrcweir { 2786*cdf0e10cSrcweir if(rCandidate.isPrevControlPointUsed(nIndex)) 2787*cdf0e10cSrcweir { 2788*cdf0e10cSrcweir if(!rCandidate.isClosed() && 0 == nIndex) 2789*cdf0e10cSrcweir { 2790*cdf0e10cSrcweir // remove existing previous vector for start point of open polygon 2791*cdf0e10cSrcweir rCandidate.resetPrevControlPoint(nIndex); 2792*cdf0e10cSrcweir } 2793*cdf0e10cSrcweir else 2794*cdf0e10cSrcweir { 2795*cdf0e10cSrcweir const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); 2796*cdf0e10cSrcweir rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0)); 2797*cdf0e10cSrcweir } 2798*cdf0e10cSrcweir 2799*cdf0e10cSrcweir bRetval = true; 2800*cdf0e10cSrcweir } 2801*cdf0e10cSrcweir 2802*cdf0e10cSrcweir if(rCandidate.isNextControlPointUsed(nIndex)) 2803*cdf0e10cSrcweir { 2804*cdf0e10cSrcweir if(!rCandidate.isClosed() && nIndex == nPointCount + 1) 2805*cdf0e10cSrcweir { 2806*cdf0e10cSrcweir // remove next vector for end point of open polygon 2807*cdf0e10cSrcweir rCandidate.resetNextControlPoint(nIndex); 2808*cdf0e10cSrcweir } 2809*cdf0e10cSrcweir else 2810*cdf0e10cSrcweir { 2811*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 2812*cdf0e10cSrcweir rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0)); 2813*cdf0e10cSrcweir } 2814*cdf0e10cSrcweir 2815*cdf0e10cSrcweir bRetval = true; 2816*cdf0e10cSrcweir } 2817*cdf0e10cSrcweir 2818*cdf0e10cSrcweir break; 2819*cdf0e10cSrcweir } 2820*cdf0e10cSrcweir case CONTINUITY_C1 : 2821*cdf0e10cSrcweir { 2822*cdf0e10cSrcweir if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex)) 2823*cdf0e10cSrcweir { 2824*cdf0e10cSrcweir // lengths both exist since both are used 2825*cdf0e10cSrcweir B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint); 2826*cdf0e10cSrcweir B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint); 2827*cdf0e10cSrcweir const double fLenPrev(aVectorPrev.getLength()); 2828*cdf0e10cSrcweir const double fLenNext(aVectorNext.getLength()); 2829*cdf0e10cSrcweir aVectorPrev.normalize(); 2830*cdf0e10cSrcweir aVectorNext.normalize(); 2831*cdf0e10cSrcweir const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); 2832*cdf0e10cSrcweir 2833*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0) 2834*cdf0e10cSrcweir { 2835*cdf0e10cSrcweir // parallel and opposite direction; check length 2836*cdf0e10cSrcweir if(fTools::equal(fLenPrev, fLenNext)) 2837*cdf0e10cSrcweir { 2838*cdf0e10cSrcweir // this would be even C2, but we want C1. Use the lengths of the corresponding edges. 2839*cdf0e10cSrcweir const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); 2840*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 2841*cdf0e10cSrcweir const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0)); 2842*cdf0e10cSrcweir const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0)); 2843*cdf0e10cSrcweir 2844*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2845*cdf0e10cSrcweir aCurrentPoint + (aVectorPrev * fLenPrevEdge), 2846*cdf0e10cSrcweir aCurrentPoint + (aVectorNext * fLenNextEdge)); 2847*cdf0e10cSrcweir bRetval = true; 2848*cdf0e10cSrcweir } 2849*cdf0e10cSrcweir } 2850*cdf0e10cSrcweir else 2851*cdf0e10cSrcweir { 2852*cdf0e10cSrcweir // not parallel or same direction, set vectors and length 2853*cdf0e10cSrcweir const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); 2854*cdf0e10cSrcweir 2855*cdf0e10cSrcweir if(ORIENTATION_POSITIVE == aOrientation) 2856*cdf0e10cSrcweir { 2857*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2858*cdf0e10cSrcweir aCurrentPoint - (aNormalizedPerpendicular * fLenPrev), 2859*cdf0e10cSrcweir aCurrentPoint + (aNormalizedPerpendicular * fLenNext)); 2860*cdf0e10cSrcweir } 2861*cdf0e10cSrcweir else 2862*cdf0e10cSrcweir { 2863*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2864*cdf0e10cSrcweir aCurrentPoint + (aNormalizedPerpendicular * fLenPrev), 2865*cdf0e10cSrcweir aCurrentPoint - (aNormalizedPerpendicular * fLenNext)); 2866*cdf0e10cSrcweir } 2867*cdf0e10cSrcweir 2868*cdf0e10cSrcweir bRetval = true; 2869*cdf0e10cSrcweir } 2870*cdf0e10cSrcweir } 2871*cdf0e10cSrcweir break; 2872*cdf0e10cSrcweir } 2873*cdf0e10cSrcweir case CONTINUITY_C2 : 2874*cdf0e10cSrcweir { 2875*cdf0e10cSrcweir if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex)) 2876*cdf0e10cSrcweir { 2877*cdf0e10cSrcweir // lengths both exist since both are used 2878*cdf0e10cSrcweir B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint); 2879*cdf0e10cSrcweir B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint); 2880*cdf0e10cSrcweir const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0); 2881*cdf0e10cSrcweir aVectorPrev.normalize(); 2882*cdf0e10cSrcweir aVectorNext.normalize(); 2883*cdf0e10cSrcweir const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); 2884*cdf0e10cSrcweir 2885*cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0) 2886*cdf0e10cSrcweir { 2887*cdf0e10cSrcweir // parallel and opposite direction; set length. Use one direction for better numerical correctness 2888*cdf0e10cSrcweir const B2DVector aScaledDirection(aVectorPrev * fCommonLength); 2889*cdf0e10cSrcweir 2890*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2891*cdf0e10cSrcweir aCurrentPoint + aScaledDirection, 2892*cdf0e10cSrcweir aCurrentPoint - aScaledDirection); 2893*cdf0e10cSrcweir } 2894*cdf0e10cSrcweir else 2895*cdf0e10cSrcweir { 2896*cdf0e10cSrcweir // not parallel or same direction, set vectors and length 2897*cdf0e10cSrcweir const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); 2898*cdf0e10cSrcweir const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength); 2899*cdf0e10cSrcweir 2900*cdf0e10cSrcweir if(ORIENTATION_POSITIVE == aOrientation) 2901*cdf0e10cSrcweir { 2902*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2903*cdf0e10cSrcweir aCurrentPoint - aPerpendicular, 2904*cdf0e10cSrcweir aCurrentPoint + aPerpendicular); 2905*cdf0e10cSrcweir } 2906*cdf0e10cSrcweir else 2907*cdf0e10cSrcweir { 2908*cdf0e10cSrcweir rCandidate.setControlPoints(nIndex, 2909*cdf0e10cSrcweir aCurrentPoint + aPerpendicular, 2910*cdf0e10cSrcweir aCurrentPoint - aPerpendicular); 2911*cdf0e10cSrcweir } 2912*cdf0e10cSrcweir } 2913*cdf0e10cSrcweir 2914*cdf0e10cSrcweir bRetval = true; 2915*cdf0e10cSrcweir } 2916*cdf0e10cSrcweir break; 2917*cdf0e10cSrcweir } 2918*cdf0e10cSrcweir } 2919*cdf0e10cSrcweir } 2920*cdf0e10cSrcweir 2921*cdf0e10cSrcweir return bRetval; 2922*cdf0e10cSrcweir } 2923*cdf0e10cSrcweir 2924*cdf0e10cSrcweir B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue) 2925*cdf0e10cSrcweir { 2926*cdf0e10cSrcweir if(0.0 != fValue) 2927*cdf0e10cSrcweir { 2928*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 2929*cdf0e10cSrcweir { 2930*cdf0e10cSrcweir // call myself recursively with subdivided input 2931*cdf0e10cSrcweir const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate)); 2932*cdf0e10cSrcweir return growInNormalDirection(aCandidate, fValue); 2933*cdf0e10cSrcweir } 2934*cdf0e10cSrcweir else 2935*cdf0e10cSrcweir { 2936*cdf0e10cSrcweir B2DPolygon aRetval; 2937*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2938*cdf0e10cSrcweir 2939*cdf0e10cSrcweir if(nPointCount) 2940*cdf0e10cSrcweir { 2941*cdf0e10cSrcweir B2DPoint aPrev(rCandidate.getB2DPoint(nPointCount - 1L)); 2942*cdf0e10cSrcweir B2DPoint aCurrent(rCandidate.getB2DPoint(0L)); 2943*cdf0e10cSrcweir 2944*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPointCount; a++) 2945*cdf0e10cSrcweir { 2946*cdf0e10cSrcweir const B2DPoint aNext(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L)); 2947*cdf0e10cSrcweir const B2DVector aBack(aPrev - aCurrent); 2948*cdf0e10cSrcweir const B2DVector aForw(aNext - aCurrent); 2949*cdf0e10cSrcweir const B2DVector aPerpBack(getNormalizedPerpendicular(aBack)); 2950*cdf0e10cSrcweir const B2DVector aPerpForw(getNormalizedPerpendicular(aForw)); 2951*cdf0e10cSrcweir B2DVector aDirection(aPerpBack - aPerpForw); 2952*cdf0e10cSrcweir aDirection.normalize(); 2953*cdf0e10cSrcweir aDirection *= fValue; 2954*cdf0e10cSrcweir aRetval.append(aCurrent + aDirection); 2955*cdf0e10cSrcweir 2956*cdf0e10cSrcweir // prepare next step 2957*cdf0e10cSrcweir aPrev = aCurrent; 2958*cdf0e10cSrcweir aCurrent = aNext; 2959*cdf0e10cSrcweir } 2960*cdf0e10cSrcweir } 2961*cdf0e10cSrcweir 2962*cdf0e10cSrcweir // copy closed state 2963*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 2964*cdf0e10cSrcweir 2965*cdf0e10cSrcweir return aRetval; 2966*cdf0e10cSrcweir } 2967*cdf0e10cSrcweir } 2968*cdf0e10cSrcweir else 2969*cdf0e10cSrcweir { 2970*cdf0e10cSrcweir return rCandidate; 2971*cdf0e10cSrcweir } 2972*cdf0e10cSrcweir } 2973*cdf0e10cSrcweir 2974*cdf0e10cSrcweir B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments) 2975*cdf0e10cSrcweir { 2976*cdf0e10cSrcweir B2DPolygon aRetval; 2977*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 2978*cdf0e10cSrcweir 2979*cdf0e10cSrcweir if(nPointCount && nSegments) 2980*cdf0e10cSrcweir { 2981*cdf0e10cSrcweir // get current segment count 2982*cdf0e10cSrcweir const sal_uInt32 nSegmentCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); 2983*cdf0e10cSrcweir 2984*cdf0e10cSrcweir if(nSegmentCount == nSegments) 2985*cdf0e10cSrcweir { 2986*cdf0e10cSrcweir aRetval = rCandidate; 2987*cdf0e10cSrcweir } 2988*cdf0e10cSrcweir else 2989*cdf0e10cSrcweir { 2990*cdf0e10cSrcweir const double fLength(getLength(rCandidate)); 2991*cdf0e10cSrcweir const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nSegments : nSegments + 1L); 2992*cdf0e10cSrcweir 2993*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nLoopCount; a++) 2994*cdf0e10cSrcweir { 2995*cdf0e10cSrcweir const double fRelativePos((double)a / (double)nSegments); // 0.0 .. 1.0 2996*cdf0e10cSrcweir const B2DPoint aNewPoint(getPositionRelative(rCandidate, fRelativePos, fLength)); 2997*cdf0e10cSrcweir aRetval.append(aNewPoint); 2998*cdf0e10cSrcweir } 2999*cdf0e10cSrcweir 3000*cdf0e10cSrcweir // copy closed flag 3001*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 3002*cdf0e10cSrcweir } 3003*cdf0e10cSrcweir } 3004*cdf0e10cSrcweir 3005*cdf0e10cSrcweir return aRetval; 3006*cdf0e10cSrcweir } 3007*cdf0e10cSrcweir 3008*cdf0e10cSrcweir B2DPolygon reSegmentPolygonEdges(const B2DPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges) 3009*cdf0e10cSrcweir { 3010*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 3011*cdf0e10cSrcweir 3012*cdf0e10cSrcweir if(nPointCount < 2 || nSubEdges < 2 || (!bHandleCurvedEdges && !bHandleStraightEdges)) 3013*cdf0e10cSrcweir { 3014*cdf0e10cSrcweir // nothing to do: 3015*cdf0e10cSrcweir // - less than two points -> no edge at all 3016*cdf0e10cSrcweir // - less than two nSubEdges -> no resegment necessary 3017*cdf0e10cSrcweir // - neither bHandleCurvedEdges nor bHandleStraightEdges -> nothing to do 3018*cdf0e10cSrcweir return rCandidate; 3019*cdf0e10cSrcweir } 3020*cdf0e10cSrcweir else 3021*cdf0e10cSrcweir { 3022*cdf0e10cSrcweir B2DPolygon aRetval; 3023*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 3024*cdf0e10cSrcweir B2DCubicBezier aCurrentEdge; 3025*cdf0e10cSrcweir 3026*cdf0e10cSrcweir // prepare first edge and add start point to target 3027*cdf0e10cSrcweir aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0)); 3028*cdf0e10cSrcweir aRetval.append(aCurrentEdge.getStartPoint()); 3029*cdf0e10cSrcweir 3030*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 3031*cdf0e10cSrcweir { 3032*cdf0e10cSrcweir // fill edge 3033*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 3034*cdf0e10cSrcweir aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a)); 3035*cdf0e10cSrcweir aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 3036*cdf0e10cSrcweir aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 3037*cdf0e10cSrcweir 3038*cdf0e10cSrcweir if(aCurrentEdge.isBezier()) 3039*cdf0e10cSrcweir { 3040*cdf0e10cSrcweir if(bHandleCurvedEdges) 3041*cdf0e10cSrcweir { 3042*cdf0e10cSrcweir for(sal_uInt32 b(nSubEdges); b > 1; b--) 3043*cdf0e10cSrcweir { 3044*cdf0e10cSrcweir const double fSplitPoint(1.0 / b); 3045*cdf0e10cSrcweir B2DCubicBezier aLeftPart; 3046*cdf0e10cSrcweir 3047*cdf0e10cSrcweir aCurrentEdge.split(fSplitPoint, &aLeftPart, &aCurrentEdge); 3048*cdf0e10cSrcweir aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), aLeftPart.getEndPoint()); 3049*cdf0e10cSrcweir } 3050*cdf0e10cSrcweir } 3051*cdf0e10cSrcweir 3052*cdf0e10cSrcweir // copy remaining segment to target 3053*cdf0e10cSrcweir aRetval.appendBezierSegment(aCurrentEdge.getControlPointA(), aCurrentEdge.getControlPointB(), aCurrentEdge.getEndPoint()); 3054*cdf0e10cSrcweir } 3055*cdf0e10cSrcweir else 3056*cdf0e10cSrcweir { 3057*cdf0e10cSrcweir if(bHandleStraightEdges) 3058*cdf0e10cSrcweir { 3059*cdf0e10cSrcweir for(sal_uInt32 b(nSubEdges); b > 1; b--) 3060*cdf0e10cSrcweir { 3061*cdf0e10cSrcweir const double fSplitPoint(1.0 / b); 3062*cdf0e10cSrcweir const B2DPoint aSplitPoint(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fSplitPoint)); 3063*cdf0e10cSrcweir 3064*cdf0e10cSrcweir aRetval.append(aSplitPoint); 3065*cdf0e10cSrcweir aCurrentEdge.setStartPoint(aSplitPoint); 3066*cdf0e10cSrcweir } 3067*cdf0e10cSrcweir } 3068*cdf0e10cSrcweir 3069*cdf0e10cSrcweir // copy remaining segment to target 3070*cdf0e10cSrcweir aRetval.append(aCurrentEdge.getEndPoint()); 3071*cdf0e10cSrcweir } 3072*cdf0e10cSrcweir 3073*cdf0e10cSrcweir // prepare next step 3074*cdf0e10cSrcweir aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint()); 3075*cdf0e10cSrcweir } 3076*cdf0e10cSrcweir 3077*cdf0e10cSrcweir // copy closed flag and return 3078*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 3079*cdf0e10cSrcweir return aRetval; 3080*cdf0e10cSrcweir } 3081*cdf0e10cSrcweir } 3082*cdf0e10cSrcweir 3083*cdf0e10cSrcweir B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t) 3084*cdf0e10cSrcweir { 3085*cdf0e10cSrcweir OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolygon interpolate: Different geometry (!)"); 3086*cdf0e10cSrcweir 3087*cdf0e10cSrcweir if(fTools::lessOrEqual(t, 0.0) || rOld1 == rOld2) 3088*cdf0e10cSrcweir { 3089*cdf0e10cSrcweir return rOld1; 3090*cdf0e10cSrcweir } 3091*cdf0e10cSrcweir else if(fTools::moreOrEqual(t, 1.0)) 3092*cdf0e10cSrcweir { 3093*cdf0e10cSrcweir return rOld2; 3094*cdf0e10cSrcweir } 3095*cdf0e10cSrcweir else 3096*cdf0e10cSrcweir { 3097*cdf0e10cSrcweir B2DPolygon aRetval; 3098*cdf0e10cSrcweir const bool bInterpolateVectors(rOld1.areControlPointsUsed() || rOld2.areControlPointsUsed()); 3099*cdf0e10cSrcweir aRetval.setClosed(rOld1.isClosed() && rOld2.isClosed()); 3100*cdf0e10cSrcweir 3101*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < rOld1.count(); a++) 3102*cdf0e10cSrcweir { 3103*cdf0e10cSrcweir aRetval.append(interpolate(rOld1.getB2DPoint(a), rOld2.getB2DPoint(a), t)); 3104*cdf0e10cSrcweir 3105*cdf0e10cSrcweir if(bInterpolateVectors) 3106*cdf0e10cSrcweir { 3107*cdf0e10cSrcweir aRetval.setPrevControlPoint(a, interpolate(rOld1.getPrevControlPoint(a), rOld2.getPrevControlPoint(a), t)); 3108*cdf0e10cSrcweir aRetval.setNextControlPoint(a, interpolate(rOld1.getNextControlPoint(a), rOld2.getNextControlPoint(a), t)); 3109*cdf0e10cSrcweir } 3110*cdf0e10cSrcweir } 3111*cdf0e10cSrcweir 3112*cdf0e10cSrcweir return aRetval; 3113*cdf0e10cSrcweir } 3114*cdf0e10cSrcweir } 3115*cdf0e10cSrcweir 3116*cdf0e10cSrcweir bool isPolyPolygonEqualRectangle( const B2DPolyPolygon& rPolyPoly, 3117*cdf0e10cSrcweir const B2DRange& rRect ) 3118*cdf0e10cSrcweir { 3119*cdf0e10cSrcweir // exclude some cheap cases first 3120*cdf0e10cSrcweir if( rPolyPoly.count() != 1 ) 3121*cdf0e10cSrcweir return false; 3122*cdf0e10cSrcweir 3123*cdf0e10cSrcweir // fill array with rectangle vertices 3124*cdf0e10cSrcweir const B2DPoint aPoints[] = 3125*cdf0e10cSrcweir { 3126*cdf0e10cSrcweir B2DPoint(rRect.getMinX(),rRect.getMinY()), 3127*cdf0e10cSrcweir B2DPoint(rRect.getMaxX(),rRect.getMinY()), 3128*cdf0e10cSrcweir B2DPoint(rRect.getMaxX(),rRect.getMaxY()), 3129*cdf0e10cSrcweir B2DPoint(rRect.getMinX(),rRect.getMaxY()) 3130*cdf0e10cSrcweir }; 3131*cdf0e10cSrcweir 3132*cdf0e10cSrcweir const B2DPolygon& rPoly( rPolyPoly.getB2DPolygon(0) ); 3133*cdf0e10cSrcweir const sal_uInt32 nCount( rPoly.count() ); 3134*cdf0e10cSrcweir const double epsilon = ::std::numeric_limits<double>::epsilon(); 3135*cdf0e10cSrcweir 3136*cdf0e10cSrcweir for(unsigned int j=0; j<4; ++j) 3137*cdf0e10cSrcweir { 3138*cdf0e10cSrcweir const B2DPoint &p1 = aPoints[j]; 3139*cdf0e10cSrcweir const B2DPoint &p2 = aPoints[(j+1)%4]; 3140*cdf0e10cSrcweir bool bPointOnBoundary = false; 3141*cdf0e10cSrcweir for( sal_uInt32 i=0; i<nCount; ++i ) 3142*cdf0e10cSrcweir { 3143*cdf0e10cSrcweir const B2DPoint p(rPoly.getB2DPoint(i)); 3144*cdf0e10cSrcweir 3145*cdf0e10cSrcweir // 1 | x0 y0 1 | 3146*cdf0e10cSrcweir // A = - | x1 y1 1 | 3147*cdf0e10cSrcweir // 2 | x2 y2 1 | 3148*cdf0e10cSrcweir double fDoubleArea = p2.getX()*p.getY() - 3149*cdf0e10cSrcweir p2.getY()*p.getX() - 3150*cdf0e10cSrcweir p1.getX()*p.getY() + 3151*cdf0e10cSrcweir p1.getY()*p.getX() + 3152*cdf0e10cSrcweir p1.getX()*p2.getY() - 3153*cdf0e10cSrcweir p1.getY()*p2.getX(); 3154*cdf0e10cSrcweir 3155*cdf0e10cSrcweir if(fDoubleArea < epsilon) 3156*cdf0e10cSrcweir { 3157*cdf0e10cSrcweir bPointOnBoundary=true; 3158*cdf0e10cSrcweir break; 3159*cdf0e10cSrcweir } 3160*cdf0e10cSrcweir } 3161*cdf0e10cSrcweir if(!(bPointOnBoundary)) 3162*cdf0e10cSrcweir return false; 3163*cdf0e10cSrcweir } 3164*cdf0e10cSrcweir 3165*cdf0e10cSrcweir return true; 3166*cdf0e10cSrcweir } 3167*cdf0e10cSrcweir 3168*cdf0e10cSrcweir 3169*cdf0e10cSrcweir // create simplified version of the original polygon by 3170*cdf0e10cSrcweir // replacing segments with spikes/loops and self intersections 3171*cdf0e10cSrcweir // by several trivial sub-segments 3172*cdf0e10cSrcweir B2DPolygon createSimplifiedPolygon( const B2DPolygon& rCandidate ) 3173*cdf0e10cSrcweir { 3174*cdf0e10cSrcweir const sal_uInt32 nCount(rCandidate.count()); 3175*cdf0e10cSrcweir 3176*cdf0e10cSrcweir if(nCount && rCandidate.areControlPointsUsed()) 3177*cdf0e10cSrcweir { 3178*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1); 3179*cdf0e10cSrcweir B2DPolygon aRetval; 3180*cdf0e10cSrcweir B2DCubicBezier aSegment; 3181*cdf0e10cSrcweir 3182*cdf0e10cSrcweir aSegment.setStartPoint(rCandidate.getB2DPoint(0)); 3183*cdf0e10cSrcweir aRetval.append(aSegment.getStartPoint()); 3184*cdf0e10cSrcweir 3185*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 3186*cdf0e10cSrcweir { 3187*cdf0e10cSrcweir // fill edge 3188*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nCount); 3189*cdf0e10cSrcweir aSegment.setControlPointA(rCandidate.getNextControlPoint(a)); 3190*cdf0e10cSrcweir aSegment.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 3191*cdf0e10cSrcweir aSegment.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 3192*cdf0e10cSrcweir 3193*cdf0e10cSrcweir if(aSegment.isBezier()) 3194*cdf0e10cSrcweir { 3195*cdf0e10cSrcweir double fExtremumPos(0.0); 3196*cdf0e10cSrcweir sal_uInt32 nExtremumCounter(4); 3197*cdf0e10cSrcweir 3198*cdf0e10cSrcweir while(nExtremumCounter-- && aSegment.isBezier() && aSegment.getMinimumExtremumPosition(fExtremumPos)) 3199*cdf0e10cSrcweir { 3200*cdf0e10cSrcweir // split off left, now extremum-free part and append 3201*cdf0e10cSrcweir B2DCubicBezier aLeft; 3202*cdf0e10cSrcweir 3203*cdf0e10cSrcweir aSegment.split(fExtremumPos, &aLeft, &aSegment); 3204*cdf0e10cSrcweir aLeft.testAndSolveTrivialBezier(); 3205*cdf0e10cSrcweir aSegment.testAndSolveTrivialBezier(); 3206*cdf0e10cSrcweir 3207*cdf0e10cSrcweir if(aLeft.isBezier()) 3208*cdf0e10cSrcweir { 3209*cdf0e10cSrcweir aRetval.appendBezierSegment(aLeft.getControlPointA(), aLeft.getControlPointB(), aLeft.getEndPoint()); 3210*cdf0e10cSrcweir } 3211*cdf0e10cSrcweir else 3212*cdf0e10cSrcweir { 3213*cdf0e10cSrcweir aRetval.append(aLeft.getEndPoint()); 3214*cdf0e10cSrcweir } 3215*cdf0e10cSrcweir } 3216*cdf0e10cSrcweir 3217*cdf0e10cSrcweir // append (evtl. reduced) rest of Segment 3218*cdf0e10cSrcweir if(aSegment.isBezier()) 3219*cdf0e10cSrcweir { 3220*cdf0e10cSrcweir aRetval.appendBezierSegment(aSegment.getControlPointA(), aSegment.getControlPointB(), aSegment.getEndPoint()); 3221*cdf0e10cSrcweir } 3222*cdf0e10cSrcweir else 3223*cdf0e10cSrcweir { 3224*cdf0e10cSrcweir aRetval.append(aSegment.getEndPoint()); 3225*cdf0e10cSrcweir } 3226*cdf0e10cSrcweir } 3227*cdf0e10cSrcweir else 3228*cdf0e10cSrcweir { 3229*cdf0e10cSrcweir // simple edge, append end point 3230*cdf0e10cSrcweir aRetval.append(aSegment.getEndPoint()); 3231*cdf0e10cSrcweir } 3232*cdf0e10cSrcweir 3233*cdf0e10cSrcweir // prepare next edge 3234*cdf0e10cSrcweir aSegment.setStartPoint(aSegment.getEndPoint()); 3235*cdf0e10cSrcweir } 3236*cdf0e10cSrcweir 3237*cdf0e10cSrcweir // copy closed flag and check for double points 3238*cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 3239*cdf0e10cSrcweir aRetval.removeDoublePoints(); 3240*cdf0e10cSrcweir 3241*cdf0e10cSrcweir return aRetval; 3242*cdf0e10cSrcweir } 3243*cdf0e10cSrcweir else 3244*cdf0e10cSrcweir { 3245*cdf0e10cSrcweir return rCandidate; 3246*cdf0e10cSrcweir } 3247*cdf0e10cSrcweir } 3248*cdf0e10cSrcweir 3249*cdf0e10cSrcweir // #i76891# 3250*cdf0e10cSrcweir B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate) 3251*cdf0e10cSrcweir { 3252*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 3253*cdf0e10cSrcweir 3254*cdf0e10cSrcweir if(nPointCount && rCandidate.areControlPointsUsed()) 3255*cdf0e10cSrcweir { 3256*cdf0e10cSrcweir // prepare loop 3257*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 3258*cdf0e10cSrcweir B2DPolygon aRetval; 3259*cdf0e10cSrcweir B2DCubicBezier aBezier; 3260*cdf0e10cSrcweir aBezier.setStartPoint(rCandidate.getB2DPoint(0)); 3261*cdf0e10cSrcweir 3262*cdf0e10cSrcweir // try to avoid costly reallocations 3263*cdf0e10cSrcweir aRetval.reserve( nEdgeCount+1); 3264*cdf0e10cSrcweir 3265*cdf0e10cSrcweir // add start point 3266*cdf0e10cSrcweir aRetval.append(aBezier.getStartPoint()); 3267*cdf0e10cSrcweir 3268*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 3269*cdf0e10cSrcweir { 3270*cdf0e10cSrcweir // get values for edge 3271*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 3272*cdf0e10cSrcweir aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 3273*cdf0e10cSrcweir aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); 3274*cdf0e10cSrcweir aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 3275*cdf0e10cSrcweir aBezier.testAndSolveTrivialBezier(); 3276*cdf0e10cSrcweir 3277*cdf0e10cSrcweir // still bezier? 3278*cdf0e10cSrcweir if(aBezier.isBezier()) 3279*cdf0e10cSrcweir { 3280*cdf0e10cSrcweir // add edge with control vectors 3281*cdf0e10cSrcweir aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint()); 3282*cdf0e10cSrcweir } 3283*cdf0e10cSrcweir else 3284*cdf0e10cSrcweir { 3285*cdf0e10cSrcweir // add edge 3286*cdf0e10cSrcweir aRetval.append(aBezier.getEndPoint()); 3287*cdf0e10cSrcweir } 3288*cdf0e10cSrcweir 3289*cdf0e10cSrcweir // next point 3290*cdf0e10cSrcweir aBezier.setStartPoint(aBezier.getEndPoint()); 3291*cdf0e10cSrcweir } 3292*cdf0e10cSrcweir 3293*cdf0e10cSrcweir if(rCandidate.isClosed()) 3294*cdf0e10cSrcweir { 3295*cdf0e10cSrcweir // set closed flag, rescue control point and correct last double point 3296*cdf0e10cSrcweir closeWithGeometryChange(aRetval); 3297*cdf0e10cSrcweir } 3298*cdf0e10cSrcweir 3299*cdf0e10cSrcweir return aRetval; 3300*cdf0e10cSrcweir } 3301*cdf0e10cSrcweir else 3302*cdf0e10cSrcweir { 3303*cdf0e10cSrcweir return rCandidate; 3304*cdf0e10cSrcweir } 3305*cdf0e10cSrcweir } 3306*cdf0e10cSrcweir 3307*cdf0e10cSrcweir // makes the given indexed point the new polygon start point. To do that, the points in the 3308*cdf0e10cSrcweir // polygon will be rotated. This is only valid for closed polygons, for non-closed ones 3309*cdf0e10cSrcweir // an assertion will be triggered 3310*cdf0e10cSrcweir B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint) 3311*cdf0e10cSrcweir { 3312*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 3313*cdf0e10cSrcweir 3314*cdf0e10cSrcweir if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount) 3315*cdf0e10cSrcweir { 3316*cdf0e10cSrcweir OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)"); 3317*cdf0e10cSrcweir B2DPolygon aRetval; 3318*cdf0e10cSrcweir 3319*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nPointCount; a++) 3320*cdf0e10cSrcweir { 3321*cdf0e10cSrcweir const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount); 3322*cdf0e10cSrcweir aRetval.append(rCandidate.getB2DPoint(nSourceIndex)); 3323*cdf0e10cSrcweir 3324*cdf0e10cSrcweir if(rCandidate.areControlPointsUsed()) 3325*cdf0e10cSrcweir { 3326*cdf0e10cSrcweir aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex)); 3327*cdf0e10cSrcweir aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex)); 3328*cdf0e10cSrcweir } 3329*cdf0e10cSrcweir } 3330*cdf0e10cSrcweir 3331*cdf0e10cSrcweir return aRetval; 3332*cdf0e10cSrcweir } 3333*cdf0e10cSrcweir 3334*cdf0e10cSrcweir return rCandidate; 3335*cdf0e10cSrcweir } 3336*cdf0e10cSrcweir 3337*cdf0e10cSrcweir B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart, double fEnd) 3338*cdf0e10cSrcweir { 3339*cdf0e10cSrcweir B2DPolygon aRetval; 3340*cdf0e10cSrcweir 3341*cdf0e10cSrcweir if(fLength < 0.0) 3342*cdf0e10cSrcweir { 3343*cdf0e10cSrcweir fLength = 0.0; 3344*cdf0e10cSrcweir } 3345*cdf0e10cSrcweir 3346*cdf0e10cSrcweir if(!fTools::equalZero(fLength)) 3347*cdf0e10cSrcweir { 3348*cdf0e10cSrcweir if(fStart < 0.0) 3349*cdf0e10cSrcweir { 3350*cdf0e10cSrcweir fStart = 0.0; 3351*cdf0e10cSrcweir } 3352*cdf0e10cSrcweir 3353*cdf0e10cSrcweir if(fEnd < 0.0) 3354*cdf0e10cSrcweir { 3355*cdf0e10cSrcweir fEnd = 0.0; 3356*cdf0e10cSrcweir } 3357*cdf0e10cSrcweir 3358*cdf0e10cSrcweir if(fEnd < fStart) 3359*cdf0e10cSrcweir { 3360*cdf0e10cSrcweir fEnd = fStart; 3361*cdf0e10cSrcweir } 3362*cdf0e10cSrcweir 3363*cdf0e10cSrcweir // iterate and consume pieces with fLength. First subdivide to reduce input to line segments 3364*cdf0e10cSrcweir const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate); 3365*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 3366*cdf0e10cSrcweir 3367*cdf0e10cSrcweir if(nPointCount > 1) 3368*cdf0e10cSrcweir { 3369*cdf0e10cSrcweir const bool bEndActive(!fTools::equalZero(fEnd)); 3370*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1); 3371*cdf0e10cSrcweir B2DPoint aCurrent(aCandidate.getB2DPoint(0)); 3372*cdf0e10cSrcweir double fPositionInEdge(fStart); 3373*cdf0e10cSrcweir double fAbsolutePosition(fStart); 3374*cdf0e10cSrcweir 3375*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 3376*cdf0e10cSrcweir { 3377*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 3378*cdf0e10cSrcweir const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex)); 3379*cdf0e10cSrcweir const B2DVector aEdge(aNext - aCurrent); 3380*cdf0e10cSrcweir double fEdgeLength(aEdge.getLength()); 3381*cdf0e10cSrcweir 3382*cdf0e10cSrcweir if(!fTools::equalZero(fEdgeLength)) 3383*cdf0e10cSrcweir { 3384*cdf0e10cSrcweir while(fTools::less(fPositionInEdge, fEdgeLength)) 3385*cdf0e10cSrcweir { 3386*cdf0e10cSrcweir // move position on edge forward as long as on edge 3387*cdf0e10cSrcweir const double fScalar(fPositionInEdge / fEdgeLength); 3388*cdf0e10cSrcweir aRetval.append(aCurrent + (aEdge * fScalar)); 3389*cdf0e10cSrcweir fPositionInEdge += fLength; 3390*cdf0e10cSrcweir 3391*cdf0e10cSrcweir if(bEndActive) 3392*cdf0e10cSrcweir { 3393*cdf0e10cSrcweir fAbsolutePosition += fLength; 3394*cdf0e10cSrcweir 3395*cdf0e10cSrcweir if(fTools::more(fAbsolutePosition, fEnd)) 3396*cdf0e10cSrcweir { 3397*cdf0e10cSrcweir break; 3398*cdf0e10cSrcweir } 3399*cdf0e10cSrcweir } 3400*cdf0e10cSrcweir } 3401*cdf0e10cSrcweir 3402*cdf0e10cSrcweir // substract length of current edge 3403*cdf0e10cSrcweir fPositionInEdge -= fEdgeLength; 3404*cdf0e10cSrcweir } 3405*cdf0e10cSrcweir 3406*cdf0e10cSrcweir if(bEndActive && fTools::more(fAbsolutePosition, fEnd)) 3407*cdf0e10cSrcweir { 3408*cdf0e10cSrcweir break; 3409*cdf0e10cSrcweir } 3410*cdf0e10cSrcweir 3411*cdf0e10cSrcweir // prepare next step 3412*cdf0e10cSrcweir aCurrent = aNext; 3413*cdf0e10cSrcweir } 3414*cdf0e10cSrcweir 3415*cdf0e10cSrcweir // keep closed state 3416*cdf0e10cSrcweir aRetval.setClosed(aCandidate.isClosed()); 3417*cdf0e10cSrcweir } 3418*cdf0e10cSrcweir else 3419*cdf0e10cSrcweir { 3420*cdf0e10cSrcweir // source polygon has only one point, return unchanged 3421*cdf0e10cSrcweir aRetval = aCandidate; 3422*cdf0e10cSrcweir } 3423*cdf0e10cSrcweir } 3424*cdf0e10cSrcweir 3425*cdf0e10cSrcweir return aRetval; 3426*cdf0e10cSrcweir } 3427*cdf0e10cSrcweir 3428*cdf0e10cSrcweir B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight) 3429*cdf0e10cSrcweir { 3430*cdf0e10cSrcweir B2DPolygon aRetval; 3431*cdf0e10cSrcweir 3432*cdf0e10cSrcweir if(fWaveWidth < 0.0) 3433*cdf0e10cSrcweir { 3434*cdf0e10cSrcweir fWaveWidth = 0.0; 3435*cdf0e10cSrcweir } 3436*cdf0e10cSrcweir 3437*cdf0e10cSrcweir if(fWaveHeight < 0.0) 3438*cdf0e10cSrcweir { 3439*cdf0e10cSrcweir fWaveHeight = 0.0; 3440*cdf0e10cSrcweir } 3441*cdf0e10cSrcweir 3442*cdf0e10cSrcweir const bool bHasWidth(!fTools::equalZero(fWaveWidth)); 3443*cdf0e10cSrcweir const bool bHasHeight(!fTools::equalZero(fWaveHeight)); 3444*cdf0e10cSrcweir 3445*cdf0e10cSrcweir if(bHasWidth) 3446*cdf0e10cSrcweir { 3447*cdf0e10cSrcweir if(bHasHeight) 3448*cdf0e10cSrcweir { 3449*cdf0e10cSrcweir // width and height, create waveline. First subdivide to reduce input to line segments 3450*cdf0e10cSrcweir // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it 3451*cdf0e10cSrcweir // may be added here again using the original last point from rCandidate. It may 3452*cdf0e10cSrcweir // also be the case that rCandidate was closed. To simplify things it is handled here 3453*cdf0e10cSrcweir // as if it was opened. 3454*cdf0e10cSrcweir // Result from createEdgesOfGivenLength contains no curved segments, handle as straight 3455*cdf0e10cSrcweir // edges. 3456*cdf0e10cSrcweir const B2DPolygon aEqualLenghEdges(createEdgesOfGivenLength(rCandidate, fWaveWidth)); 3457*cdf0e10cSrcweir const sal_uInt32 nPointCount(aEqualLenghEdges.count()); 3458*cdf0e10cSrcweir 3459*cdf0e10cSrcweir if(nPointCount > 1) 3460*cdf0e10cSrcweir { 3461*cdf0e10cSrcweir // iterate over straight edges, add start point 3462*cdf0e10cSrcweir B2DPoint aCurrent(aEqualLenghEdges.getB2DPoint(0)); 3463*cdf0e10cSrcweir aRetval.append(aCurrent); 3464*cdf0e10cSrcweir 3465*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nPointCount - 1; a++) 3466*cdf0e10cSrcweir { 3467*cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 3468*cdf0e10cSrcweir const B2DPoint aNext(aEqualLenghEdges.getB2DPoint(nNextIndex)); 3469*cdf0e10cSrcweir const B2DVector aEdge(aNext - aCurrent); 3470*cdf0e10cSrcweir const B2DVector aPerpendicular(getNormalizedPerpendicular(aEdge)); 3471*cdf0e10cSrcweir const B2DVector aControlOffset((aEdge * 0.467308) - (aPerpendicular * fWaveHeight)); 3472*cdf0e10cSrcweir 3473*cdf0e10cSrcweir // add curve segment 3474*cdf0e10cSrcweir aRetval.appendBezierSegment( 3475*cdf0e10cSrcweir aCurrent + aControlOffset, 3476*cdf0e10cSrcweir aNext - aControlOffset, 3477*cdf0e10cSrcweir aNext); 3478*cdf0e10cSrcweir 3479*cdf0e10cSrcweir // prepare next step 3480*cdf0e10cSrcweir aCurrent = aNext; 3481*cdf0e10cSrcweir } 3482*cdf0e10cSrcweir } 3483*cdf0e10cSrcweir } 3484*cdf0e10cSrcweir else 3485*cdf0e10cSrcweir { 3486*cdf0e10cSrcweir // width but no height -> return original polygon 3487*cdf0e10cSrcweir aRetval = rCandidate; 3488*cdf0e10cSrcweir } 3489*cdf0e10cSrcweir } 3490*cdf0e10cSrcweir else 3491*cdf0e10cSrcweir { 3492*cdf0e10cSrcweir // no width -> no waveline, stay empty and return 3493*cdf0e10cSrcweir } 3494*cdf0e10cSrcweir 3495*cdf0e10cSrcweir return aRetval; 3496*cdf0e10cSrcweir } 3497*cdf0e10cSrcweir 3498*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////// 3499*cdf0e10cSrcweir // comparators with tolerance for 2D Polygons 3500*cdf0e10cSrcweir 3501*cdf0e10cSrcweir bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, const double& rfSmallValue) 3502*cdf0e10cSrcweir { 3503*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidateA.count()); 3504*cdf0e10cSrcweir 3505*cdf0e10cSrcweir if(nPointCount != rCandidateB.count()) 3506*cdf0e10cSrcweir return false; 3507*cdf0e10cSrcweir 3508*cdf0e10cSrcweir const bool bClosed(rCandidateA.isClosed()); 3509*cdf0e10cSrcweir 3510*cdf0e10cSrcweir if(bClosed != rCandidateB.isClosed()) 3511*cdf0e10cSrcweir return false; 3512*cdf0e10cSrcweir 3513*cdf0e10cSrcweir const bool bAreControlPointsUsed(rCandidateA.areControlPointsUsed()); 3514*cdf0e10cSrcweir 3515*cdf0e10cSrcweir if(bAreControlPointsUsed != rCandidateB.areControlPointsUsed()) 3516*cdf0e10cSrcweir return false; 3517*cdf0e10cSrcweir 3518*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nPointCount; a++) 3519*cdf0e10cSrcweir { 3520*cdf0e10cSrcweir const B2DPoint aPoint(rCandidateA.getB2DPoint(a)); 3521*cdf0e10cSrcweir 3522*cdf0e10cSrcweir if(!aPoint.equal(rCandidateB.getB2DPoint(a), rfSmallValue)) 3523*cdf0e10cSrcweir return false; 3524*cdf0e10cSrcweir 3525*cdf0e10cSrcweir if(bAreControlPointsUsed) 3526*cdf0e10cSrcweir { 3527*cdf0e10cSrcweir const basegfx::B2DPoint aPrev(rCandidateA.getPrevControlPoint(a)); 3528*cdf0e10cSrcweir 3529*cdf0e10cSrcweir if(!aPrev.equal(rCandidateB.getPrevControlPoint(a), rfSmallValue)) 3530*cdf0e10cSrcweir return false; 3531*cdf0e10cSrcweir 3532*cdf0e10cSrcweir const basegfx::B2DPoint aNext(rCandidateA.getNextControlPoint(a)); 3533*cdf0e10cSrcweir 3534*cdf0e10cSrcweir if(!aNext.equal(rCandidateB.getNextControlPoint(a), rfSmallValue)) 3535*cdf0e10cSrcweir return false; 3536*cdf0e10cSrcweir } 3537*cdf0e10cSrcweir } 3538*cdf0e10cSrcweir 3539*cdf0e10cSrcweir return true; 3540*cdf0e10cSrcweir } 3541*cdf0e10cSrcweir 3542*cdf0e10cSrcweir bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB) 3543*cdf0e10cSrcweir { 3544*cdf0e10cSrcweir const double fSmallValue(fTools::getSmallValue()); 3545*cdf0e10cSrcweir 3546*cdf0e10cSrcweir return equal(rCandidateA, rCandidateB, fSmallValue); 3547*cdf0e10cSrcweir } 3548*cdf0e10cSrcweir 3549*cdf0e10cSrcweir // snap points of horizontal or vertical edges to discrete values 3550*cdf0e10cSrcweir B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate) 3551*cdf0e10cSrcweir { 3552*cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 3553*cdf0e10cSrcweir 3554*cdf0e10cSrcweir if(nPointCount > 1) 3555*cdf0e10cSrcweir { 3556*cdf0e10cSrcweir // Start by copying the source polygon to get a writeable copy. The closed state is 3557*cdf0e10cSrcweir // copied by aRetval's initialisation, too, so no need to copy it in this method 3558*cdf0e10cSrcweir B2DPolygon aRetval(rCandidate); 3559*cdf0e10cSrcweir 3560*cdf0e10cSrcweir // prepare geometry data. Get rounded from original 3561*cdf0e10cSrcweir B2ITuple aPrevTuple(basegfx::fround(rCandidate.getB2DPoint(nPointCount - 1))); 3562*cdf0e10cSrcweir B2DPoint aCurrPoint(rCandidate.getB2DPoint(0)); 3563*cdf0e10cSrcweir B2ITuple aCurrTuple(basegfx::fround(aCurrPoint)); 3564*cdf0e10cSrcweir 3565*cdf0e10cSrcweir // loop over all points. This will also snap the implicit closing edge 3566*cdf0e10cSrcweir // even when not closed, but that's no problem here 3567*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nPointCount; a++) 3568*cdf0e10cSrcweir { 3569*cdf0e10cSrcweir // get next point. Get rounded from original 3570*cdf0e10cSrcweir const bool bLastRun(a + 1 == nPointCount); 3571*cdf0e10cSrcweir const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1); 3572*cdf0e10cSrcweir const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex)); 3573*cdf0e10cSrcweir const B2ITuple aNextTuple(basegfx::fround(aNextPoint)); 3574*cdf0e10cSrcweir 3575*cdf0e10cSrcweir // get the states 3576*cdf0e10cSrcweir const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX()); 3577*cdf0e10cSrcweir const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX()); 3578*cdf0e10cSrcweir const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY()); 3579*cdf0e10cSrcweir const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY()); 3580*cdf0e10cSrcweir const bool bSnapX(bPrevVertical || bNextVertical); 3581*cdf0e10cSrcweir const bool bSnapY(bPrevHorizontal || bNextHorizontal); 3582*cdf0e10cSrcweir 3583*cdf0e10cSrcweir if(bSnapX || bSnapY) 3584*cdf0e10cSrcweir { 3585*cdf0e10cSrcweir const B2DPoint aSnappedPoint( 3586*cdf0e10cSrcweir bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(), 3587*cdf0e10cSrcweir bSnapY ? aCurrTuple.getY() : aCurrPoint.getY()); 3588*cdf0e10cSrcweir 3589*cdf0e10cSrcweir aRetval.setB2DPoint(a, aSnappedPoint); 3590*cdf0e10cSrcweir } 3591*cdf0e10cSrcweir 3592*cdf0e10cSrcweir // prepare next point 3593*cdf0e10cSrcweir if(!bLastRun) 3594*cdf0e10cSrcweir { 3595*cdf0e10cSrcweir aPrevTuple = aCurrTuple; 3596*cdf0e10cSrcweir aCurrPoint = aNextPoint; 3597*cdf0e10cSrcweir aCurrTuple = aNextTuple; 3598*cdf0e10cSrcweir } 3599*cdf0e10cSrcweir } 3600*cdf0e10cSrcweir 3601*cdf0e10cSrcweir return aRetval; 3602*cdf0e10cSrcweir } 3603*cdf0e10cSrcweir else 3604*cdf0e10cSrcweir { 3605*cdf0e10cSrcweir return rCandidate; 3606*cdf0e10cSrcweir } 3607*cdf0e10cSrcweir } 3608*cdf0e10cSrcweir 3609*cdf0e10cSrcweir } // end of namespace tools 3610*cdf0e10cSrcweir } // end of namespace basegfx 3611*cdf0e10cSrcweir 3612*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 3613*cdf0e10cSrcweir // eof 3614