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