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