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