diff options
Diffstat (limited to 'basegfx')
-rw-r--r-- | basegfx/source/polygon/b2dpolygontools.cxx | 419 |
1 files changed, 240 insertions, 179 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index cf2de34859cd..68d1120bc2cb 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -1110,7 +1110,102 @@ namespace basegfx::utils return false; } - void applyLineDashing(const B2DPolygon& rCandidate, const std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength) + void applyLineDashing( + const B2DPolygon& rCandidate, + const std::vector<double>& rDotDashArray, + B2DPolyPolygon* pLineTarget, + B2DPolyPolygon* pGapTarget, + double fDotDashLength) + { + // clear targets in any case + if(pLineTarget) + { + pLineTarget->clear(); + } + + if(pGapTarget) + { + pGapTarget->clear(); + } + + // provide callbacks as lambdas + auto aLineCallback( + nullptr == pLineTarget + ? std::function<void(const basegfx::B2DPolygon&)>() + : [&pLineTarget](const basegfx::B2DPolygon& rSnippet){ pLineTarget->append(rSnippet); }); + auto aGapCallback( + nullptr == pGapTarget + ? std::function<void(const basegfx::B2DPolygon&)>() + : [&pGapTarget](const basegfx::B2DPolygon& rSnippet){ pGapTarget->append(rSnippet); }); + + // call version that uses callbacks + applyLineDashing( + rCandidate, + rDotDashArray, + aLineCallback, + aGapCallback, + fDotDashLength); + } + + static void implHandleSnippet( + const B2DPolygon& rSnippet, + std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback, + B2DPolygon& rFirst, + B2DPolygon& rLast) + { + if(rSnippet.isClosed()) + { + if(!rFirst.count()) + { + rFirst = rSnippet; + } + else + { + if(rLast.count()) + { + rTargetCallback(rLast); + } + + rLast = rSnippet; + } + } + else + { + rTargetCallback(rSnippet); + } + } + + static void implHandleFirstLast( + std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback, + B2DPolygon& rFirst, + B2DPolygon& rLast) + { + if(rFirst.count() && rLast.count() + && rFirst.getB2DPoint(0).equal(rLast.getB2DPoint(rLast.count() - 1))) + { + // start of first and end of last are the same -> merge them + rLast.append(rFirst); + rLast.removeDoublePoints(); + rFirst.clear(); + } + + if(rLast.count()) + { + rTargetCallback(rLast); + } + + if(rFirst.count()) + { + rTargetCallback(rFirst); + } + } + + void applyLineDashing( + const B2DPolygon& rCandidate, + const std::vector<double>& rDotDashArray, + std::function<void(const basegfx::B2DPolygon& rSnippet)> aLineTargetCallback, + std::function<void(const basegfx::B2DPolygon& rSnippet)> aGapTargetCallback, + double fDotDashLength) { const sal_uInt32 nPointCount(rCandidate.count()); const sal_uInt32 nDotDashCount(rDotDashArray.size()); @@ -1120,244 +1215,210 @@ namespace basegfx::utils fDotDashLength = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0); } - if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount) + if(fTools::lessOrEqual(fDotDashLength, 0.0) || (!aLineTargetCallback && !aGapTargetCallback) || !nPointCount) { - // clear targets - if(pLineTarget) + // parameters make no sense, just add source to targets + if(aLineTargetCallback) { - pLineTarget->clear(); + aLineTargetCallback(rCandidate); } - if(pGapTarget) + if(aGapTargetCallback) { - pGapTarget->clear(); + aGapTargetCallback(rCandidate); } - // prepare current edge's start - B2DCubicBezier aCurrentEdge; - const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); - aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0)); + return; + } - // prepare DotDashArray iteration and the line/gap switching bool - sal_uInt32 nDotDashIndex(0); - bool bIsLine(true); - double fDotDashMovingLength(rDotDashArray[0]); - B2DPolygon aSnippet; + // prepare current edge's start + B2DCubicBezier aCurrentEdge; + const bool bIsClosed(rCandidate.isClosed()); + const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1); + aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0)); - // iterate over all edges - for(sal_uInt32 a(0); a < nEdgeCount; a++) - { - // update current edge (fill in C1, C2 and end point) - double fLastDotDashMovingLength(0.0); - const sal_uInt32 nNextIndex((a + 1) % nPointCount); - aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a)); - aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); - aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); + // prepare DotDashArray iteration and the line/gap switching bool + sal_uInt32 nDotDashIndex(0); + bool bIsLine(true); + double fDotDashMovingLength(rDotDashArray[0]); + B2DPolygon aSnippet; - // check if we have a trivial bezier segment -> possible fallback to edge - aCurrentEdge.testAndSolveTrivialBezier(); + // remember 1st and last snippets to try to merge after execution + // is complete and hand to callback + B2DPolygon aFirstLine, aLastLine; + B2DPolygon aFirstGap, aLastGap; - if(aCurrentEdge.isBezier()) - { - // bezier segment - const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge); - const double fEdgeLength(aCubicBezierHelper.getLength()); + // iterate over all edges + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // update current edge (fill in C1, C2 and end point) + double fLastDotDashMovingLength(0.0); + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a)); + aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); - if(!fTools::equalZero(fEdgeLength)) + // check if we have a trivial bezier segment -> possible fallback to edge + aCurrentEdge.testAndSolveTrivialBezier(); + + if(aCurrentEdge.isBezier()) + { + // bezier segment + const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge); + const double fEdgeLength(aCubicBezierHelper.getLength()); + + if(!fTools::equalZero(fEdgeLength)) + { + while(fTools::less(fDotDashMovingLength, fEdgeLength)) { - while(fTools::less(fDotDashMovingLength, fEdgeLength)) + // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); + const bool bHandleGap(!bIsLine && aGapTargetCallback); + + if(bHandleLine || bHandleGap) { - // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); + const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); + const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength)); + B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd)); - if(bHandleLine || bHandleGap) + if(!aSnippet.count()) { - const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); - const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength)); - B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd)); - - if(!aSnippet.count()) - { - aSnippet.append(aBezierSnippet.getStartPoint()); - } + aSnippet.append(aBezierSnippet.getStartPoint()); + } - aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint()); + aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint()); - if(bHandleLine) - { - pLineTarget->append(aSnippet); - } - else - { - pGapTarget->append(aSnippet); - } + if(bHandleLine) + { + implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); + } - aSnippet.clear(); + if(bHandleGap) + { + implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap); } - // prepare next DotDashArray step and flip line/gap flag - fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; - bIsLine = !bIsLine; + aSnippet.clear(); } - // append closing snippet [fLastDotDashMovingLength, fEdgeLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); + // prepare next DotDashArray step and flip line/gap flag + fLastDotDashMovingLength = fDotDashMovingLength; + fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; + bIsLine = !bIsLine; + } - if(bHandleLine || bHandleGap) - { - B2DCubicBezier aRight; - const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); + // append closing snippet [fLastDotDashMovingLength, fEdgeLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); + const bool bHandleGap(!bIsLine && aGapTargetCallback); - aCurrentEdge.split(fBezierSplit, nullptr, &aRight); + if(bHandleLine || bHandleGap) + { + B2DCubicBezier aRight; + const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength)); - if(!aSnippet.count()) - { - aSnippet.append(aRight.getStartPoint()); - } + aCurrentEdge.split(fBezierSplit, nullptr, &aRight); - aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint()); + if(!aSnippet.count()) + { + aSnippet.append(aRight.getStartPoint()); } - // prepare move to next edge - fDotDashMovingLength -= fEdgeLength; + aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint()); } + + // prepare move to next edge + fDotDashMovingLength -= fEdgeLength; } - else - { - // simple edge - const double fEdgeLength(aCurrentEdge.getEdgeLength()); + } + else + { + // simple edge + const double fEdgeLength(aCurrentEdge.getEdgeLength()); - if(!fTools::equalZero(fEdgeLength)) + if(!fTools::equalZero(fEdgeLength)) + { + while(fTools::less(fDotDashMovingLength, fEdgeLength)) { - while(fTools::less(fDotDashMovingLength, fEdgeLength)) - { - // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); + // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); + const bool bHandleGap(!bIsLine && aGapTargetCallback); - if(bHandleLine || bHandleGap) + if(bHandleLine || bHandleGap) + { + if(!aSnippet.count()) { - if(!aSnippet.count()) - { - aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); - } + aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); + } - aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength)); + aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength)); - if(bHandleLine) - { - pLineTarget->append(aSnippet); - } - else - { - pGapTarget->append(aSnippet); - } + if(bHandleLine) + { + implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); + } - aSnippet.clear(); + if(bHandleGap) + { + implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap); } - // prepare next DotDashArray step and flip line/gap flag - fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; - bIsLine = !bIsLine; + aSnippet.clear(); } - // append snippet [fLastDotDashMovingLength, fEdgeLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); + // prepare next DotDashArray step and flip line/gap flag + fLastDotDashMovingLength = fDotDashMovingLength; + fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; + bIsLine = !bIsLine; + } - if(bHandleLine || bHandleGap) - { - if(!aSnippet.count()) - { - aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); - } + // append snippet [fLastDotDashMovingLength, fEdgeLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); + const bool bHandleGap(!bIsLine && aGapTargetCallback); - aSnippet.append(aCurrentEdge.getEndPoint()); + if(bHandleLine || bHandleGap) + { + if(!aSnippet.count()) + { + aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength)); } - // prepare move to next edge - fDotDashMovingLength -= fEdgeLength; + aSnippet.append(aCurrentEdge.getEndPoint()); } - } - // prepare next edge step (end point gets new start point) - aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint()); - } - - // append last intermediate results (if exists) - if(aSnippet.count()) - { - if(bIsLine && pLineTarget) - { - pLineTarget->append(aSnippet); - } - else if(!bIsLine && pGapTarget) - { - pGapTarget->append(aSnippet); + // prepare move to next edge + fDotDashMovingLength -= fEdgeLength; } } - // check if start and end polygon may be merged - if(pLineTarget) - { - const sal_uInt32 nCount(pLineTarget->count()); + // prepare next edge step (end point gets new start point) + aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint()); + } - if(nCount > 1) - { - // these polygons were created above, there exists none with less than two points, - // thus direct point access below is allowed - const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0)); - B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1)); + // append last intermediate results (if exists) + if(aSnippet.count()) + { + const bool bHandleLine(bIsLine && aLineTargetCallback); + const bool bHandleGap(!bIsLine && aGapTargetCallback); - if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1))) - { - // start of first and end of last are the same -> merge them - aLast.append(aFirst); - aLast.removeDoublePoints(); - pLineTarget->setB2DPolygon(0, aLast); - pLineTarget->remove(nCount - 1); - } - } + if(bHandleLine) + { + implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); } - if(pGapTarget) + if(bHandleGap) { - const sal_uInt32 nCount(pGapTarget->count()); - - if(nCount > 1) - { - // these polygons were created above, there exists none with less than two points, - // thus direct point access below is allowed - const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0)); - B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1)); - - if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1))) - { - // start of first and end of last are the same -> merge them - aLast.append(aFirst); - aLast.removeDoublePoints(); - pGapTarget->setB2DPolygon(0, aLast); - pGapTarget->remove(nCount - 1); - } - } + implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap); } } - else + + if(bIsClosed && aLineTargetCallback) { - // parameters make no sense, just add source to targets - if(pLineTarget) - { - pLineTarget->append(rCandidate); - } + implHandleFirstLast(aLineTargetCallback, aFirstLine, aLastLine); + } - if(pGapTarget) - { - pGapTarget->append(rCandidate); - } + if(bIsClosed && aGapTargetCallback) + { + implHandleFirstLast(aGapTargetCallback, aFirstGap, aLastGap); } } |