diff options
author | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:57:57 +0000 |
---|---|---|
committer | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:57:57 +0000 |
commit | b62bca7a2c2e30fefcc0e8ddce3f5b440ab13080 (patch) | |
tree | ec59b411399881b182965e7fb98a3b99f3a3460e /basegfx/source/polygon | |
parent | 6a16e9b41217e6cc15b708988140b3b350c380a4 (diff) |
INTEGRATION: CWS canvas02 (1.2.8); FILE MERGED
2005/10/08 13:19:12 thb 1.2.8.2: RESYNC: (1.2-1.3); FILE MERGED
2005/07/28 10:10:19 thb 1.2.8.1: Join from cws_src680_aw024: #i48939# and new rendering subsystem need AW's clipper changes
Diffstat (limited to 'basegfx/source/polygon')
-rw-r--r-- | basegfx/source/polygon/b2dpolygoncutandtouch.cxx | 927 |
1 files changed, 668 insertions, 259 deletions
diff --git a/basegfx/source/polygon/b2dpolygoncutandtouch.cxx b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx index 38f2ad27115f..88ad7b821ab9 100644 --- a/basegfx/source/polygon/b2dpolygoncutandtouch.cxx +++ b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx @@ -4,9 +4,9 @@ * * $RCSfile: b2dpolygoncutandtouch.cxx,v $ * - * $Revision: 1.3 $ + * $Revision: 1.4 $ * - * last change: $Author: rt $ $Date: 2005-09-07 20:46:07 $ + * last change: $Author: kz $ $Date: 2005-11-02 13:57:57 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -65,42 +65,53 @@ #include <basegfx/polygon/b2dpolypolygontools.hxx> #endif +#ifndef _BGFX_CURVE_B2DCUBICBEZIER_HXX +#include <basegfx/curve/b2dcubicbezier.hxx> +#endif + #include <vector> #include <algorithm> ////////////////////////////////////////////////////////////////////////////// +// defines + +#define SUBDIVIDE_FOR_CUT_TEST_COUNT (50) + +////////////////////////////////////////////////////////////////////////////// namespace basegfx { namespace { + //////////////////////////////////////////////////////////////////////////////// + class temporaryPoint { - B2DPoint maPoint; - sal_uInt32 mnIndex; - double mfQuadDistance; + B2DPoint maPoint; // the new point + sal_uInt32 mnIndex; // index after which to insert + double mfCut; // parametric cut description [0.0 .. 1.0] public: - temporaryPoint(const B2DPoint& rOldPoint, const B2DPoint& rNewPoint, sal_uInt32 nIndex) + temporaryPoint(const B2DPoint& rNewPoint, sal_uInt32 nIndex, double fCut) : maPoint(rNewPoint), - mnIndex(nIndex) + mnIndex(nIndex), + mfCut(fCut) { - const B2DVector aDist(rNewPoint - rOldPoint); - mfQuadDistance = (aDist.getX() * aDist.getX() + aDist.getY() * aDist.getY()); } bool operator<(const temporaryPoint& rComp) const { if(mnIndex == rComp.mnIndex) { - return (mfQuadDistance < rComp.mfQuadDistance); + return (mfCut < rComp.mfCut); } return (mnIndex < rComp.mnIndex); } const B2DPoint& getPoint() const { return maPoint; } - const sal_uInt32 getIndex() const { return mnIndex; } + sal_uInt32 getIndex() const { return mnIndex; } + double getCut() const { return mfCut; } }; //////////////////////////////////////////////////////////////////////////////// @@ -124,265 +135,627 @@ namespace basegfx //////////////////////////////////////////////////////////////////////////////// - void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) + B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) { - const sal_uInt32 nCount(rCandidate.count()); + if(rTempPoints.size()) + { + B2DPolygon aRetval; + sal_uInt32 nNewInd(0L); + const sal_uInt32 nCount(rCandidate.count()); + const bool bCurvesInvolved(rCandidate.areControlPointsUsed()); + + // sort by indices to assure increasing fCut values and increasing indices + ::std::sort(rTempPoints.begin(), rTempPoints.end()); + + // add found cut and touch points + if(bCurvesInvolved) + { + // merge new polygon by indices + for(sal_uInt32 a(0L); a < nCount; a++) + { + // get cubic bezier snippet + B2DCubicBezier aCubicBezier( + rCandidate.getB2DPoint(a), + rCandidate.getControlVectorA(a), + rCandidate.getControlVectorB(a), + rCandidate.getB2DPoint(a + 1L == nCount ? 0L : a + 1L)); + double fLeftStart(0.0); + + // add original point + aRetval.append(aCubicBezier.getStartPoint()); + + if(aCubicBezier.isBezier()) + { + // if really bezier, copy control vectors to added point, too + const sal_uInt32 nRetvalIndex(aRetval.count() - 1L); + aRetval.setControlPointA(nRetvalIndex, aCubicBezier.getControlPointA()); + aRetval.setControlPointB(nRetvalIndex, aCubicBezier.getControlPointB()); + } + + // now add all points targeted to be at this index + while(nNewInd < rTempPoints.size() && rTempPoints[nNewInd].getIndex() == a) + { + const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; + + // split curve segment. Splits need to come sorted and need to be < 1.0. Also, + // since original segment is consumed from left to right, the cut values need + // to be scaled to the remaining part + B2DCubicBezier aLeftPart; + const double fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart)); + aCubicBezier.split(fRelativeSplitPoint, aLeftPart, aCubicBezier); + fLeftStart = rTempPoint.getCut(); + + // correct vectors on last added point + const sal_uInt32 nRetvalCount(aRetval.count()); + aRetval.setControlPointA(nRetvalCount - 1L, aLeftPart.getControlPointA()); + aRetval.setControlPointB(nRetvalCount - 1L, aLeftPart.getControlPointB()); + + // append new point, use point from rTempPoint for numerical reasons. This + // will guarantee the usage of exactly the same point in different curves + aRetval.append(rTempPoint.getPoint()); + + // set new vectors for newly added point + aRetval.setControlPointA(nRetvalCount, aCubicBezier.getControlPointA()); + aRetval.setControlPointB(nRetvalCount, aCubicBezier.getControlPointB()); + } + } + } + else + { + // merge new polygon by indices + for(sal_uInt32 a(0L); a < nCount; a++) + { + // add original point + aRetval.append(rCandidate.getB2DPoint(a)); + + // add all points targeted to be at this index + while(nNewInd < rTempPoints.size() && rTempPoints[nNewInd].getIndex() == a) + { + const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; + const B2DPoint aNewPoint(rTempPoint.getPoint()); - if(nCount > 3L) + // do not add points double + if(!aRetval.getB2DPoint(aRetval.count() - 1L).equal(aNewPoint)) + { + aRetval.append(aNewPoint); + } + } + } + } + + aRetval.setClosed(rCandidate.isClosed()); + return aRetval; + } + else { - const double fZero(0.0); - const double fOne(1.0); - B2DPoint aCurrentA(rCandidate.getB2DPoint(0L)); + return rCandidate; + } + } + + //////////////////////////////////////////////////////////////////////////////// - for(sal_uInt32 a(0L); a < nCount - 1L; a++) + void adaptAndTransferCutsWithBezierSegment( + const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon, + sal_uInt32 nInd, temporaryPointVector& rTempPoints) + { + // assuming that the subdivision to create rPolygon used aequidistant pieces + // (as in adaptiveSubdivideByCount) it is now possible to calculate back the + // cut positions in the polygon to relative cut positions on the original bezier + // segment. + const sal_uInt32 nTempPointCount(rPointVector.size()); + const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1L : 0L); + + if(nTempPointCount && nEdgeCount) + { + for(sal_uInt32 a(0L); a < nTempPointCount; a++) { - // start edge is aCurrentA, aNextA - const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1L)); + const temporaryPoint& rTempPoint = rPointVector[a]; + const double fCutPosInPolygon((double)rTempPoint.getIndex() + rTempPoint.getCut()); + const double fRelativeCutPos(fCutPosInPolygon / (double)nEdgeCount); + rTempPoints.push_back(temporaryPoint(rTempPoint.getPoint(), nInd, fRelativeCutPos)); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// - if(!aCurrentA.equal(aNextA)) + } // end of anonymous namespace +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + namespace + { + //////////////////////////////////////////////////////////////////////////////// + // predefines for calls to this methods before method implementation + + void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints); + void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints); + void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB); + + //////////////////////////////////////////////////////////////////////////////// + + void findEdgeCutsTwoEdges( + const B2DPoint& rCurrA, const B2DPoint& rNextA, + const B2DPoint& rCurrB, const B2DPoint& rNextB, + sal_uInt32 nIndA, sal_uInt32 nIndB, + temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) + { + // no null length edges + if(!(rCurrA.equal(rNextA) || rCurrB.equal(rNextB))) + { + // no common start/end points, this can be no cuts + if(!(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA))) + { + const B2DVector aVecA(rNextA - rCurrA); + const B2DVector aVecB(rNextB - rCurrB); + double fCut(aVecA.cross(aVecB)); + + if(!fTools::equalZero(fCut)) { - const B2DVector aVecA(aNextA - aCurrentA); - const B2DRange aRangeA(aCurrentA, aNextA); - B2DPoint aCurrentB(aNextA); + const double fZero(0.0); + const double fOne(1.0); + fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.getY())) / fCut; - for(sal_uInt32 b(a + 1L); b < nCount; b++) + if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne)) { - // compare edge is aCurrentB, aNextB - const sal_uInt32 nNextIndB(tools::getIndexOfSuccessor(b, rCandidate)); - const B2DPoint aNextB(rCandidate.getB2DPoint(nNextIndB)); + // it's a candidate, but also need to test parameter value of cut on line 2 + double fCut2; + + // choose the more precise version + if(fabs(aVecB.getX()) > fabs(aVecB.getY())) + { + fCut2 = (rCurrA.getX() + (fCut * aVecA.getX()) - rCurrB.getX()) / aVecB.getX(); + } + else + { + fCut2 = (rCurrA.getY() + (fCut * aVecA.getY()) - rCurrB.getY()) / aVecB.getY(); + } + + if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne)) + { + // cut is in range, add point. Two edges can have only one cut, but + // add a cut point to each list. The lists may be the same for + // self intersections. + const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut)); + rTempPointsA.push_back(temporaryPoint(aCutPoint, nIndA, fCut)); + rTempPointsB.push_back(temporaryPoint(aCutPoint, nIndB, fCut2)); + } + } + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void findEdgeCutsBezierAndEdge( + const B2DCubicBezier& rCubicA, + const B2DPoint& rCurrB, const B2DPoint& rNextB, + sal_uInt32 nIndA, sal_uInt32 nIndB, + temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) + { + // find all cuts between given bezier segment and edge. Add an entry to the tempPoints + // for each common point with the cut value describing the relative position on given + // bezier segment and edge. + B2DPolygon aTempPolygonA; + B2DPolygon aTempPolygonEdge; + temporaryPointVector aTempPointVectorA; + temporaryPointVector aTempPointVectorEdge; + + // create subdivided polygons and find cuts between them + rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT, true); + aTempPolygonEdge.append(rCurrB); + aTempPolygonEdge.append(rNextB); + findCuts(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge); + + if(aTempPointVectorA.size()) + { + // adapt tempVector entries to segment + adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA); + } + + // append remapped tempVector entries for edge to tempPoints for edge + for(sal_uInt32 a(0L); a < aTempPointVectorEdge.size(); a++) + { + const temporaryPoint& rTempPoint = aTempPointVectorEdge[a]; + rTempPointsB.push_back(temporaryPoint(rTempPoint.getPoint(), nIndB, rTempPoint.getCut())); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void findEdgeCutsTwoBeziers( + const B2DCubicBezier& rCubicA, + const B2DCubicBezier& rCubicB, + sal_uInt32 nIndA, sal_uInt32 nIndB, + temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) + { + // find all cuts between the two given bezier segments. Add an entry to the tempPoints + // for each common point with the cut value describing the relative position on given + // bezier segments. + B2DPolygon aTempPolygonA; + B2DPolygon aTempPolygonB; + temporaryPointVector aTempPointVectorA; + temporaryPointVector aTempPointVectorB; + + // create subdivided polygons and find cuts between them + rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT, true); + rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT, true); + findCuts(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB); + + if(aTempPointVectorA.size()) + { + // adapt tempVector entries to segment + adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA); + } + + if(aTempPointVectorB.size()) + { + // adapt tempVector entries to segment + adaptAndTransferCutsWithBezierSegment(aTempPointVectorB, aTempPolygonB, nIndB, rTempPointsB); + } + } - if(!aCurrentB.equal(aNextB)) + //////////////////////////////////////////////////////////////////////////////// + + void findEdgeCutsOneBezier( + const B2DCubicBezier& rCubicA, + sal_uInt32 nInd, temporaryPointVector& rTempPoints) + { + // find all self-intersections on the given bezier segment. Add an entry to the tempPoints + // for each self intersection point with the cut value describing the relative position on given + // bezier segment. + B2DPolygon aTempPolygon; + temporaryPointVector aTempPointVector; + + // create subdivided polygon and find cuts on it + rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT, true); + findCuts(aTempPolygon, aTempPointVector); + + if(aTempPointVector.size()) + { + // adapt tempVector entries to segment + adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) + { + // find out if there are edges with intersections (self-cuts). If yes, add + // entries to rTempPoints accordingly + const sal_uInt32 nPointCount(rCandidate.count()); + + if(nPointCount) + { + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); + + if(nEdgeCount) + { + const bool bCurvesInvolved(rCandidate.areControlPointsUsed()); + + if(bCurvesInvolved) + { + for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++) + { + const B2DCubicBezier aCubicA( + rCandidate.getB2DPoint(a), + rCandidate.getControlVectorA(a), + rCandidate.getControlVectorB(a), + rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L)); + const bool bEdgeAIsCurve(aCubicA.isBezier()); + const B2DRange aRangeA(aCubicA.getRange()); + + for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++) { - const B2DRange aRangeB(aCurrentB, aNextB); + const B2DCubicBezier aCubicB( + rCandidate.getB2DPoint(b), + rCandidate.getControlVectorA(b), + rCandidate.getControlVectorB(b), + rCandidate.getB2DPoint(b + 1L == nPointCount ? 0L : b + 1L)); + const bool bEdgeBIsCurve(aCubicB.isBezier()); + const B2DRange aRangeB(aCubicB.getRange()); if(aRangeA.overlaps(aRangeB)) { - if(!(aCurrentB.equal(aCurrentA) || aCurrentB.equal(aNextA) || aNextB.equal(aCurrentA) || aNextB.equal(aNextA))) + if(bEdgeAIsCurve && bEdgeBIsCurve) { - const B2DVector aVecB(aNextB - aCurrentB); - double fCut(aVecA.cross(aVecB)); - - if(!fTools::equalZero(fCut)) - { - fCut = (aVecB.getY() * (aCurrentB.getX() - aCurrentA.getX()) + aVecB.getX() * (aCurrentA.getY() - aCurrentB.getY())) / fCut; - - if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne)) - { - // it's a candidate, but also need to test parameter value - // of cut on line 2 - double fCut2; - - // choose the more precise version - if(fabs(aVecB.getX()) > fabs(aVecB.getY())) - { - fCut2 = (aCurrentA.getX() + (fCut * aVecA.getX()) - aCurrentB.getX()) / aVecB.getX(); - } - else - { - fCut2 = (aCurrentA.getY() + (fCut * aVecA.getY()) - aCurrentB.getY()) / aVecB.getY(); - } - - if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne)) - { - B2DPoint aCutPoint(interpolate(aCurrentA, aNextA, fCut)); - rTempPoints.push_back(temporaryPoint(aCurrentA, aCutPoint, a)); - rTempPoints.push_back(temporaryPoint(aCurrentB, aCutPoint, b)); - } - } - } + // test for bezier-bezier cuts + findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPoints, rTempPoints); + } + else if(bEdgeAIsCurve) + { + // test for bezier-edge cuts + findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPoints, rTempPoints); + } + else if(bEdgeBIsCurve) + { + // test for bezier-edge cuts + findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPoints, rTempPoints); + } + else + { + // test for simple edge-edge cuts + findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), + a, b, rTempPoints, rTempPoints); } } } + } + } + else + { + B2DPoint aCurrA(rCandidate.getB2DPoint(0L)); + + for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++) + { + const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L)); + const B2DRange aRangeA(aCurrA, aNextA); + B2DPoint aCurrB(rCandidate.getB2DPoint(a + 1L)); - // prepare next compare edge - aCurrentB = aNextB; + for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++) + { + const B2DPoint aNextB(rCandidate.getB2DPoint(b + 1L == nPointCount ? 0L : b + 1L)); + const B2DRange aRangeB(aCurrB, aNextB); + + if(aRangeA.overlaps(aRangeB)) + { + findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPoints, rTempPoints); + } + + // prepare next step + aCurrB = aNextB; + } + + // prepare next step + aCurrA = aNextA; } } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + + } // end of anonymous namespace +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + namespace + { + //////////////////////////////////////////////////////////////////////////////// + + void findTouchesOnEdge( + const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon, + sal_uInt32 nInd, temporaryPointVector& rTempPoints) + { + // find out if points from rPointPolygon are positioned on given edge. If Yes, add + // points there to represent touches (which may be enter or leave nodes later). + const sal_uInt32 nPointCount(rPointPolygon.count()); + + if(nPointCount) + { + const B2DRange aRange(rCurr, rNext); + const B2DVector aEdgeVector(rNext - rCurr); + bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())); + + for(sal_uInt32 a(0L); a < nPointCount; a++) + { + const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(a)); + + if(aRange.isInside(aTestPoint)) + { + if(!aTestPoint.equal(rCurr) && !aTestPoint.equal(rNext)) + { + const B2DVector aTestVector(aTestPoint - rCurr); + + if(areParallel(aEdgeVector, aTestVector)) + { + const double fCut((bTestUsingX) + ? aTestVector.getX() / aEdgeVector.getX() + : aTestVector.getY() / aEdgeVector.getY()); + const double fZero(0.0); + const double fOne(1.0); - // prepare next start edge - aCurrentA = aNextA; + if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne)) + { + rTempPoints.push_back(temporaryPoint(aTestPoint, nInd, fCut)); + } + } + } + } } } } + //////////////////////////////////////////////////////////////////////////////// + + void findTouchesOnCurve( + const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon, + sal_uInt32 nInd, temporaryPointVector& rTempPoints) + { + // find all points from rPointPolygon which touch the given bezier segment. Add an entry + // for each touch to the given pointVector. The cut for that entry is the relative position on + // the given bezier segment. + B2DPolygon aTempPolygon; + temporaryPointVector aTempPointVector; + + // create subdivided polygon and find cuts on it + rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT, true); + findTouches(aTempPolygon, rPointPolygon, aTempPointVector); + + if(aTempPointVector.size()) + { + // adapt tempVector entries to segment + adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints); + } + } + + //////////////////////////////////////////////////////////////////////////////// + void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints) { // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes, - // add entries to rTempPoints which should be added to rEdgePolygon - const sal_uInt32 nEdgeCount(rEdgePolygon.count()); + // add entries to rTempPoints const sal_uInt32 nPointCount(rPointPolygon.count()); + const sal_uInt32 nEdgePointCount(rEdgePolygon.count()); - if(nEdgeCount && nPointCount) + if(nPointCount && nEdgePointCount) { - const double fZero(0.0); - const double fOne(1.0); - B2DPoint aCurrent(rEdgePolygon.getB2DPoint(0L)); + const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1L); for(sal_uInt32 a(0L); a < nEdgeCount; a++) { - // edge is aCurrent, aNext - const sal_uInt32 nNextInd(tools::getIndexOfSuccessor(a, rEdgePolygon)); - const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextInd)); + const B2DPoint aCurr(rEdgePolygon.getB2DPoint(a)); + const B2DPoint aNext(rEdgePolygon.getB2DPoint(a + 1L == nEdgePointCount ? 0L : a + 1L)); - if(!aNext.equal(aCurrent)) + if(!aCurr.equal(aNext)) { - const B2DRange aRange(aCurrent, aNext); + bool bHandleAsSimpleEdge(true); - if(aCurrent != aNext) + if(rEdgePolygon.areControlPointsUsed()) { - const B2DVector aEdgeVector(aNext - aCurrent); - bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())); + const B2DVector aCVecA(rEdgePolygon.getControlVectorA(a)); + const B2DVector aCVecB(rEdgePolygon.getControlVectorB(a)); + const bool bEdgeIsCurve(!aCVecA.equalZero() || !aCVecB.equalZero()); - for(sal_uInt32 b(0L); b < nPointCount; b++) + if(bEdgeIsCurve) { - const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(b)); - - if(aRange.isInside(aTestPoint)) - { - if(!(aTestPoint.equal(aCurrent) || aTestPoint.equal(aNext))) - { - const B2DVector aTestVector(aTestPoint - aCurrent); - - if(areParallel(aEdgeVector, aTestVector)) - { - const double fParamTestOnCurr((bTestUsingX) - ? aTestVector.getX() / aEdgeVector.getX() - : aTestVector.getY() / aEdgeVector.getY()); - - if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne)) - { - rTempPoints.push_back(temporaryPoint(aCurrent, aTestPoint, a)); - } - } - } - } + bHandleAsSimpleEdge = false; + const B2DCubicBezier aCubicA(aCurr, aCVecA, aCVecB, aNext); + findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints); } } - } - // prepare next edge - aCurrent = aNext; + if(bHandleAsSimpleEdge) + { + findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints); + } + } } } } + //////////////////////////////////////////////////////////////////////////////// + + } // end of anonymous namespace +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + namespace + { + //////////////////////////////////////////////////////////////////////////////// + void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) { // find out if edges from both polygons cut. If so, add entries to rTempPoints which // should be added to the polygons accordingly - const sal_uInt32 nCountA(rCandidateA.count()); - const sal_uInt32 nCountB(rCandidateB.count()); + const sal_uInt32 nPointCountA(rCandidateA.count()); + const sal_uInt32 nPointCountB(rCandidateB.count()); - if(nCountA && nCountB) + if(nPointCountA && nPointCountB) { - const double fZero(0.0); - const double fOne(1.0); - B2DPoint aCurrentA(rCandidateA.getB2DPoint(0L)); + const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1L); + const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1L); - for(sal_uInt32 a(0L); a < nCountA; a++) + if(nEdgeCountA && nEdgeCountB) { - // edge A is aCurrentA, aNextA - const sal_uInt32 nNextIndA(tools::getIndexOfSuccessor(a, rCandidateA)); - const B2DPoint aNextA(rCandidateA.getB2DPoint(nNextIndA)); + const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed()); - if(!aCurrentA.equal(aNextA)) + if(bCurvesInvolved) { - const B2DVector aVecA(aNextA - aCurrentA); - const B2DRange aRangeA(aCurrentA, aNextA); - B2DPoint aCurrentB(rCandidateB.getB2DPoint(0L)); - - for(sal_uInt32 b(0L); b < nCountB; b++) + for(sal_uInt32 a(0L); a < nEdgeCountA; a++) { - // edge B is aCurrentB, aNextB - const sal_uInt32 nNextIndB(tools::getIndexOfSuccessor(b, rCandidateB)); - const B2DPoint aNextB(rCandidateB.getB2DPoint(nNextIndB)); - - if(!aCurrentB.equal(aNextB)) + const B2DCubicBezier aCubicA( + rCandidateA.getB2DPoint(a), + rCandidateA.getControlVectorA(a), + rCandidateA.getControlVectorB(a), + rCandidateA.getB2DPoint(a + 1L == nPointCountA ? 0L : a + 1L)); + const bool bEdgeAIsCurve(aCubicA.isBezier()); + const B2DRange aRangeA(aCubicA.getRange()); + + for(sal_uInt32 b(0L); b < nEdgeCountB; b++) { - const B2DRange aRangeB(aCurrentB, aNextB); + const B2DCubicBezier aCubicB( + rCandidateB.getB2DPoint(b), + rCandidateB.getControlVectorA(b), + rCandidateB.getControlVectorB(b), + rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L)); + const bool bEdgeBIsCurve(aCubicB.isBezier()); + const B2DRange aRangeB(aCubicB.getRange()); if(aRangeA.overlaps(aRangeB)) { - if(!(aCurrentB.equal(aCurrentA) || aCurrentB.equal(aNextA) || aNextB.equal(aCurrentA) || aNextB.equal(aNextA))) + if(bEdgeAIsCurve && bEdgeBIsCurve) + { + // test for bezier-bezier cuts + findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB); + } + else if(bEdgeAIsCurve) { - const B2DVector aVecB(aNextB - aCurrentB); - double fCut(aVecA.cross(aVecB)); - - if(!fTools::equalZero(fCut)) - { - fCut = (aVecB.getY() * (aCurrentB.getX() - aCurrentA.getX()) + aVecB.getX() * (aCurrentA.getY() - aCurrentB.getY())) / fCut; - - if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne)) - { - // it's a candidate, but also need to test parameter value - // of cut on line 2 - double fCut2; - - // choose the more precise version - if(fabs(aVecB.getX()) > fabs(aVecB.getY())) - { - fCut2 = (aCurrentA.getX() + (fCut * aVecA.getX()) - aCurrentB.getX()) / aVecB.getX(); - } - else - { - fCut2 = (aCurrentA.getY() + (fCut * aVecA.getY()) - aCurrentB.getY()) / aVecB.getY(); - } - - if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne)) - { - B2DPoint aCutPoint(interpolate(aCurrentA, aNextA, fCut)); - rTempPointsA.push_back(temporaryPoint(aCurrentA, aCutPoint, a)); - rTempPointsB.push_back(temporaryPoint(aCurrentB, aCutPoint, b)); - } - } - } + // test for bezier-edge cuts + findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPointsA, rTempPointsB); + } + else if(bEdgeBIsCurve) + { + // test for bezier-edge cuts + findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPointsB, rTempPointsA); + } + else + { + // test for simple edge-edge cuts + findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), + a, b, rTempPointsA, rTempPointsB); } } } - - // prepare next edge - aCurrentB = aNextB; } } + else + { + B2DPoint aCurrA(rCandidateA.getB2DPoint(0L)); - // prepare next edge - aCurrentA = aNextA; - } - } - } + for(sal_uInt32 a(0L); a < nEdgeCountA; a++) + { + const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L == nPointCountA ? 0L : a + 1L)); + const B2DRange aRangeA(aCurrA, aNextA); + B2DPoint aCurrB(rCandidateB.getB2DPoint(0L)); - B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) - { - if(rTempPoints.size()) - { - // add found cut and touch points - B2DPolygon aRetval; - sal_uInt32 nNewInd(0L); - const sal_uInt32 nCount(rCandidate.count()); + for(sal_uInt32 b(0L); b < nEdgeCountB; b++) + { + const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L)); + const B2DRange aRangeB(aCurrB, aNextB); - // first sort by indices - ::std::sort(rTempPoints.begin(), rTempPoints.end()); + if(aRangeA.overlaps(aRangeB)) + { + // test for simple edge-edge cuts + findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB); + } - // now merge new polygon by indices - for(sal_uInt32 a(0L); a < nCount; a++) - { - // first add original point - aRetval.append(rCandidate.getB2DPoint(a)); + // prepare next step + aCurrB = aNextB; + } - // now add all points targeted to be at this index - while(nNewInd < rTempPoints.size() && rTempPoints[nNewInd].getIndex() == a) - { - aRetval.append(rTempPoints[nNewInd++].getPoint()); + // prepare next step + aCurrA = aNextA; + } } } - - return aRetval; - } - else - { - return rCandidate; } } + + //////////////////////////////////////////////////////////////////////////////// + } // end of anonymous namespace } // end of namespace basegfx @@ -392,33 +765,18 @@ namespace basegfx { namespace tools { + //////////////////////////////////////////////////////////////////////////////// + B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate) { - if(rCandidate.areControlPointsUsed()) + if(rCandidate.count()) { - OSL_ENSURE(false, "addPointsAtCutsAndTouches: works not for curves (!)"); - B2DPolygon aCandidate = tools::adaptiveSubdivideByAngle(rCandidate); - return addPointsAtCutsAndTouches(aCandidate); - } + temporaryPointVector aTempPoints; - const sal_uInt32 nCount(rCandidate.count()); - temporaryPointVector aTempPoints; - - if(nCount > 2L) - { findTouches(rCandidate, rCandidate, aTempPoints); - } - - if(nCount > 3L) - { findCuts(rCandidate, aTempPoints); - } - - if(aTempPoints.size()) - { - B2DPolygon aRetval(mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints)); - return aRetval; + return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); } else { @@ -426,81 +784,132 @@ namespace basegfx } } - B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate) - { - if(rCandidate.areControlPointsUsed()) - { - OSL_ENSURE(false, "addPointsAtCutsAndTouches: works not for curves (!)"); - B2DPolyPolygon aCandidate = tools::adaptiveSubdivideByAngle(rCandidate); - return addPointsAtCutsAndTouches(aCandidate); - } + //////////////////////////////////////////////////////////////////////////////// + B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections) + { const sal_uInt32 nCount(rCandidate.count()); - B2DPolyPolygon aRetval; - if(1L == nCount) + if(nCount) { - // only one Polygon contained, solve self cuts and self touches - const B2DPolygon aInput(rCandidate.getB2DPolygon(0L)); - const B2DPolygon aResult(addPointsAtCutsAndTouches(aInput)); + B2DPolyPolygon aRetval; - aRetval.append(aResult); - } - else - { - // first solve self cuts and self touches for all contained single polygons - temporaryPolygonData *pTempData = new temporaryPolygonData[nCount]; - sal_uInt32 a, b; - - for(a = 0L; a < nCount; a++) + if(1L == nCount) { - pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a))); + if(bSelfIntersections) + { + // remove self intersections + aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0L))); + } + else + { + // copy source + aRetval = rCandidate; + } } - - // now cuts and touches between the polygons - for(a = 0L; a < nCount; a++) + else { - for(b = 0L; b < nCount; b++) + // first solve self cuts and self touches for all contained single polygons + temporaryPolygonData *pTempData = new temporaryPolygonData[nCount]; + sal_uInt32 a, b; + + for(a = 0L; a < nCount; a++) { - if(a != b) + if(bSelfIntersections) { - // look for touches, compare each edge polygon to all other points - if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) - { - findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector()); - } + // use polygons with solved self intersections + pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a))); } + else + { + // copy given polygons + pTempData[a].setPolygon(rCandidate.getB2DPolygon(a)); + } + } - if(a < b) + // now cuts and touches between the polygons + for(a = 0L; a < nCount; a++) + { + for(b = 0L; b < nCount; b++) { - // look for cuts, compare each edge polygon to following ones - if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) + if(a != b) { - findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector()); + // look for touches, compare each edge polygon to all other points + if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) + { + findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector()); + } + } + + if(a < b) + { + // look for cuts, compare each edge polygon to following ones + if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) + { + findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector()); + } } } } - } - // consolidate the result - for(a = 0L; a < nCount; a++) - { - if(pTempData[a].getTemporaryPointVector().size()) + // consolidate the result + for(a = 0L; a < nCount; a++) { aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector())); } - else - { - aRetval.append(pTempData[a].getPolygon()); - } + + delete[] pTempData; } - delete[] pTempData; + return aRetval; + } + else + { + return rCandidate; + } + } + + //////////////////////////////////////////////////////////////////////////////// + + B2DPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolygon& rCandidate) + { + if(rCandidate.count()) + { + temporaryPointVector aTempPoints; + temporaryPointVector aTempPointsUnused; + + for(sal_uInt32 a(0L); a < rMask.count(); a++) + { + const B2DPolygon aPartMask(rMask.getB2DPolygon(a)); + + findTouches(rCandidate, aPartMask, aTempPoints); + findCuts(rCandidate, aPartMask, aTempPoints, aTempPointsUnused); + } + + return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); + } + else + { + return rCandidate; + } + } + + //////////////////////////////////////////////////////////////////////////////// + + B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolyPolygon& rCandidate) + { + B2DPolyPolygon aRetval; + + for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + { + aRetval.append(addPointsAtCutsAndTouches(rMask, rCandidate.getB2DPolygon(a))); } return aRetval; } + //////////////////////////////////////////////////////////////////////////////// + } // end of namespace tools } // end of namespace basegfx |