| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_basegfx.hxx" |
| #include <basegfx/polygon/b2dpolygoncutandtouch.hxx> |
| #include <osl/diagnose.h> |
| #include <basegfx/numeric/ftools.hxx> |
| #include <basegfx/point/b2dpoint.hxx> |
| #include <basegfx/vector/b2dvector.hxx> |
| #include <basegfx/range/b2drange.hxx> |
| #include <basegfx/polygon/b2dpolygontools.hxx> |
| #include <basegfx/polygon/b2dpolypolygontools.hxx> |
| #include <basegfx/curve/b2dcubicbezier.hxx> |
| |
| #include <vector> |
| #include <algorithm> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // defines |
| |
| #define SUBDIVIDE_FOR_CUT_TEST_COUNT (50) |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace basegfx |
| { |
| namespace |
| { |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class temporaryPoint |
| { |
| 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& rNewPoint, sal_uInt32 nIndex, double fCut) |
| : maPoint(rNewPoint), |
| mnIndex(nIndex), |
| mfCut(fCut) |
| { |
| } |
| |
| bool operator<(const temporaryPoint& rComp) const |
| { |
| if(mnIndex == rComp.mnIndex) |
| { |
| return (mfCut < rComp.mfCut); |
| } |
| |
| return (mnIndex < rComp.mnIndex); |
| } |
| |
| const B2DPoint& getPoint() const { return maPoint; } |
| sal_uInt32 getIndex() const { return mnIndex; } |
| double getCut() const { return mfCut; } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| typedef ::std::vector< temporaryPoint > temporaryPointVector; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class temporaryPolygonData |
| { |
| B2DPolygon maPolygon; |
| B2DRange maRange; |
| temporaryPointVector maPoints; |
| |
| public: |
| const B2DPolygon& getPolygon() const { return maPolygon; } |
| void setPolygon(const B2DPolygon& rNew) { maPolygon = rNew; maRange = tools::getRange(maPolygon); } |
| const B2DRange& getRange() const { return maRange; } |
| temporaryPointVector& getTemporaryPointVector() { return maPoints; } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) |
| { |
| // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle |
| // single edges with/without control points |
| // #i101491# added counter for non-changing element count |
| const sal_uInt32 nTempPointCount(rTempPoints.size()); |
| |
| if(nTempPointCount) |
| { |
| B2DPolygon aRetval; |
| const sal_uInt32 nCount(rCandidate.count()); |
| |
| if(nCount) |
| { |
| // sort temp points to assure increasing fCut values and increasing indices |
| ::std::sort(rTempPoints.begin(), rTempPoints.end()); |
| |
| // prepare loop |
| B2DCubicBezier aEdge; |
| sal_uInt32 nNewInd(0L); |
| |
| // add start point |
| aRetval.append(rCandidate.getB2DPoint(0)); |
| |
| for(sal_uInt32 a(0L); a < nCount; a++) |
| { |
| // get edge |
| rCandidate.getBezierSegment(a, aEdge); |
| |
| if(aEdge.isBezier()) |
| { |
| // control vectors involved for this edge |
| double fLeftStart(0.0); |
| |
| // now add all points targeted to be at this index |
| while(nNewInd < nTempPointCount && 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)); |
| aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge); |
| fLeftStart = rTempPoint.getCut(); |
| |
| // add left bow |
| aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint()); |
| } |
| |
| // add remaining bow |
| aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint()); |
| } |
| else |
| { |
| // add all points targeted to be at this index |
| while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a) |
| { |
| const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; |
| const B2DPoint aNewPoint(rTempPoint.getPoint()); |
| |
| // do not add points double |
| if(!aRetval.getB2DPoint(aRetval.count() - 1L).equal(aNewPoint)) |
| { |
| aRetval.append(aNewPoint); |
| } |
| } |
| |
| // add edge end point |
| aRetval.append(aEdge.getEndPoint()); |
| } |
| } |
| } |
| |
| if(rCandidate.isClosed()) |
| { |
| // set closed flag and correct last point (which is added double now). |
| tools::closeWithGeometryChange(aRetval); |
| } |
| |
| return aRetval; |
| } |
| else |
| { |
| return rCandidate; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| 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++) |
| { |
| 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)); |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| } // 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 double fZero(0.0); |
| const double fOne(1.0); |
| fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.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 = (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 findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
| { |
| // #i76891# |
| // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers |
| // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the |
| // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or |
| // equal points of them. |
| // It would be possible to find the toches using findTouches(), but at last with commpn points |
| // the adding of cut points (temporary points) would fail. But for these temporarily adaptive |
| // subdivided bezier segments, common points may be not very likely, but the bug shows that it |
| // happens. |
| // Touch points are a little bit more likely than common points. All in all it is best to use |
| // a specialized method here which can profit from knowing that it is working on a special |
| // family of B2DPolygons: no curve segments included and not closed. |
| OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)"); |
| OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)"); |
| const sal_uInt32 nPointCountA(rCandidateA.count()); |
| const sal_uInt32 nPointCountB(rCandidateB.count()); |
| |
| if(nPointCountA > 1 && nPointCountB > 1) |
| { |
| const sal_uInt32 nEdgeCountA(nPointCountA - 1); |
| const sal_uInt32 nEdgeCountB(nPointCountB - 1); |
| B2DPoint aCurrA(rCandidateA.getB2DPoint(0L)); |
| |
| for(sal_uInt32 a(0L); a < nEdgeCountA; a++) |
| { |
| const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L)); |
| const B2DRange aRangeA(aCurrA, aNextA); |
| B2DPoint aCurrB(rCandidateB.getB2DPoint(0L)); |
| |
| for(sal_uInt32 b(0L); b < nEdgeCountB; b++) |
| { |
| const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L)); |
| const B2DRange aRangeB(aCurrB, aNextB); |
| |
| if(aRangeA.overlaps(aRangeB)) |
| { |
| // no null length edges |
| if(!(aCurrA.equal(aNextA) || aCurrB.equal(aNextB))) |
| { |
| const B2DVector aVecA(aNextA - aCurrA); |
| const B2DVector aVecB(aNextB - aCurrB); |
| double fCutA(aVecA.cross(aVecB)); |
| |
| if(!fTools::equalZero(fCutA)) |
| { |
| const double fZero(0.0); |
| const double fOne(1.0); |
| fCutA = (aVecB.getY() * (aCurrB.getX() - aCurrA.getX()) + aVecB.getX() * (aCurrA.getY() - aCurrB.getY())) / fCutA; |
| |
| // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered |
| // as 0.0 cut. The 1.0 cut will be registered in the next loop step |
| if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne)) |
| { |
| // it's a candidate, but also need to test parameter value of cut on line 2 |
| double fCutB; |
| |
| // choose the more precise version |
| if(fabs(aVecB.getX()) > fabs(aVecB.getY())) |
| { |
| fCutB = (aCurrA.getX() + (fCutA * aVecA.getX()) - aCurrB.getX()) / aVecB.getX(); |
| } |
| else |
| { |
| fCutB = (aCurrA.getY() + (fCutA * aVecA.getY()) - aCurrB.getY()) / aVecB.getY(); |
| } |
| |
| // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered |
| // as 0.0 cut. The 1.0 cut will be registered in the next loop step |
| if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne)) |
| { |
| // cut is in both ranges. Add points for A and B |
| // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy |
| if(fTools::equal(fCutA, fZero)) |
| { |
| // ignore for start point in first edge; this is handled |
| // by outer methods and would just produce a double point |
| if(a) |
| { |
| rTempPointsA.push_back(temporaryPoint(aCurrA, a, 0.0)); |
| } |
| } |
| else |
| { |
| const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA)); |
| rTempPointsA.push_back(temporaryPoint(aCutPoint, a, fCutA)); |
| } |
| |
| // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy |
| if(fTools::equal(fCutB, fZero)) |
| { |
| // ignore for start point in first edge; this is handled |
| // by outer methods and would just produce a double point |
| if(b) |
| { |
| rTempPointsB.push_back(temporaryPoint(aCurrB, b, 0.0)); |
| } |
| } |
| else |
| { |
| const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB)); |
| rTempPointsB.push_back(temporaryPoint(aCutPoint, b, fCutB)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // prepare next step |
| aCurrB = aNextB; |
| } |
| |
| // prepare next step |
| aCurrA = aNextA; |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| 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 |
| // Keep adaptiveSubdivideByCount due to needed quality |
| aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
| aTempPolygonA.append(rCubicA.getStartPoint()); |
| rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
| aTempPolygonEdge.append(rCurrB); |
| aTempPolygonEdge.append(rNextB); |
| |
| // #i76891# using findCuts recursively is not sufficient here |
| findCutsAndTouchesAndCommonForBezier(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 |
| // Keep adaptiveSubdivideByCount due to needed quality |
| aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
| aTempPolygonA.append(rCubicA.getStartPoint()); |
| rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
| aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
| aTempPolygonB.append(rCubicB.getStartPoint()); |
| rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
| |
| // #i76891# using findCuts recursively is not sufficient here |
| findCutsAndTouchesAndCommonForBezier(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); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| void findEdgeCutsOneBezier( |
| const B2DCubicBezier& rCubicA, |
| sal_uInt32 nInd, temporaryPointVector& rTempPoints) |
| { |
| // avoid expensive part of this method if possible |
| // TODO: use hasAnyExtremum() method instead when it becomes available |
| double fDummy; |
| const bool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy ); |
| if( !bHasAnyExtremum ) |
| return; |
| |
| // 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 |
| // Keep adaptiveSubdivideByCount due to needed quality |
| aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
| aTempPolygon.append(rCubicA.getStartPoint()); |
| rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
| 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) |
| { |
| B2DCubicBezier aCubicA; |
| B2DCubicBezier aCubicB; |
| |
| for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++) |
| { |
| rCandidate.getBezierSegment(a, aCubicA); |
| aCubicA.testAndSolveTrivialBezier(); |
| const bool bEdgeAIsCurve(aCubicA.isBezier()); |
| const B2DRange aRangeA(aCubicA.getRange()); |
| |
| if(bEdgeAIsCurve) |
| { |
| // curved segments may have self-intersections, do not forget those (!) |
| findEdgeCutsOneBezier(aCubicA, a, rTempPoints); |
| } |
| |
| for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++) |
| { |
| rCandidate.getBezierSegment(b, aCubicB); |
| aCubicB.testAndSolveTrivialBezier(); |
| const bool bEdgeBIsCurve(aCubicB.isBezier()); |
| const B2DRange aRangeB(aCubicB.getRange()); |
| |
| // only overlapping segments need to be tested |
| // consecutive segments touch of course |
| bool bOverlap = false; |
| if( b > a+1) |
| bOverlap = aRangeA.overlaps(aRangeB); |
| else |
| bOverlap = aRangeA.overlapsMore(aRangeB); |
| if( bOverlap) |
| { |
| if(bEdgeAIsCurve && bEdgeBIsCurve) |
| { |
| // 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)); |
| |
| 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); |
| |
| // consecutive segments touch of course |
| bool bOverlap = false; |
| if( b > a+1) |
| bOverlap = aRangeA.overlaps(aRangeB); |
| else |
| bOverlap = aRangeA.overlapsMore(aRangeB); |
| if( bOverlap) |
| { |
| 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); |
| B2DVector aNormalizedEdgeVector(aEdgeVector); |
| aNormalizedEdgeVector.normalize(); |
| 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(aNormalizedEdgeVector, aTestVector)) |
| { |
| const double fCut((bTestUsingX) |
| ? aTestVector.getX() / aEdgeVector.getX() |
| : aTestVector.getY() / aEdgeVector.getY()); |
| const double fZero(0.0); |
| const double fOne(1.0); |
| |
| 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 |
| // Keep adaptiveSubdivideByCount due to needed quality |
| aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
| aTempPolygon.append(rCubicA.getStartPoint()); |
| rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
| 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 |
| const sal_uInt32 nPointCount(rPointPolygon.count()); |
| const sal_uInt32 nEdgePointCount(rEdgePolygon.count()); |
| |
| if(nPointCount && nEdgePointCount) |
| { |
| const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1L); |
| B2DPoint aCurr(rEdgePolygon.getB2DPoint(0)); |
| |
| for(sal_uInt32 a(0L); a < nEdgeCount; a++) |
| { |
| const sal_uInt32 nNextIndex((a + 1) % nEdgePointCount); |
| const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextIndex)); |
| |
| if(!aCurr.equal(aNext)) |
| { |
| bool bHandleAsSimpleEdge(true); |
| |
| if(rEdgePolygon.areControlPointsUsed()) |
| { |
| const B2DPoint aNextControlPoint(rEdgePolygon.getNextControlPoint(a)); |
| const B2DPoint aPrevControlPoint(rEdgePolygon.getPrevControlPoint(nNextIndex)); |
| const bool bEdgeIsCurve(!aNextControlPoint.equal(aCurr) || !aPrevControlPoint.equal(aNext)); |
| |
| if(bEdgeIsCurve) |
| { |
| bHandleAsSimpleEdge = false; |
| const B2DCubicBezier aCubicA(aCurr, aNextControlPoint, aPrevControlPoint, aNext); |
| findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints); |
| } |
| } |
| |
| if(bHandleAsSimpleEdge) |
| { |
| findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints); |
| } |
| } |
| |
| // next step |
| aCurr = aNext; |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| } // 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 nPointCountA(rCandidateA.count()); |
| const sal_uInt32 nPointCountB(rCandidateB.count()); |
| |
| if(nPointCountA && nPointCountB) |
| { |
| const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1L); |
| const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1L); |
| |
| if(nEdgeCountA && nEdgeCountB) |
| { |
| const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed()); |
| |
| if(bCurvesInvolved) |
| { |
| B2DCubicBezier aCubicA; |
| B2DCubicBezier aCubicB; |
| |
| for(sal_uInt32 a(0L); a < nEdgeCountA; a++) |
| { |
| rCandidateA.getBezierSegment(a, aCubicA); |
| aCubicA.testAndSolveTrivialBezier(); |
| const bool bEdgeAIsCurve(aCubicA.isBezier()); |
| const B2DRange aRangeA(aCubicA.getRange()); |
| |
| for(sal_uInt32 b(0L); b < nEdgeCountB; b++) |
| { |
| rCandidateB.getBezierSegment(b, aCubicB); |
| aCubicB.testAndSolveTrivialBezier(); |
| const bool bEdgeBIsCurve(aCubicB.isBezier()); |
| const B2DRange aRangeB(aCubicB.getRange()); |
| |
| // consecutive segments touch of course |
| bool bOverlap = false; |
| if( b > a+1) |
| bOverlap = aRangeA.overlaps(aRangeB); |
| else |
| bOverlap = aRangeA.overlapsMore(aRangeB); |
| if( bOverlap) |
| { |
| if(bEdgeAIsCurve && bEdgeBIsCurve) |
| { |
| // test for bezier-bezier cuts |
| findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB); |
| } |
| else if(bEdgeAIsCurve) |
| { |
| // 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); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| B2DPoint aCurrA(rCandidateA.getB2DPoint(0L)); |
| |
| 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)); |
| |
| for(sal_uInt32 b(0L); b < nEdgeCountB; b++) |
| { |
| const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L)); |
| const B2DRange aRangeB(aCurrB, aNextB); |
| |
| // consecutive segments touch of course |
| bool bOverlap = false; |
| if( b > a+1) |
| bOverlap = aRangeA.overlaps(aRangeB); |
| else |
| bOverlap = aRangeA.overlapsMore(aRangeB); |
| if( bOverlap) |
| { |
| // test for simple edge-edge cuts |
| findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB); |
| } |
| |
| // prepare next step |
| aCurrB = aNextB; |
| } |
| |
| // prepare next step |
| aCurrA = aNextA; |
| } |
| } |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| } // end of anonymous namespace |
| } // end of namespace basegfx |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace basegfx |
| { |
| namespace tools |
| { |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate) |
| { |
| if(rCandidate.count()) |
| { |
| temporaryPointVector aTempPoints; |
| |
| findTouches(rCandidate, rCandidate, aTempPoints); |
| findCuts(rCandidate, aTempPoints); |
| |
| return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); |
| } |
| else |
| { |
| return rCandidate; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections) |
| { |
| const sal_uInt32 nCount(rCandidate.count()); |
| |
| if(nCount) |
| { |
| B2DPolyPolygon aRetval; |
| |
| if(1L == nCount) |
| { |
| if(bSelfIntersections) |
| { |
| // remove self intersections |
| aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0L))); |
| } |
| else |
| { |
| // copy source |
| aRetval = rCandidate; |
| } |
| } |
| 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(bSelfIntersections) |
| { |
| // use polygons with solved self intersections |
| pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a))); |
| } |
| else |
| { |
| // copy given polygons |
| pTempData[a].setPolygon(rCandidate.getB2DPolygon(a)); |
| } |
| } |
| |
| // now cuts and touches between the polygons |
| for(a = 0L; a < nCount; a++) |
| { |
| for(b = 0L; b < nCount; b++) |
| { |
| if(a != b) |
| { |
| // 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++) |
| { |
| aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector())); |
| } |
| |
| 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; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd) |
| { |
| const sal_uInt32 nCount(rCandidate.count()); |
| |
| if(nCount && !rStart.equal(rEnd)) |
| { |
| const B2DRange aPolygonRange(rCandidate.getB2DRange()); |
| const B2DRange aEdgeRange(rStart, rEnd); |
| |
| if(aPolygonRange.overlaps(aEdgeRange)) |
| { |
| const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1); |
| temporaryPointVector aTempPoints; |
| temporaryPointVector aUnusedTempPoints; |
| B2DCubicBezier aCubic; |
| |
| for(sal_uInt32 a(0); a < nEdgeCount; a++) |
| { |
| rCandidate.getBezierSegment(a, aCubic); |
| B2DRange aCubicRange(aCubic.getStartPoint(), aCubic.getEndPoint()); |
| |
| if(aCubic.isBezier()) |
| { |
| aCubicRange.expand(aCubic.getControlPointA()); |
| aCubicRange.expand(aCubic.getControlPointB()); |
| |
| if(aCubicRange.overlaps(aEdgeRange)) |
| { |
| findEdgeCutsBezierAndEdge(aCubic, rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints); |
| } |
| } |
| else |
| { |
| if(aCubicRange.overlaps(aEdgeRange)) |
| { |
| findEdgeCutsTwoEdges(aCubic.getStartPoint(), aCubic.getEndPoint(), rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints); |
| } |
| } |
| } |
| |
| return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); |
| } |
| } |
| |
| return rCandidate; |
| } |
| |
| B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd) |
| { |
| B2DPolyPolygon aRetval; |
| |
| for(sal_uInt32 a(0); a < rCandidate.count(); a++) |
| { |
| aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rStart, rEnd)); |
| } |
| |
| return aRetval; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rPolyMask) |
| { |
| const sal_uInt32 nCountA(rCandidate.count()); |
| const sal_uInt32 nCountM(rPolyMask.count()); |
| |
| if(nCountA && nCountM) |
| { |
| const B2DRange aRangeA(rCandidate.getB2DRange()); |
| const B2DRange aRangeM(rPolyMask.getB2DRange()); |
| |
| if(aRangeA.overlaps(aRangeM)) |
| { |
| const sal_uInt32 nEdgeCountA(rCandidate.isClosed() ? nCountA : nCountA - 1); |
| temporaryPointVector aTempPointsA; |
| temporaryPointVector aUnusedTempPointsB; |
| |
| for(sal_uInt32 m(0); m < nCountM; m++) |
| { |
| const B2DPolygon aMask(rPolyMask.getB2DPolygon(m)); |
| const sal_uInt32 nCountB(aMask.count()); |
| |
| if(nCountB) |
| { |
| B2DCubicBezier aCubicA; |
| B2DCubicBezier aCubicB; |
| |
| for(sal_uInt32 a(0); a < nEdgeCountA; a++) |
| { |
| rCandidate.getBezierSegment(a, aCubicA); |
| const bool bCubicAIsCurve(aCubicA.isBezier()); |
| B2DRange aCubicRangeA(aCubicA.getStartPoint(), aCubicA.getEndPoint()); |
| |
| if(bCubicAIsCurve) |
| { |
| aCubicRangeA.expand(aCubicA.getControlPointA()); |
| aCubicRangeA.expand(aCubicA.getControlPointB()); |
| } |
| |
| for(sal_uInt32 b(0); b < nCountB; b++) |
| { |
| aMask.getBezierSegment(b, aCubicB); |
| const bool bCubicBIsCurve(aCubicB.isBezier()); |
| B2DRange aCubicRangeB(aCubicB.getStartPoint(), aCubicB.getEndPoint()); |
| |
| if(bCubicBIsCurve) |
| { |
| aCubicRangeB.expand(aCubicB.getControlPointA()); |
| aCubicRangeB.expand(aCubicB.getControlPointB()); |
| } |
| |
| if(aCubicRangeA.overlaps(aCubicRangeB)) |
| { |
| if(bCubicAIsCurve && bCubicBIsCurve) |
| { |
| findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, aTempPointsA, aUnusedTempPointsB); |
| } |
| else if(bCubicAIsCurve) |
| { |
| findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB); |
| } |
| else if(bCubicBIsCurve) |
| { |
| findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, aUnusedTempPointsB, aTempPointsA); |
| } |
| else |
| { |
| findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return mergeTemporaryPointsAndPolygon(rCandidate, aTempPointsA); |
| } |
| } |
| |
| return rCandidate; |
| } |
| |
| B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rMask) |
| { |
| B2DPolyPolygon aRetval; |
| |
| for(sal_uInt32 a(0); a < rCandidate.count(); a++) |
| { |
| aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rMask)); |
| } |
| |
| return aRetval; |
| } |
| |
| B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate) |
| { |
| if(rCandidate.count()) |
| { |
| temporaryPointVector aTempPoints; |
| |
| findCuts(rCandidate, aTempPoints); |
| |
| return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); |
| } |
| else |
| { |
| return rCandidate; |
| } |
| } |
| |
| B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, bool bSelfIntersections) |
| { |
| const sal_uInt32 nCount(rCandidate.count()); |
| |
| if(nCount) |
| { |
| B2DPolyPolygon aRetval; |
| |
| if(1 == nCount) |
| { |
| if(bSelfIntersections) |
| { |
| // remove self intersections |
| aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(0))); |
| } |
| else |
| { |
| // copy source |
| aRetval = rCandidate; |
| } |
| } |
| else |
| { |
| // first solve self cuts for all contained single polygons |
| temporaryPolygonData *pTempData = new temporaryPolygonData[nCount]; |
| sal_uInt32 a, b; |
| |
| for(a = 0; a < nCount; a++) |
| { |
| if(bSelfIntersections) |
| { |
| // use polygons with solved self intersections |
| pTempData[a].setPolygon(addPointsAtCuts(rCandidate.getB2DPolygon(a))); |
| } |
| else |
| { |
| // copy given polygons |
| pTempData[a].setPolygon(rCandidate.getB2DPolygon(a)); |
| } |
| } |
| |
| // now cuts and touches between the polygons |
| for(a = 0; a < nCount; a++) |
| { |
| for(b = 0; b < nCount; b++) |
| { |
| 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++) |
| { |
| aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector())); |
| } |
| |
| delete[] pTempData; |
| } |
| |
| return aRetval; |
| } |
| else |
| { |
| return rCandidate; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| } // end of namespace tools |
| } // end of namespace basegfx |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |