diff options
Diffstat (limited to 'basegfx')
-rw-r--r-- | basegfx/source/polygon/b2dpolygontools.cxx | 36 | ||||
-rw-r--r-- | basegfx/source/polygon/b3dpolygontools.cxx | 255 |
2 files changed, 205 insertions, 86 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index 68d1120bc2cb..c194a38dc9d2 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -16,6 +16,8 @@ * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <numeric> +#include <algorithm> #include <basegfx/numeric/ftools.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -32,8 +34,6 @@ #include <basegfx/curve/b2dbeziertools.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> -#include <numeric> - // #i37443# #define ANGLE_BOUND_START_VALUE (2.25) #define ANGLE_BOUND_MINIMUM_VALUE (0.1) @@ -1231,6 +1231,32 @@ namespace basegfx::utils return; } + // precalculate maximal acceptable length of candidate polygon assuming + // we want to create a maximum of fNumberOfAllowedSnippets. For + // fNumberOfAllowedSnippets use ca. 65536, double due to line & gap. + static double fNumberOfAllowedSnippets(65535.0 * 2.0); + const double fAllowedLength((fNumberOfAllowedSnippets * fDotDashLength) / double(rDotDashArray.size())); + const double fCandidateLength(basegfx::utils::getLength(rCandidate)); + std::vector<double> aDotDashArray(rDotDashArray); + + if(fCandidateLength > fAllowedLength) + { + // we would produce more than fNumberOfAllowedSnippets, so + // adapt aDotDashArray to exactly produce assumed number. Also + // assert this to let the caller know about it. + // If this asserts: Please think about checking your DotDashArray + // before calling this function or evtl. use the callback version + // to *not* produce that much of data. Even then, you may still + // think about producing too much runtime (!) + assert(true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)"); + + // calculate correcting factor, apply to aDotDashArray and fDotDashLength + // to enlarge these as needed + const double fFactor(fCandidateLength / fAllowedLength); + std::for_each(aDotDashArray.begin(), aDotDashArray.end(), [&fFactor](double &f){ f *= fFactor; }); + fDotDashLength *= fFactor; + } + // prepare current edge's start B2DCubicBezier aCurrentEdge; const bool bIsClosed(rCandidate.isClosed()); @@ -1240,7 +1266,7 @@ namespace basegfx::utils // prepare DotDashArray iteration and the line/gap switching bool sal_uInt32 nDotDashIndex(0); bool bIsLine(true); - double fDotDashMovingLength(rDotDashArray[0]); + double fDotDashMovingLength(aDotDashArray[0]); B2DPolygon aSnippet; // remember 1st and last snippets to try to merge after execution @@ -1303,7 +1329,7 @@ namespace basegfx::utils // prepare next DotDashArray step and flip line/gap flag fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; + fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount]; bIsLine = !bIsLine; } @@ -1367,7 +1393,7 @@ namespace basegfx::utils // prepare next DotDashArray step and flip line/gap flag fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; + fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount]; bIsLine = !bIsLine; } diff --git a/basegfx/source/polygon/b3dpolygontools.cxx b/basegfx/source/polygon/b3dpolygontools.cxx index 1d97d4d43f0e..a0349a156626 100644 --- a/basegfx/source/polygon/b3dpolygontools.cxx +++ b/basegfx/source/polygon/b3dpolygontools.cxx @@ -90,7 +90,90 @@ namespace basegfx::utils return fRetval; } - void applyLineDashing(const B3DPolygon& rCandidate, const std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, double fDotDashLength) + void applyLineDashing( + const B3DPolygon& rCandidate, + const std::vector<double>& rDotDashArray, + B3DPolyPolygon* pLineTarget, + double fDotDashLength) + { + // clear targets in any case + if(pLineTarget) + { + pLineTarget->clear(); + } + + // provide callback as lambda + auto aLineCallback( + nullptr == pLineTarget + ? std::function<void(const basegfx::B3DPolygon&)>() + : [&pLineTarget](const basegfx::B3DPolygon& rSnippet){ pLineTarget->append(rSnippet); }); + + // call version that uses callbacks + applyLineDashing( + rCandidate, + rDotDashArray, + aLineCallback, + fDotDashLength); + } + + static void implHandleSnippet( + const B3DPolygon& rSnippet, + std::function<void(const basegfx::B3DPolygon& rSnippet)>& rTargetCallback, + B3DPolygon& rFirst, + B3DPolygon& 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::B3DPolygon& rSnippet)>& rTargetCallback, + B3DPolygon& rFirst, + B3DPolygon& rLast) + { + if(rFirst.count() && rLast.count() + && rFirst.getB3DPoint(0).equal(rLast.getB3DPoint(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 B3DPolygon& rCandidate, + const std::vector<double>& rDotDashArray, + std::function<void(const basegfx::B3DPolygon& rSnippet)> aLineTargetCallback, + double fDotDashLength) { const sal_uInt32 nPointCount(rCandidate.count()); const sal_uInt32 nDotDashCount(rDotDashArray.size()); @@ -100,62 +183,74 @@ namespace basegfx::utils fDotDashLength = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0); } - if(fTools::more(fDotDashLength, 0.0) && pLineTarget && nPointCount) + if(fTools::lessOrEqual(fDotDashLength, 0.0) || !aLineTargetCallback || !nPointCount) { - // clear targets - if(pLineTarget) + // parameters make no sense, just add source to targets + if(aLineTargetCallback) { - pLineTarget->clear(); + aLineTargetCallback(rCandidate); } - // prepare current edge's start - B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); - const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); - - // prepare DotDashArray iteration and the line/gap switching bool - sal_uInt32 nDotDashIndex(0); - bool bIsLine(true); - double fDotDashMovingLength(rDotDashArray[0]); - B3DPolygon aSnippet; - - // iterate over all edges - for(sal_uInt32 a(0); a < nEdgeCount; a++) - { - // update current edge - const sal_uInt32 nNextIndex((a + 1) % nPointCount); - const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); - const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength()); + return; + } - if(!fTools::equalZero(fEdgeLength)) - { - double fLastDotDashMovingLength(0.0); - while(fTools::less(fDotDashMovingLength, fEdgeLength)) - { - // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] - const bool bHandleLine(bIsLine && pLineTarget); + // precalculate maximal acceptable length of candidate polygon assuming + // we want to create a maximum of fNumberOfAllowedSnippets. In 3D + // use less for fNumberOfAllowedSnippets, ca. 6553.6, double due to line & gap. + // Less in 3D due to potentially blowing up to rounded line segments. + static double fNumberOfAllowedSnippets(6553.5 * 2.0); + const double fAllowedLength((fNumberOfAllowedSnippets * fDotDashLength) / double(rDotDashArray.size())); + const double fCandidateLength(basegfx::utils::getLength(rCandidate)); + std::vector<double> aDotDashArray(rDotDashArray); - if(bHandleLine) - { - if(!aSnippet.count()) - { - aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); - } + if(fCandidateLength > fAllowedLength) + { + // we would produce more than fNumberOfAllowedSnippets, so + // adapt aDotDashArray to exactly produce assumed number. Also + // assert this to let the caller know about it. + // If this asserts: Please think about checking your DotDashArray + // before calling this function or evtl. use the callback version + // to *not* produce that much of data. Even then, you may still + // think about producing too much runtime (!) + assert(true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)"); + + // calculate correcting factor, apply to aDotDashArray and fDotDashLength + // to enlarge these as needed + const double fFactor(fCandidateLength / fAllowedLength); + std::for_each(aDotDashArray.begin(), aDotDashArray.end(), [&fFactor](double &f){ f *= fFactor; }); + fDotDashLength *= fFactor; + } - aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); + // prepare current edge's start + B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); + const bool bIsClosed(rCandidate.isClosed()); + const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1); - pLineTarget->append(aSnippet); + // prepare DotDashArray iteration and the line/gap switching bool + sal_uInt32 nDotDashIndex(0); + bool bIsLine(true); + double fDotDashMovingLength(aDotDashArray[0]); + B3DPolygon aSnippet; - aSnippet.clear(); - } + // remember 1st and last snippets to try to merge after execution + // is complete and hand to callback + B3DPolygon aFirstLine, aLastLine; - // prepare next DotDashArray step and flip line/gap flag - fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; - bIsLine = !bIsLine; - } + // iterate over all edges + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // update current edge + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); + const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength()); - // append snippet [fLastDotDashMovingLength, fEdgeLength] - const bool bHandleLine(bIsLine && pLineTarget); + if(!fTools::equalZero(fEdgeLength)) + { + double fLastDotDashMovingLength(0.0); + while(fTools::less(fDotDashMovingLength, fEdgeLength)) + { + // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); if(bHandleLine) { @@ -164,57 +259,55 @@ namespace basegfx::utils aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); } - aSnippet.append(aNextPoint); - } + aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); - // prepare move to next edge - fDotDashMovingLength -= fEdgeLength; - } + implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); - // prepare next edge step (end point gets new start point) - aCurrentPoint = aNextPoint; - } + aSnippet.clear(); + } - // append last intermediate results (if exists) - if(aSnippet.count()) - { - if(bIsLine && pLineTarget) - { - pLineTarget->append(aSnippet); + // prepare next DotDashArray step and flip line/gap flag + fLastDotDashMovingLength = fDotDashMovingLength; + fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount]; + bIsLine = !bIsLine; } - } - // check if start and end polygon may be merged - if(pLineTarget) - { - const sal_uInt32 nCount(pLineTarget->count()); + // append snippet [fLastDotDashMovingLength, fEdgeLength] + const bool bHandleLine(bIsLine && aLineTargetCallback); - if(nCount > 1) + if(bHandleLine) { - // these polygons were created above, there exists none with less than two points, - // thus direct point access below is allowed - const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0)); - B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1)); - - if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1))) + if(!aSnippet.count()) { - // start of first and end of last are the same -> merge them - aLast.append(aFirst); - aLast.removeDoublePoints(); - pLineTarget->setB3DPolygon(0, aLast); - pLineTarget->remove(nCount - 1); + aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); } + + aSnippet.append(aNextPoint); } + + // prepare move to next edge + fDotDashMovingLength -= fEdgeLength; } + + // prepare next edge step (end point gets new start point) + aCurrentPoint = aNextPoint; } - else + + // append last intermediate results (if exists) + if(aSnippet.count()) { - // parameters make no sense, just add source to targets - if(pLineTarget) + const bool bHandleLine(bIsLine && aLineTargetCallback); + + if(bHandleLine) { - pLineTarget->append(rCandidate); + implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); } } + + if(bIsClosed && aLineTargetCallback) + { + implHandleFirstLast(aLineTargetCallback, aFirstLine, aLastLine); + } } B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter) |