109dbbe93SAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 309dbbe93SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 409dbbe93SAndrew Rist * or more contributor license agreements. See the NOTICE file 509dbbe93SAndrew Rist * distributed with this work for additional information 609dbbe93SAndrew Rist * regarding copyright ownership. The ASF licenses this file 709dbbe93SAndrew Rist * to you under the Apache License, Version 2.0 (the 809dbbe93SAndrew Rist * "License"); you may not use this file except in compliance 909dbbe93SAndrew Rist * with the License. You may obtain a copy of the License at 10cdf0e10cSrcweir * 1109dbbe93SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12cdf0e10cSrcweir * 1309dbbe93SAndrew Rist * Unless required by applicable law or agreed to in writing, 1409dbbe93SAndrew Rist * software distributed under the License is distributed on an 1509dbbe93SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1609dbbe93SAndrew Rist * KIND, either express or implied. See the License for the 1709dbbe93SAndrew Rist * specific language governing permissions and limitations 1809dbbe93SAndrew Rist * under the License. 19cdf0e10cSrcweir * 2009dbbe93SAndrew Rist *************************************************************/ 2109dbbe93SAndrew Rist 22cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 23cdf0e10cSrcweir #include "precompiled_basegfx.hxx" 24cdf0e10cSrcweir #include <cstdio> 25cdf0e10cSrcweir #include <osl/diagnose.h> 26cdf0e10cSrcweir #include <basegfx/polygon/b2dlinegeometry.hxx> 27cdf0e10cSrcweir #include <basegfx/point/b2dpoint.hxx> 28cdf0e10cSrcweir #include <basegfx/vector/b2dvector.hxx> 29cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 30cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx> 31cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx> 32cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 33cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx> 34cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 355aaf853bSArmin Le Grand #include <com/sun/star/drawing/LineCap.hpp> 36c514522bSArmin Le Grand #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 37cdf0e10cSrcweir 38cdf0e10cSrcweir namespace basegfx 39cdf0e10cSrcweir { 40cdf0e10cSrcweir namespace tools 41cdf0e10cSrcweir { createAreaGeometryForLineStartEnd(const B2DPolygon & rCandidate,const B2DPolyPolygon & rArrow,bool bStart,double fWidth,double fCandidateLength,double fDockingPosition,double * pConsumedLength)42cdf0e10cSrcweir B2DPolyPolygon createAreaGeometryForLineStartEnd( 43cdf0e10cSrcweir const B2DPolygon& rCandidate, 44cdf0e10cSrcweir const B2DPolyPolygon& rArrow, 45cdf0e10cSrcweir bool bStart, 46cdf0e10cSrcweir double fWidth, 47cdf0e10cSrcweir double fCandidateLength, 48cdf0e10cSrcweir double fDockingPosition, // 0->top, 1->bottom 49cdf0e10cSrcweir double* pConsumedLength) 50cdf0e10cSrcweir { 51cdf0e10cSrcweir B2DPolyPolygon aRetval; 52cdf0e10cSrcweir OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)"); 53cdf0e10cSrcweir OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)"); 54cdf0e10cSrcweir OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)"); 55cdf0e10cSrcweir OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0, 56cdf0e10cSrcweir "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)"); 57cdf0e10cSrcweir 58cdf0e10cSrcweir if(fWidth < 0.0) 59cdf0e10cSrcweir { 60cdf0e10cSrcweir fWidth = -fWidth; 61cdf0e10cSrcweir } 62cdf0e10cSrcweir 63cdf0e10cSrcweir if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth)) 64cdf0e10cSrcweir { 65cdf0e10cSrcweir if(fDockingPosition < 0.0) 66cdf0e10cSrcweir { 67cdf0e10cSrcweir fDockingPosition = 0.0; 68cdf0e10cSrcweir } 69cdf0e10cSrcweir else if(fDockingPosition > 1.0) 70cdf0e10cSrcweir { 71cdf0e10cSrcweir fDockingPosition = 1.0; 72cdf0e10cSrcweir } 73cdf0e10cSrcweir 74cdf0e10cSrcweir // init return value from arrow 75cdf0e10cSrcweir aRetval.append(rArrow); 76cdf0e10cSrcweir 77cdf0e10cSrcweir // get size of the arrow 78cdf0e10cSrcweir const B2DRange aArrowSize(getRange(rArrow)); 79cdf0e10cSrcweir 80cdf0e10cSrcweir // build ArrowTransform; center in X, align with axis in Y 81cdf0e10cSrcweir B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix( 82cdf0e10cSrcweir -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY())); 83cdf0e10cSrcweir 84cdf0e10cSrcweir // scale to target size 85cdf0e10cSrcweir const double fArrowScale(fWidth / (aArrowSize.getRange().getX())); 86cdf0e10cSrcweir aArrowTransform.scale(fArrowScale, fArrowScale); 87cdf0e10cSrcweir 88cdf0e10cSrcweir // get arrow size in Y 89cdf0e10cSrcweir B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY()); 90cdf0e10cSrcweir aUpperCenter *= aArrowTransform; 91cdf0e10cSrcweir const double fArrowYLength(B2DVector(aUpperCenter).getLength()); 92cdf0e10cSrcweir 93cdf0e10cSrcweir // move arrow to have docking position centered 94cdf0e10cSrcweir aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition); 95cdf0e10cSrcweir 96cdf0e10cSrcweir // prepare polygon length 97cdf0e10cSrcweir if(fTools::equalZero(fCandidateLength)) 98cdf0e10cSrcweir { 99cdf0e10cSrcweir fCandidateLength = getLength(rCandidate); 100cdf0e10cSrcweir } 101cdf0e10cSrcweir 102cdf0e10cSrcweir // get the polygon vector we want to plant this arrow on 103cdf0e10cSrcweir const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition)); 104cdf0e10cSrcweir const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L)); 105cdf0e10cSrcweir const B2DVector aTail(getPositionAbsolute(rCandidate, 106cdf0e10cSrcweir (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength)); 107cdf0e10cSrcweir 108cdf0e10cSrcweir // from that vector, take the needed rotation and add rotate for arrow to transformation 109cdf0e10cSrcweir const B2DVector aTargetDirection(aHead - aTail); 110cdf0e10cSrcweir const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180)); 111cdf0e10cSrcweir 112cdf0e10cSrcweir // rotate around docking position 113cdf0e10cSrcweir aArrowTransform.rotate(fRotation); 114cdf0e10cSrcweir 115cdf0e10cSrcweir // move arrow docking position to polygon head 116cdf0e10cSrcweir aArrowTransform.translate(aHead.getX(), aHead.getY()); 117cdf0e10cSrcweir 118cdf0e10cSrcweir // transform retval and close 119cdf0e10cSrcweir aRetval.transform(aArrowTransform); 120cdf0e10cSrcweir aRetval.setClosed(true); 121cdf0e10cSrcweir 122cdf0e10cSrcweir // if pConsumedLength is asked for, fill it 123cdf0e10cSrcweir if(pConsumedLength) 124cdf0e10cSrcweir { 125cdf0e10cSrcweir *pConsumedLength = fConsumedLength; 126cdf0e10cSrcweir } 127cdf0e10cSrcweir } 128cdf0e10cSrcweir 129cdf0e10cSrcweir return aRetval; 130cdf0e10cSrcweir } 131cdf0e10cSrcweir } // end of namespace tools 132cdf0e10cSrcweir } // end of namespace basegfx 133cdf0e10cSrcweir 134cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 135cdf0e10cSrcweir 136cdf0e10cSrcweir namespace basegfx 137cdf0e10cSrcweir { 138cdf0e10cSrcweir // anonymus namespace for local helpers 139cdf0e10cSrcweir namespace 140cdf0e10cSrcweir { impIsSimpleEdge(const B2DCubicBezier & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)141cdf0e10cSrcweir bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad) 142cdf0e10cSrcweir { 143cdf0e10cSrcweir // isBezier() is true, already tested by caller 144cdf0e10cSrcweir const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint()); 145cdf0e10cSrcweir 146cdf0e10cSrcweir if(aEdge.equalZero()) 147cdf0e10cSrcweir { 148*3646db82Smseidel // start and end point the same, but control vectors used -> balloon curve loop 149cdf0e10cSrcweir // is not a simple edge 150cdf0e10cSrcweir return false; 151cdf0e10cSrcweir } 152cdf0e10cSrcweir 153cdf0e10cSrcweir // get tangentA and scalar with edge 154cdf0e10cSrcweir const B2DVector aTangentA(rCandidate.getTangent(0.0)); 155cdf0e10cSrcweir const double fScalarAE(aEdge.scalar(aTangentA)); 156cdf0e10cSrcweir 157cdf0e10cSrcweir if(fTools::lessOrEqual(fScalarAE, 0.0)) 158cdf0e10cSrcweir { 159cdf0e10cSrcweir // angle between TangentA and Edge is bigger or equal 90 degrees 160cdf0e10cSrcweir return false; 161cdf0e10cSrcweir } 162cdf0e10cSrcweir 163cdf0e10cSrcweir // get self-scalars for E and A 164cdf0e10cSrcweir const double fScalarE(aEdge.scalar(aEdge)); 165cdf0e10cSrcweir const double fScalarA(aTangentA.scalar(aTangentA)); 166cdf0e10cSrcweir const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad); 167cdf0e10cSrcweir 168cdf0e10cSrcweir if(fTools::moreOrEqual(fScalarA, fLengthCompareE)) 169cdf0e10cSrcweir { 170cdf0e10cSrcweir // length of TangentA is more than fMaxPartOfEdge of length of edge 171cdf0e10cSrcweir return false; 172cdf0e10cSrcweir } 173cdf0e10cSrcweir 174cdf0e10cSrcweir if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad)) 175cdf0e10cSrcweir { 176cdf0e10cSrcweir // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos 177cdf0e10cSrcweir return false; 178cdf0e10cSrcweir } 179cdf0e10cSrcweir 180cdf0e10cSrcweir // get tangentB and scalar with edge 181cdf0e10cSrcweir const B2DVector aTangentB(rCandidate.getTangent(1.0)); 182cdf0e10cSrcweir const double fScalarBE(aEdge.scalar(aTangentB)); 183cdf0e10cSrcweir 184cdf0e10cSrcweir if(fTools::lessOrEqual(fScalarBE, 0.0)) 185cdf0e10cSrcweir { 186cdf0e10cSrcweir // angle between TangentB and Edge is bigger or equal 90 degrees 187cdf0e10cSrcweir return false; 188cdf0e10cSrcweir } 189cdf0e10cSrcweir 190cdf0e10cSrcweir // get self-scalar for B 191cdf0e10cSrcweir const double fScalarB(aTangentB.scalar(aTangentB)); 192cdf0e10cSrcweir 193cdf0e10cSrcweir if(fTools::moreOrEqual(fScalarB, fLengthCompareE)) 194cdf0e10cSrcweir { 195cdf0e10cSrcweir // length of TangentB is more than fMaxPartOfEdge of length of edge 196cdf0e10cSrcweir return false; 197cdf0e10cSrcweir } 198cdf0e10cSrcweir 199cdf0e10cSrcweir if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad)) 200cdf0e10cSrcweir { 201cdf0e10cSrcweir // angle between TangentB and Edge is bigger or equal defined by fMaxCos 202cdf0e10cSrcweir return false; 203cdf0e10cSrcweir } 204cdf0e10cSrcweir 205cdf0e10cSrcweir return true; 206cdf0e10cSrcweir } 207cdf0e10cSrcweir impSubdivideToSimple(const B2DCubicBezier & rCandidate,B2DPolygon & rTarget,double fMaxCosQuad,double fMaxPartOfEdgeQuad,sal_uInt32 nMaxRecursionDepth)208cdf0e10cSrcweir void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth) 209cdf0e10cSrcweir { 210cdf0e10cSrcweir if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad)) 211cdf0e10cSrcweir { 212cdf0e10cSrcweir rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint()); 213cdf0e10cSrcweir } 214cdf0e10cSrcweir else 215cdf0e10cSrcweir { 216cdf0e10cSrcweir B2DCubicBezier aLeft, aRight; 217cdf0e10cSrcweir rCandidate.split(0.5, &aLeft, &aRight); 218cdf0e10cSrcweir 219cdf0e10cSrcweir impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1); 220cdf0e10cSrcweir impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1); 221cdf0e10cSrcweir } 222cdf0e10cSrcweir } 223cdf0e10cSrcweir subdivideToSimple(const B2DPolygon & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)224cdf0e10cSrcweir B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad) 225cdf0e10cSrcweir { 226cdf0e10cSrcweir const sal_uInt32 nPointCount(rCandidate.count()); 227cdf0e10cSrcweir 228cdf0e10cSrcweir if(rCandidate.areControlPointsUsed() && nPointCount) 229cdf0e10cSrcweir { 230cdf0e10cSrcweir const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); 231cdf0e10cSrcweir B2DPolygon aRetval; 232cdf0e10cSrcweir B2DCubicBezier aEdge; 233cdf0e10cSrcweir 234cdf0e10cSrcweir // prepare edge for loop 235cdf0e10cSrcweir aEdge.setStartPoint(rCandidate.getB2DPoint(0)); 236cdf0e10cSrcweir aRetval.append(aEdge.getStartPoint()); 237cdf0e10cSrcweir 238cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 239cdf0e10cSrcweir { 240cdf0e10cSrcweir // fill B2DCubicBezier 241cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 242cdf0e10cSrcweir aEdge.setControlPointA(rCandidate.getNextControlPoint(a)); 243cdf0e10cSrcweir aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); 244cdf0e10cSrcweir aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); 245cdf0e10cSrcweir 246cdf0e10cSrcweir // get rid of unnecessary bezier segments 247cdf0e10cSrcweir aEdge.testAndSolveTrivialBezier(); 248cdf0e10cSrcweir 249cdf0e10cSrcweir if(aEdge.isBezier()) 250cdf0e10cSrcweir { 251cdf0e10cSrcweir // before splitting recursively with internal simple criteria, use 252cdf0e10cSrcweir // ExtremumPosFinder to remove those 253cdf0e10cSrcweir ::std::vector< double > aExtremumPositions; 254cdf0e10cSrcweir 255cdf0e10cSrcweir aExtremumPositions.reserve(4); 256cdf0e10cSrcweir aEdge.getAllExtremumPositions(aExtremumPositions); 257cdf0e10cSrcweir 258cdf0e10cSrcweir const sal_uInt32 nCount(aExtremumPositions.size()); 259cdf0e10cSrcweir 260cdf0e10cSrcweir if(nCount) 261cdf0e10cSrcweir { 262cdf0e10cSrcweir if(nCount > 1) 263cdf0e10cSrcweir { 264cdf0e10cSrcweir // create order from left to right 265cdf0e10cSrcweir ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end()); 266cdf0e10cSrcweir } 267cdf0e10cSrcweir 268cdf0e10cSrcweir for(sal_uInt32 b(0); b < nCount;) 269cdf0e10cSrcweir { 270cdf0e10cSrcweir // split aEdge at next split pos 271cdf0e10cSrcweir B2DCubicBezier aLeft; 272cdf0e10cSrcweir const double fSplitPos(aExtremumPositions[b++]); 273cdf0e10cSrcweir 274cdf0e10cSrcweir aEdge.split(fSplitPos, &aLeft, &aEdge); 275cdf0e10cSrcweir aLeft.testAndSolveTrivialBezier(); 276cdf0e10cSrcweir 277cdf0e10cSrcweir // consume left part 278cdf0e10cSrcweir if(aLeft.isBezier()) 279cdf0e10cSrcweir { 280cdf0e10cSrcweir impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6); 281cdf0e10cSrcweir } 282cdf0e10cSrcweir else 283cdf0e10cSrcweir { 284cdf0e10cSrcweir aRetval.append(aLeft.getEndPoint()); 285cdf0e10cSrcweir } 286cdf0e10cSrcweir 287cdf0e10cSrcweir if(b < nCount) 288cdf0e10cSrcweir { 289cdf0e10cSrcweir // correct the remaining split positions to fit to shortened aEdge 290cdf0e10cSrcweir const double fScaleFactor(1.0 / (1.0 - fSplitPos)); 291cdf0e10cSrcweir 292cdf0e10cSrcweir for(sal_uInt32 c(b); c < nCount; c++) 293cdf0e10cSrcweir { 294cdf0e10cSrcweir aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor; 295cdf0e10cSrcweir } 296cdf0e10cSrcweir } 297cdf0e10cSrcweir } 298cdf0e10cSrcweir 299cdf0e10cSrcweir // test the shortened rest of aEdge 300cdf0e10cSrcweir aEdge.testAndSolveTrivialBezier(); 301cdf0e10cSrcweir 302cdf0e10cSrcweir // consume right part 303cdf0e10cSrcweir if(aEdge.isBezier()) 304cdf0e10cSrcweir { 305cdf0e10cSrcweir impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6); 306cdf0e10cSrcweir } 307cdf0e10cSrcweir else 308cdf0e10cSrcweir { 309cdf0e10cSrcweir aRetval.append(aEdge.getEndPoint()); 310cdf0e10cSrcweir } 311cdf0e10cSrcweir } 312cdf0e10cSrcweir else 313cdf0e10cSrcweir { 314cdf0e10cSrcweir impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6); 315cdf0e10cSrcweir } 316cdf0e10cSrcweir } 317cdf0e10cSrcweir else 318cdf0e10cSrcweir { 319cdf0e10cSrcweir // straight edge, add point 320cdf0e10cSrcweir aRetval.append(aEdge.getEndPoint()); 321cdf0e10cSrcweir } 322cdf0e10cSrcweir 323cdf0e10cSrcweir // prepare edge for next step 324cdf0e10cSrcweir aEdge.setStartPoint(aEdge.getEndPoint()); 325cdf0e10cSrcweir } 326cdf0e10cSrcweir 327cdf0e10cSrcweir // copy closed flag and check for double points 328cdf0e10cSrcweir aRetval.setClosed(rCandidate.isClosed()); 329cdf0e10cSrcweir aRetval.removeDoublePoints(); 330cdf0e10cSrcweir 331cdf0e10cSrcweir return aRetval; 332cdf0e10cSrcweir } 333cdf0e10cSrcweir else 334cdf0e10cSrcweir { 335cdf0e10cSrcweir return rCandidate; 336cdf0e10cSrcweir } 337cdf0e10cSrcweir } 338cdf0e10cSrcweir createAreaGeometryForEdge(const B2DCubicBezier & rEdge,double fHalfLineWidth,bool bStartRound,bool bEndRound,bool bStartSquare,bool bEndSquare)339c514522bSArmin Le Grand B2DPolygon createAreaGeometryForEdge( 3405aaf853bSArmin Le Grand const B2DCubicBezier& rEdge, 3415aaf853bSArmin Le Grand double fHalfLineWidth, 3425aaf853bSArmin Le Grand bool bStartRound, 3435aaf853bSArmin Le Grand bool bEndRound, 3445aaf853bSArmin Le Grand bool bStartSquare, 3455aaf853bSArmin Le Grand bool bEndSquare) 346cdf0e10cSrcweir { 347cdf0e10cSrcweir // create polygon for edge 348cdf0e10cSrcweir // Unfortunately, while it would be geometrically correct to not add 349cdf0e10cSrcweir // the in-between points EdgeEnd and EdgeStart, it leads to rounding 350cdf0e10cSrcweir // errors when converting to integer polygon coordinates for painting 351cdf0e10cSrcweir if(rEdge.isBezier()) 352cdf0e10cSrcweir { 353cdf0e10cSrcweir // prepare target and data common for upper and lower 354cdf0e10cSrcweir B2DPolygon aBezierPolygon; 355cdf0e10cSrcweir const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint()); 356cdf0e10cSrcweir const double fEdgeLength(aPureEdgeVector.getLength()); 357cdf0e10cSrcweir const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength)); 3585aaf853bSArmin Le Grand B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize(); 3595aaf853bSArmin Le Grand B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize(); 3605aaf853bSArmin Le Grand const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA)); 3615aaf853bSArmin Le Grand const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB)); 3625aaf853bSArmin Le Grand 3635aaf853bSArmin Le Grand // create upper displacement vectors and check if they cut 3645aaf853bSArmin Le Grand const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth); 3655aaf853bSArmin Le Grand const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth); 3665aaf853bSArmin Le Grand double fCutA(0.0); 3675aaf853bSArmin Le Grand const tools::CutFlagValue aCutA(tools::findCut( 3685aaf853bSArmin Le Grand rEdge.getStartPoint(), aPerpendStartA, 3695aaf853bSArmin Le Grand rEdge.getEndPoint(), aPerpendEndA, 3705aaf853bSArmin Le Grand CUTFLAG_ALL, &fCutA)); 3715aaf853bSArmin Le Grand const bool bCutA(CUTFLAG_NONE != aCutA); 3725aaf853bSArmin Le Grand 3735aaf853bSArmin Le Grand // create lower displacement vectors and check if they cut 3745aaf853bSArmin Le Grand const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth); 3755aaf853bSArmin Le Grand const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth); 3765aaf853bSArmin Le Grand double fCutB(0.0); 3775aaf853bSArmin Le Grand const tools::CutFlagValue aCutB(tools::findCut( 3785aaf853bSArmin Le Grand rEdge.getEndPoint(), aPerpendEndB, 3795aaf853bSArmin Le Grand rEdge.getStartPoint(), aPerpendStartB, 3805aaf853bSArmin Le Grand CUTFLAG_ALL, &fCutB)); 3815aaf853bSArmin Le Grand const bool bCutB(CUTFLAG_NONE != aCutB); 3825aaf853bSArmin Le Grand 3835aaf853bSArmin Le Grand // check if cut happens 3845aaf853bSArmin Le Grand const bool bCut(bCutA || bCutB); 385c514522bSArmin Le Grand B2DPoint aCutPoint; 3865aaf853bSArmin Le Grand 3875aaf853bSArmin Le Grand // create left edge 3885aaf853bSArmin Le Grand if(bStartRound || bStartSquare) 3895aaf853bSArmin Le Grand { 3905aaf853bSArmin Le Grand if(bStartRound) 3915aaf853bSArmin Le Grand { 392c514522bSArmin Le Grand basegfx::B2DPolygon aStartPolygon(tools::createHalfUnitCircle()); 393c514522bSArmin Le Grand 3945aaf853bSArmin Le Grand aStartPolygon.transform( 3955aaf853bSArmin Le Grand tools::createScaleShearXRotateTranslateB2DHomMatrix( 3965aaf853bSArmin Le Grand fHalfLineWidth, fHalfLineWidth, 3975aaf853bSArmin Le Grand 0.0, 3985aaf853bSArmin Le Grand atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2, 3995aaf853bSArmin Le Grand rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY())); 400c514522bSArmin Le Grand aBezierPolygon.append(aStartPolygon); 4015aaf853bSArmin Le Grand } 4025aaf853bSArmin Le Grand else // bStartSquare 4035aaf853bSArmin Le Grand { 4045aaf853bSArmin Le Grand const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth)); 4055aaf853bSArmin Le Grand 406c514522bSArmin Le Grand if(bCutB) 4075aaf853bSArmin Le Grand { 408c514522bSArmin Le Grand aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartB); 4095aaf853bSArmin Le Grand } 4105aaf853bSArmin Le Grand 411c514522bSArmin Le Grand aBezierPolygon.append(aStart + aPerpendStartB); 412c514522bSArmin Le Grand aBezierPolygon.append(aStart + aPerpendStartA); 4135aaf853bSArmin Le Grand 414c514522bSArmin Le Grand if(bCutA) 4155aaf853bSArmin Le Grand { 416c514522bSArmin Le Grand aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartA); 4175aaf853bSArmin Le Grand } 4185aaf853bSArmin Le Grand } 4195aaf853bSArmin Le Grand } 4205aaf853bSArmin Le Grand else 4215aaf853bSArmin Le Grand { 4225aaf853bSArmin Le Grand // append original in-between point 4235aaf853bSArmin Le Grand aBezierPolygon.append(rEdge.getStartPoint()); 4245aaf853bSArmin Le Grand } 425cdf0e10cSrcweir 426cdf0e10cSrcweir // create upper edge. 427cdf0e10cSrcweir { 4285aaf853bSArmin Le Grand if(bCutA) 429cdf0e10cSrcweir { 430cdf0e10cSrcweir // calculate cut point and add 431c514522bSArmin Le Grand aCutPoint = rEdge.getStartPoint() + (aPerpendStartA * fCutA); 432cdf0e10cSrcweir aBezierPolygon.append(aCutPoint); 433cdf0e10cSrcweir } 434cdf0e10cSrcweir else 435cdf0e10cSrcweir { 436cdf0e10cSrcweir // create scaled bezier segment 4375aaf853bSArmin Le Grand const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA); 4385aaf853bSArmin Le Grand const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA); 439cdf0e10cSrcweir const B2DVector aEdge(aEnd - aStart); 440cdf0e10cSrcweir const double fLength(aEdge.getLength()); 441cdf0e10cSrcweir const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength); 442cdf0e10cSrcweir const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint()); 443cdf0e10cSrcweir const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint()); 444cdf0e10cSrcweir 445cdf0e10cSrcweir aBezierPolygon.append(aStart); 446cdf0e10cSrcweir aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd); 447cdf0e10cSrcweir } 448cdf0e10cSrcweir } 449cdf0e10cSrcweir 4505aaf853bSArmin Le Grand // create right edge 4515aaf853bSArmin Le Grand if(bEndRound || bEndSquare) 4525aaf853bSArmin Le Grand { 4535aaf853bSArmin Le Grand if(bEndRound) 4545aaf853bSArmin Le Grand { 455c514522bSArmin Le Grand basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle()); 456c514522bSArmin Le Grand 4575aaf853bSArmin Le Grand aEndPolygon.transform( 4585aaf853bSArmin Le Grand tools::createScaleShearXRotateTranslateB2DHomMatrix( 4595aaf853bSArmin Le Grand fHalfLineWidth, fHalfLineWidth, 4605aaf853bSArmin Le Grand 0.0, 4615aaf853bSArmin Le Grand atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2, 4625aaf853bSArmin Le Grand rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY())); 463c514522bSArmin Le Grand aBezierPolygon.append(aEndPolygon); 4645aaf853bSArmin Le Grand } 4655aaf853bSArmin Le Grand else // bEndSquare 4665aaf853bSArmin Le Grand { 4675aaf853bSArmin Le Grand const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth)); 4685aaf853bSArmin Le Grand 469c514522bSArmin Le Grand if(bCutA) 4705aaf853bSArmin Le Grand { 471c514522bSArmin Le Grand aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndA); 4725aaf853bSArmin Le Grand } 4735aaf853bSArmin Le Grand 474c514522bSArmin Le Grand aBezierPolygon.append(aEnd + aPerpendEndA); 475c514522bSArmin Le Grand aBezierPolygon.append(aEnd + aPerpendEndB); 4765aaf853bSArmin Le Grand 477c514522bSArmin Le Grand if(bCutB) 4785aaf853bSArmin Le Grand { 479c514522bSArmin Le Grand aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndB); 4805aaf853bSArmin Le Grand } 4815aaf853bSArmin Le Grand } 4825aaf853bSArmin Le Grand } 4835aaf853bSArmin Le Grand else 4845aaf853bSArmin Le Grand { 485cdf0e10cSrcweir // append original in-between point 486cdf0e10cSrcweir aBezierPolygon.append(rEdge.getEndPoint()); 4875aaf853bSArmin Le Grand } 488cdf0e10cSrcweir 489cdf0e10cSrcweir // create lower edge. 490cdf0e10cSrcweir { 4915aaf853bSArmin Le Grand if(bCutB) 492cdf0e10cSrcweir { 493cdf0e10cSrcweir // calculate cut point and add 494c514522bSArmin Le Grand aCutPoint = rEdge.getEndPoint() + (aPerpendEndB * fCutB); 495cdf0e10cSrcweir aBezierPolygon.append(aCutPoint); 496cdf0e10cSrcweir } 497cdf0e10cSrcweir else 498cdf0e10cSrcweir { 499cdf0e10cSrcweir // create scaled bezier segment 5005aaf853bSArmin Le Grand const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB); 5015aaf853bSArmin Le Grand const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB); 502cdf0e10cSrcweir const B2DVector aEdge(aEnd - aStart); 503cdf0e10cSrcweir const double fLength(aEdge.getLength()); 504cdf0e10cSrcweir const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength); 505cdf0e10cSrcweir const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint()); 506cdf0e10cSrcweir const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint()); 507cdf0e10cSrcweir 508cdf0e10cSrcweir aBezierPolygon.append(aStart); 509cdf0e10cSrcweir aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd); 510cdf0e10cSrcweir } 511cdf0e10cSrcweir } 512cdf0e10cSrcweir 513c514522bSArmin Le Grand // close 514cdf0e10cSrcweir aBezierPolygon.setClosed(true); 5155aaf853bSArmin Le Grand 516c514522bSArmin Le Grand if(bStartRound || bEndRound) 517c514522bSArmin Le Grand { 518c514522bSArmin Le Grand // double points possible when round caps are used at start or end 519c514522bSArmin Le Grand aBezierPolygon.removeDoublePoints(); 520c514522bSArmin Le Grand } 521c514522bSArmin Le Grand 522c514522bSArmin Le Grand if(bCut && ((bStartRound || bStartSquare) && (bEndRound || bEndSquare))) 523c514522bSArmin Le Grand { 524c514522bSArmin Le Grand // When cut exists and both ends are extended with caps, a self-intersecting polygon 525c514522bSArmin Le Grand // is created; one cut point is known, but there is a 2nd one in the caps geometry. 526c514522bSArmin Le Grand // Solve by using tooling. 527c514522bSArmin Le Grand // Remark: This nearly never happens due to curve preparations to extreme points 528*3646db82Smseidel // and maximum angle turning, but I constructed a test case and checked that it is 529*3646db82Smseidel // working properly. 530c514522bSArmin Le Grand const B2DPolyPolygon aTemp(tools::solveCrossovers(aBezierPolygon)); 531c514522bSArmin Le Grand const sal_uInt32 nTempCount(aTemp.count()); 532c514522bSArmin Le Grand 533c514522bSArmin Le Grand if(nTempCount) 534c514522bSArmin Le Grand { 535c514522bSArmin Le Grand if(nTempCount > 1) 536c514522bSArmin Le Grand { 537c514522bSArmin Le Grand // as expected, multiple polygons (with same orientation). Remove 538c514522bSArmin Le Grand // the one which contains aCutPoint, or better take the one without 539c514522bSArmin Le Grand for (sal_uInt32 a(0); a < nTempCount; a++) 540c514522bSArmin Le Grand { 541c514522bSArmin Le Grand aBezierPolygon = aTemp.getB2DPolygon(a); 542c514522bSArmin Le Grand 543c514522bSArmin Le Grand const sal_uInt32 nCandCount(aBezierPolygon.count()); 544c514522bSArmin Le Grand 545c514522bSArmin Le Grand for(sal_uInt32 b(0); b < nCandCount; b++) 546c514522bSArmin Le Grand { 547c514522bSArmin Le Grand if(aCutPoint.equal(aBezierPolygon.getB2DPoint(b))) 548c514522bSArmin Le Grand { 549c514522bSArmin Le Grand aBezierPolygon.clear(); 550c514522bSArmin Le Grand break; 551c514522bSArmin Le Grand } 552c514522bSArmin Le Grand } 553c514522bSArmin Le Grand 554c514522bSArmin Le Grand if(aBezierPolygon.count()) 555c514522bSArmin Le Grand { 556c514522bSArmin Le Grand break; 557c514522bSArmin Le Grand } 558c514522bSArmin Le Grand } 559c514522bSArmin Le Grand 560c514522bSArmin Le Grand OSL_ENSURE(aBezierPolygon.count(), "Error in line geometry creation, could not solve self-intersection (!)"); 561c514522bSArmin Le Grand } 562c514522bSArmin Le Grand else 563c514522bSArmin Le Grand { 564c514522bSArmin Le Grand // none found, use result 565c514522bSArmin Le Grand aBezierPolygon = aTemp.getB2DPolygon(0); 566c514522bSArmin Le Grand } 567c514522bSArmin Le Grand } 568c514522bSArmin Le Grand else 569c514522bSArmin Le Grand { 570c514522bSArmin Le Grand OSL_ENSURE(false, "Error in line geometry creation, could not solve self-intersection (!)"); 571c514522bSArmin Le Grand } 572c514522bSArmin Le Grand } 573c514522bSArmin Le Grand 574c514522bSArmin Le Grand // return 575c514522bSArmin Le Grand return aBezierPolygon; 576cdf0e10cSrcweir } 577cdf0e10cSrcweir else 578cdf0e10cSrcweir { 5795aaf853bSArmin Le Grand // Get start and end point, create tangent and set to needed length 5805aaf853bSArmin Le Grand B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint()); 5815aaf853bSArmin Le Grand aTangent.setLength(fHalfLineWidth); 5825aaf853bSArmin Le Grand 5835aaf853bSArmin Le Grand // prepare return value 584cdf0e10cSrcweir B2DPolygon aEdgePolygon; 585cdf0e10cSrcweir 5865aaf853bSArmin Le Grand // buffered angle 5875aaf853bSArmin Le Grand double fAngle(0.0); 5885aaf853bSArmin Le Grand bool bAngle(false); 589cdf0e10cSrcweir 5905aaf853bSArmin Le Grand // buffered perpendicular 5915aaf853bSArmin Le Grand B2DVector aPerpend; 5925aaf853bSArmin Le Grand bool bPerpend(false); 593cdf0e10cSrcweir 5945aaf853bSArmin Le Grand // create left vertical 5955aaf853bSArmin Le Grand if(bStartRound) 5965aaf853bSArmin Le Grand { 5975aaf853bSArmin Le Grand aEdgePolygon = tools::createHalfUnitCircle(); 5985aaf853bSArmin Le Grand fAngle = atan2(aTangent.getY(), aTangent.getX()); 5995aaf853bSArmin Le Grand bAngle = true; 6005aaf853bSArmin Le Grand aEdgePolygon.transform( 6015aaf853bSArmin Le Grand tools::createScaleShearXRotateTranslateB2DHomMatrix( 6025aaf853bSArmin Le Grand fHalfLineWidth, fHalfLineWidth, 6035aaf853bSArmin Le Grand 0.0, 6045aaf853bSArmin Le Grand fAngle + F_PI2, 6055aaf853bSArmin Le Grand rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY())); 6065aaf853bSArmin Le Grand } 6075aaf853bSArmin Le Grand else 6085aaf853bSArmin Le Grand { 6095aaf853bSArmin Le Grand aPerpend.setX(-aTangent.getY()); 6105aaf853bSArmin Le Grand aPerpend.setY(aTangent.getX()); 6115aaf853bSArmin Le Grand bPerpend = true; 612cdf0e10cSrcweir 6135aaf853bSArmin Le Grand if(bStartSquare) 6145aaf853bSArmin Le Grand { 6155aaf853bSArmin Le Grand const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent); 6165aaf853bSArmin Le Grand 6175aaf853bSArmin Le Grand aEdgePolygon.append(aStart + aPerpend); 6185aaf853bSArmin Le Grand aEdgePolygon.append(aStart - aPerpend); 6195aaf853bSArmin Le Grand } 6205aaf853bSArmin Le Grand else 6215aaf853bSArmin Le Grand { 6225aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getStartPoint() + aPerpend); 6235aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons 6245aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getStartPoint() - aPerpend); 6255aaf853bSArmin Le Grand } 6265aaf853bSArmin Le Grand } 6275aaf853bSArmin Le Grand 6285aaf853bSArmin Le Grand // create right vertical 6295aaf853bSArmin Le Grand if(bEndRound) 6305aaf853bSArmin Le Grand { 6315aaf853bSArmin Le Grand basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle()); 6325aaf853bSArmin Le Grand 6335aaf853bSArmin Le Grand if(!bAngle) 6345aaf853bSArmin Le Grand { 6355aaf853bSArmin Le Grand fAngle = atan2(aTangent.getY(), aTangent.getX()); 6365aaf853bSArmin Le Grand } 6375aaf853bSArmin Le Grand 6385aaf853bSArmin Le Grand aEndPolygon.transform( 6395aaf853bSArmin Le Grand tools::createScaleShearXRotateTranslateB2DHomMatrix( 6405aaf853bSArmin Le Grand fHalfLineWidth, fHalfLineWidth, 6415aaf853bSArmin Le Grand 0.0, 6425aaf853bSArmin Le Grand fAngle - F_PI2, 6435aaf853bSArmin Le Grand rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY())); 6445aaf853bSArmin Le Grand aEdgePolygon.append(aEndPolygon); 6455aaf853bSArmin Le Grand } 6465aaf853bSArmin Le Grand else 6475aaf853bSArmin Le Grand { 6485aaf853bSArmin Le Grand if(!bPerpend) 6495aaf853bSArmin Le Grand { 6505aaf853bSArmin Le Grand aPerpend.setX(-aTangent.getY()); 6515aaf853bSArmin Le Grand aPerpend.setY(aTangent.getX()); 6525aaf853bSArmin Le Grand } 6535aaf853bSArmin Le Grand 6545aaf853bSArmin Le Grand if(bEndSquare) 6555aaf853bSArmin Le Grand { 6565aaf853bSArmin Le Grand const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent); 6575aaf853bSArmin Le Grand 6585aaf853bSArmin Le Grand aEdgePolygon.append(aEnd - aPerpend); 6595aaf853bSArmin Le Grand aEdgePolygon.append(aEnd + aPerpend); 6605aaf853bSArmin Le Grand } 6615aaf853bSArmin Le Grand else 6625aaf853bSArmin Le Grand { 6635aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getEndPoint() - aPerpend); 6645aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons 6655aaf853bSArmin Le Grand aEdgePolygon.append(rEdge.getEndPoint() + aPerpend); 6665aaf853bSArmin Le Grand } 6675aaf853bSArmin Le Grand } 668cdf0e10cSrcweir 669cdf0e10cSrcweir // close and return 670cdf0e10cSrcweir aEdgePolygon.setClosed(true); 6715aaf853bSArmin Le Grand 672c514522bSArmin Le Grand return aEdgePolygon; 673cdf0e10cSrcweir } 674cdf0e10cSrcweir } 675cdf0e10cSrcweir createAreaGeometryForJoin(const B2DVector & rTangentPrev,const B2DVector & rTangentEdge,const B2DVector & rPerpendPrev,const B2DVector & rPerpendEdge,const B2DPoint & rPoint,double fHalfLineWidth,B2DLineJoin eJoin,double fMiterMinimumAngle)676cdf0e10cSrcweir B2DPolygon createAreaGeometryForJoin( 677cdf0e10cSrcweir const B2DVector& rTangentPrev, 678cdf0e10cSrcweir const B2DVector& rTangentEdge, 679cdf0e10cSrcweir const B2DVector& rPerpendPrev, 680cdf0e10cSrcweir const B2DVector& rPerpendEdge, 681cdf0e10cSrcweir const B2DPoint& rPoint, 682cdf0e10cSrcweir double fHalfLineWidth, 683cdf0e10cSrcweir B2DLineJoin eJoin, 684cdf0e10cSrcweir double fMiterMinimumAngle) 685cdf0e10cSrcweir { 686cdf0e10cSrcweir OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)"); 687cdf0e10cSrcweir OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)"); 688cdf0e10cSrcweir 689cdf0e10cSrcweir // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint 690cdf0e10cSrcweir B2DPolygon aEdgePolygon; 691cdf0e10cSrcweir const B2DPoint aStartPoint(rPoint + rPerpendPrev); 692cdf0e10cSrcweir const B2DPoint aEndPoint(rPoint + rPerpendEdge); 693cdf0e10cSrcweir 694cdf0e10cSrcweir // test if for Miter, the angle is too small and the fallback 695cdf0e10cSrcweir // to bevel needs to be used 696cdf0e10cSrcweir if(B2DLINEJOIN_MITER == eJoin) 697cdf0e10cSrcweir { 698cdf0e10cSrcweir const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge))); 699cdf0e10cSrcweir 700cdf0e10cSrcweir if((F_PI - fAngle) < fMiterMinimumAngle) 701cdf0e10cSrcweir { 702cdf0e10cSrcweir // fallback to bevel 703cdf0e10cSrcweir eJoin = B2DLINEJOIN_BEVEL; 704cdf0e10cSrcweir } 705cdf0e10cSrcweir } 706cdf0e10cSrcweir 707cdf0e10cSrcweir switch(eJoin) 708cdf0e10cSrcweir { 709cdf0e10cSrcweir case B2DLINEJOIN_MITER : 710cdf0e10cSrcweir { 711cdf0e10cSrcweir aEdgePolygon.append(aEndPoint); 712cdf0e10cSrcweir aEdgePolygon.append(rPoint); 713cdf0e10cSrcweir aEdgePolygon.append(aStartPoint); 714cdf0e10cSrcweir 715cdf0e10cSrcweir // Look for the cut point between start point along rTangentPrev and 716cdf0e10cSrcweir // end point along rTangentEdge. -rTangentEdge should be used, but since 717cdf0e10cSrcweir // the cut value is used for interpolating along the first edge, the negation 718cdf0e10cSrcweir // is not needed since the same fCut will be found on the first edge. 719cdf0e10cSrcweir // If it exists, insert it to complete the mitered fill polygon. 720cdf0e10cSrcweir double fCutPos(0.0); 721cdf0e10cSrcweir tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos); 722cdf0e10cSrcweir 723cdf0e10cSrcweir if(0.0 != fCutPos) 724cdf0e10cSrcweir { 7251fe6099dSArmin Le Grand const B2DPoint aCutPoint(aStartPoint + (rTangentPrev * fCutPos)); 726cdf0e10cSrcweir aEdgePolygon.append(aCutPoint); 727cdf0e10cSrcweir } 728cdf0e10cSrcweir 729cdf0e10cSrcweir break; 730cdf0e10cSrcweir } 731cdf0e10cSrcweir case B2DLINEJOIN_ROUND : 732cdf0e10cSrcweir { 733cdf0e10cSrcweir // use tooling to add needed EllipseSegment 734cdf0e10cSrcweir double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX())); 735cdf0e10cSrcweir double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX())); 736cdf0e10cSrcweir 737cdf0e10cSrcweir // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI] 738cdf0e10cSrcweir if(fAngleStart < 0.0) 739cdf0e10cSrcweir { 740cdf0e10cSrcweir fAngleStart += F_2PI; 741cdf0e10cSrcweir } 742cdf0e10cSrcweir 743cdf0e10cSrcweir if(fAngleEnd < 0.0) 744cdf0e10cSrcweir { 745cdf0e10cSrcweir fAngleEnd += F_2PI; 746cdf0e10cSrcweir } 747cdf0e10cSrcweir 748cdf0e10cSrcweir const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd)); 749cdf0e10cSrcweir 750cdf0e10cSrcweir if(aBow.count() > 1) 751cdf0e10cSrcweir { 752cdf0e10cSrcweir // #i101491# 753cdf0e10cSrcweir // use the original start/end positions; the ones from bow creation may be numerically 754cdf0e10cSrcweir // different due to their different creation. To guarantee good merging quality with edges 755cdf0e10cSrcweir // and edge roundings (and to reduce point count) 756cdf0e10cSrcweir aEdgePolygon = aBow; 757cdf0e10cSrcweir aEdgePolygon.setB2DPoint(0, aStartPoint); 758cdf0e10cSrcweir aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint); 759cdf0e10cSrcweir aEdgePolygon.append(rPoint); 760cdf0e10cSrcweir 761cdf0e10cSrcweir break; 762cdf0e10cSrcweir } 763cdf0e10cSrcweir else 764cdf0e10cSrcweir { 765cdf0e10cSrcweir // wanted fall-through to default 766cdf0e10cSrcweir } 767cdf0e10cSrcweir } 768cdf0e10cSrcweir default: // B2DLINEJOIN_BEVEL 769cdf0e10cSrcweir { 770cdf0e10cSrcweir aEdgePolygon.append(aEndPoint); 771cdf0e10cSrcweir aEdgePolygon.append(rPoint); 772cdf0e10cSrcweir aEdgePolygon.append(aStartPoint); 773cdf0e10cSrcweir 774cdf0e10cSrcweir break; 775cdf0e10cSrcweir } 776cdf0e10cSrcweir } 777cdf0e10cSrcweir 778cdf0e10cSrcweir // create last polygon part for edge 779cdf0e10cSrcweir aEdgePolygon.setClosed(true); 780cdf0e10cSrcweir 781cdf0e10cSrcweir return aEdgePolygon; 782cdf0e10cSrcweir } 783*3646db82Smseidel } // end of anonymous namespace 784cdf0e10cSrcweir 785cdf0e10cSrcweir namespace tools 786cdf0e10cSrcweir { createAreaGeometry(const B2DPolygon & rCandidate,double fHalfLineWidth,B2DLineJoin eJoin,com::sun::star::drawing::LineCap eCap,double fMaxAllowedAngle,double fMaxPartOfEdge,double fMiterMinimumAngle)787cdf0e10cSrcweir B2DPolyPolygon createAreaGeometry( 788cdf0e10cSrcweir const B2DPolygon& rCandidate, 789cdf0e10cSrcweir double fHalfLineWidth, 790cdf0e10cSrcweir B2DLineJoin eJoin, 7915aaf853bSArmin Le Grand com::sun::star::drawing::LineCap eCap, 792cdf0e10cSrcweir double fMaxAllowedAngle, 793cdf0e10cSrcweir double fMaxPartOfEdge, 794cdf0e10cSrcweir double fMiterMinimumAngle) 795cdf0e10cSrcweir { 796cdf0e10cSrcweir if(fMaxAllowedAngle > F_PI2) 797cdf0e10cSrcweir { 798cdf0e10cSrcweir fMaxAllowedAngle = F_PI2; 799cdf0e10cSrcweir } 800cdf0e10cSrcweir else if(fMaxAllowedAngle < 0.01 * F_PI2) 801cdf0e10cSrcweir { 802cdf0e10cSrcweir fMaxAllowedAngle = 0.01 * F_PI2; 803cdf0e10cSrcweir } 804cdf0e10cSrcweir 805cdf0e10cSrcweir if(fMaxPartOfEdge > 1.0) 806cdf0e10cSrcweir { 807cdf0e10cSrcweir fMaxPartOfEdge = 1.0; 808cdf0e10cSrcweir } 809cdf0e10cSrcweir else if(fMaxPartOfEdge < 0.01) 810cdf0e10cSrcweir { 811cdf0e10cSrcweir fMaxPartOfEdge = 0.01; 812cdf0e10cSrcweir } 813cdf0e10cSrcweir 814cdf0e10cSrcweir if(fMiterMinimumAngle > F_PI) 815cdf0e10cSrcweir { 816cdf0e10cSrcweir fMiterMinimumAngle = F_PI; 817cdf0e10cSrcweir } 818cdf0e10cSrcweir else if(fMiterMinimumAngle < 0.01 * F_PI) 819cdf0e10cSrcweir { 820cdf0e10cSrcweir fMiterMinimumAngle = 0.01 * F_PI; 821cdf0e10cSrcweir } 822cdf0e10cSrcweir 823cdf0e10cSrcweir B2DPolygon aCandidate(rCandidate); 824cdf0e10cSrcweir const double fMaxCos(cos(fMaxAllowedAngle)); 825cdf0e10cSrcweir 826cdf0e10cSrcweir aCandidate.removeDoublePoints(); 827cdf0e10cSrcweir aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge); 828cdf0e10cSrcweir 829cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 830cdf0e10cSrcweir 831cdf0e10cSrcweir if(nPointCount) 832cdf0e10cSrcweir { 833cdf0e10cSrcweir B2DPolyPolygon aRetval; 834cdf0e10cSrcweir const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin); 835cdf0e10cSrcweir const bool bIsClosed(aCandidate.isClosed()); 836cdf0e10cSrcweir const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1); 8375aaf853bSArmin Le Grand const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap); 838cdf0e10cSrcweir 839cdf0e10cSrcweir if(nEdgeCount) 840cdf0e10cSrcweir { 841cdf0e10cSrcweir B2DCubicBezier aEdge; 842cdf0e10cSrcweir B2DCubicBezier aPrev; 843cdf0e10cSrcweir 844cdf0e10cSrcweir // prepare edge 845cdf0e10cSrcweir aEdge.setStartPoint(aCandidate.getB2DPoint(0)); 846cdf0e10cSrcweir 847cdf0e10cSrcweir if(bIsClosed && bEventuallyCreateLineJoin) 848cdf0e10cSrcweir { 849cdf0e10cSrcweir // prepare previous edge 850cdf0e10cSrcweir const sal_uInt32 nPrevIndex(nPointCount - 1); 851cdf0e10cSrcweir aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex)); 852cdf0e10cSrcweir aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex)); 853cdf0e10cSrcweir aPrev.setControlPointB(aCandidate.getPrevControlPoint(0)); 854cdf0e10cSrcweir aPrev.setEndPoint(aEdge.getStartPoint()); 855cdf0e10cSrcweir } 856cdf0e10cSrcweir 857cdf0e10cSrcweir for(sal_uInt32 a(0); a < nEdgeCount; a++) 858cdf0e10cSrcweir { 859cdf0e10cSrcweir // fill current Edge 860cdf0e10cSrcweir const sal_uInt32 nNextIndex((a + 1) % nPointCount); 861cdf0e10cSrcweir aEdge.setControlPointA(aCandidate.getNextControlPoint(a)); 862cdf0e10cSrcweir aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex)); 863cdf0e10cSrcweir aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex)); 864cdf0e10cSrcweir 865cdf0e10cSrcweir // check and create linejoin 866cdf0e10cSrcweir if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a)) 867cdf0e10cSrcweir { 8685aaf853bSArmin Le Grand B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize(); 8695aaf853bSArmin Le Grand B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize(); 870cdf0e10cSrcweir B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge)); 871cdf0e10cSrcweir 872cdf0e10cSrcweir if(ORIENTATION_NEUTRAL == aOrientation) 873cdf0e10cSrcweir { 87430acf5e8Spfg // they are parallel or empty; if they are both not zero and point 875cdf0e10cSrcweir // in opposite direction, a half-circle is needed 876cdf0e10cSrcweir if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero()) 877cdf0e10cSrcweir { 878cdf0e10cSrcweir const double fAngle(fabs(aTangentPrev.angle(aTangentEdge))); 879cdf0e10cSrcweir 880cdf0e10cSrcweir if(fTools::equal(fAngle, F_PI)) 881cdf0e10cSrcweir { 882cdf0e10cSrcweir // for half-circle production, fallback to positive 883cdf0e10cSrcweir // orientation 884cdf0e10cSrcweir aOrientation = ORIENTATION_POSITIVE; 885cdf0e10cSrcweir } 886cdf0e10cSrcweir } 887cdf0e10cSrcweir } 888cdf0e10cSrcweir 889cdf0e10cSrcweir if(ORIENTATION_POSITIVE == aOrientation) 890cdf0e10cSrcweir { 8915aaf853bSArmin Le Grand const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth); 8925aaf853bSArmin Le Grand const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth); 893cdf0e10cSrcweir 8945aaf853bSArmin Le Grand aRetval.append( 8955aaf853bSArmin Le Grand createAreaGeometryForJoin( 8965aaf853bSArmin Le Grand aTangentPrev, 8975aaf853bSArmin Le Grand aTangentEdge, 8985aaf853bSArmin Le Grand aPerpendPrev, 8995aaf853bSArmin Le Grand aPerpendEdge, 9005aaf853bSArmin Le Grand aEdge.getStartPoint(), 9015aaf853bSArmin Le Grand fHalfLineWidth, 9025aaf853bSArmin Le Grand eJoin, 9035aaf853bSArmin Le Grand fMiterMinimumAngle)); 904cdf0e10cSrcweir } 905cdf0e10cSrcweir else if(ORIENTATION_NEGATIVE == aOrientation) 906cdf0e10cSrcweir { 9075aaf853bSArmin Le Grand const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth); 9085aaf853bSArmin Le Grand const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth); 909cdf0e10cSrcweir 9105aaf853bSArmin Le Grand aRetval.append( 9115aaf853bSArmin Le Grand createAreaGeometryForJoin( 9125aaf853bSArmin Le Grand aTangentEdge, 9135aaf853bSArmin Le Grand aTangentPrev, 9145aaf853bSArmin Le Grand aPerpendEdge, 9155aaf853bSArmin Le Grand aPerpendPrev, 9165aaf853bSArmin Le Grand aEdge.getStartPoint(), 9175aaf853bSArmin Le Grand fHalfLineWidth, 9185aaf853bSArmin Le Grand eJoin, 9195aaf853bSArmin Le Grand fMiterMinimumAngle)); 920cdf0e10cSrcweir } 921cdf0e10cSrcweir } 922cdf0e10cSrcweir 923cdf0e10cSrcweir // create geometry for edge 9245aaf853bSArmin Le Grand const bool bLast(a + 1 == nEdgeCount); 9255aaf853bSArmin Le Grand 9265aaf853bSArmin Le Grand if(bLineCap) 9275aaf853bSArmin Le Grand { 9285aaf853bSArmin Le Grand const bool bFirst(!a); 9295aaf853bSArmin Le Grand 9305aaf853bSArmin Le Grand aRetval.append( 9315aaf853bSArmin Le Grand createAreaGeometryForEdge( 9325aaf853bSArmin Le Grand aEdge, 9335aaf853bSArmin Le Grand fHalfLineWidth, 9345aaf853bSArmin Le Grand bFirst && com::sun::star::drawing::LineCap_ROUND == eCap, 9355aaf853bSArmin Le Grand bLast && com::sun::star::drawing::LineCap_ROUND == eCap, 9365aaf853bSArmin Le Grand bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap, 9375aaf853bSArmin Le Grand bLast && com::sun::star::drawing::LineCap_SQUARE == eCap)); 9385aaf853bSArmin Le Grand } 9395aaf853bSArmin Le Grand else 9405aaf853bSArmin Le Grand { 9415aaf853bSArmin Le Grand aRetval.append( 9425aaf853bSArmin Le Grand createAreaGeometryForEdge( 9435aaf853bSArmin Le Grand aEdge, 9445aaf853bSArmin Le Grand fHalfLineWidth, 9455aaf853bSArmin Le Grand false, 9465aaf853bSArmin Le Grand false, 9475aaf853bSArmin Le Grand false, 9485aaf853bSArmin Le Grand false)); 9495aaf853bSArmin Le Grand } 950cdf0e10cSrcweir 951cdf0e10cSrcweir // prepare next step 9525aaf853bSArmin Le Grand if(!bLast) 9535aaf853bSArmin Le Grand { 954cdf0e10cSrcweir if(bEventuallyCreateLineJoin) 955cdf0e10cSrcweir { 956cdf0e10cSrcweir aPrev = aEdge; 957cdf0e10cSrcweir } 958cdf0e10cSrcweir 959cdf0e10cSrcweir aEdge.setStartPoint(aEdge.getEndPoint()); 960cdf0e10cSrcweir } 961cdf0e10cSrcweir } 9625aaf853bSArmin Le Grand } 963cdf0e10cSrcweir 964cdf0e10cSrcweir return aRetval; 965cdf0e10cSrcweir } 966cdf0e10cSrcweir else 967cdf0e10cSrcweir { 968cdf0e10cSrcweir return B2DPolyPolygon(rCandidate); 969cdf0e10cSrcweir } 970cdf0e10cSrcweir } 971cdf0e10cSrcweir } // end of namespace tools 972cdf0e10cSrcweir } // end of namespace basegfx 973cdf0e10cSrcweir 974*3646db82Smseidel /* vim: set noet sw=4 ts=4: */ 975