From 0f5a32b442636086d34584ae8f98546870a666f1 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Wed, 17 Feb 2016 13:10:13 -0800 Subject: [PATCH] Additional Clipper changes through https://github.com/mapnik/clipper/commit/7484da1 --- clipper/clipper.cpp | 556 ++++++++++++++++++++++++-------------------- clipper/clipper.hpp | 46 ++-- 2 files changed, 327 insertions(+), 275 deletions(-) diff --git a/clipper/clipper.cpp b/clipper/clipper.cpp index fa5eefc..1625b0e 100644 --- a/clipper/clipper.cpp +++ b/clipper/clipper.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -66,12 +66,11 @@ static int const Skip = -2; //edge that would otherwise close a path struct TEdge { IntPoint Bot; - IntPoint Curr; + IntPoint Curr; //current (updated for every new scanbeam) IntPoint Top; - IntPoint Delta; double Dx; PolyType PolyTyp; - EdgeSide Side; + EdgeSide Side; //side only refers to current side of solution poly int WindDelta; //1 or -1 depending on winding direction int WindCnt; int WindCnt2; //winding count of the opposite polytype @@ -99,6 +98,8 @@ struct LocalMinimum { struct OutPt; +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. struct OutRec { int Idx; bool IsHole; @@ -403,19 +404,25 @@ double Area(const Path &poly) } //------------------------------------------------------------------------------ -double Area(const OutRec &outRec) +double Area(const OutPt *op) { - OutPt *op = outRec.Pts; + const OutPt *startOp = op; if (!op) return 0; double a = 0; do { a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); op = op->Next; - } while (op != outRec.Pts); + } while (op != startOp); return a * 0.5; } //------------------------------------------------------------------------------ +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + bool PointIsVertex(const IntPoint &Pt, OutPt *pp) { OutPt *pp2 = pp; @@ -536,10 +543,12 @@ bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) { #ifndef use_int32 if (UseFullInt64Range) - return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); else #endif - return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); } //------------------------------------------------------------------------------ @@ -569,7 +578,7 @@ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, inline bool IsHorizontal(TEdge &e) { - return e.Delta.Y == 0; + return e.Dx == HORIZONTAL; } //------------------------------------------------------------------------------ @@ -582,11 +591,9 @@ inline double GetDx(const IntPoint pt1, const IntPoint pt2) inline void SetDx(TEdge &e) { - e.Delta.X = (e.Top.X - e.Bot.X); - e.Delta.Y = (e.Top.Y - e.Bot.Y); - - if (e.Delta.Y == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Delta.X) / e.Delta.Y; + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; } //--------------------------------------------------------------------------- @@ -626,7 +633,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.X = TopX(Edge1, ip.Y); return; } - else if (Edge1.Delta.X == 0) + else if (Edge1.Dx == 0) { ip.X = Edge1.Bot.X; if (IsHorizontal(Edge2)) @@ -637,7 +644,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.Y = Round(ip.X / Edge2.Dx + b2); } } - else if (Edge2.Delta.X == 0) + else if (Edge2.Dx == 0) { ip.X = Edge2.Bot.X; if (IsHorizontal(Edge1)) @@ -804,7 +811,12 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) p = btmPt2->Next; while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); } //------------------------------------------------------------------------------ @@ -1191,8 +1203,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) locMin.RightBound = E->Prev; leftBoundIsForward = true; //Q.nextInLML = Q.next } - locMin.LeftBound->Side = esLeft; - locMin.RightBound->Side = esRight; if (!Closed) locMin.LeftBound->WindDelta = 0; else if (locMin.LeftBound->Next == locMin.RightBound) @@ -1231,8 +1241,6 @@ void ClipperBase::Clear() DisposeLocalMinimaList(); for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) { - //for each edge array in turn, find the first used edge and - //check for and remove any hiddenPts in each edge in the array. TEdge* edges = m_edges[i]; delete [] edges; } @@ -1248,9 +1256,11 @@ void ClipperBase::Reset() if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process std::stable_sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue //reset all edges ... for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) { + InsertScanbeam(lm->Y); TEdge* e = lm->LeftBound; if (e) { @@ -1267,6 +1277,8 @@ void ClipperBase::Reset() e->OutIdx = Unassigned; } } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); } //------------------------------------------------------------------------------ @@ -1277,10 +1289,12 @@ void ClipperBase::DisposeLocalMinimaList() } //------------------------------------------------------------------------------ -void ClipperBase::PopLocalMinima() +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) { - if (m_CurrentLM == m_MinimaList.end()) return; + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); ++m_CurrentLM; + return true; } //------------------------------------------------------------------------------ @@ -1299,6 +1313,7 @@ IntRect ClipperBase::GetBounds() result.bottom = lm->LeftBound->Bot.Y; while (lm != m_MinimaList.end()) { + //todo - needs fixing for open paths result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); TEdge* e = lm->LeftBound; for (;;) { @@ -1321,6 +1336,142 @@ IntRect ClipperBase::GetBounds() } return result; } +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} //------------------------------------------------------------------------------ // TClipper methods ... @@ -1328,8 +1479,6 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase() //constructor { - m_ActiveEdges = 0; - m_SortedEdges = 0; m_ExecuteLocked = false; m_UseFullRange = false; m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); @@ -1342,12 +1491,6 @@ Clipper::Clipper(int initOptions) : ClipperBase() //constructor } //------------------------------------------------------------------------------ -Clipper::~Clipper() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - #ifdef use_xyz void Clipper::ZFillFunction(ZFillCallback zFillFunc) { @@ -1356,18 +1499,6 @@ void Clipper::ZFillFunction(ZFillCallback zFillFunc) //------------------------------------------------------------------------------ #endif -void Clipper::Reset() -{ - ClipperBase::Reset(); - m_Scanbeam = ScanbeamList(); - m_Maxima = MaximaList(); - m_ActiveEdges = 0; - m_SortedEdges = 0; - for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) - InsertScanbeam(lm->Y); -} -//------------------------------------------------------------------------------ - bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) { return Execute(clipType, solution, fillType, fillType); @@ -1437,19 +1568,26 @@ bool Clipper::ExecuteInternal() bool succeeded = true; try { Reset(); - if (m_CurrentLM == m_MinimaList.end()) return true; - cInt botY = PopScanbeam(); - do { - InsertLocalMinimaIntoAEL(botY); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { ProcessHorizontals(); - ClearGhostJoins(); - if (m_Scanbeam.empty()) break; - cInt topY = PopScanbeam(); - succeeded = ProcessIntersections(topY); - if (!succeeded) break; + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } ProcessEdgesAtTopOfScanbeam(topY); botY = topY; - } while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end()); + InsertLocalMinimaIntoAEL(botY); + } } catch(std::exception const&) { @@ -1489,37 +1627,6 @@ bool Clipper::ExecuteInternal() } //------------------------------------------------------------------------------ -void Clipper::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.push(Y); -} -//------------------------------------------------------------------------------ - -cInt Clipper::PopScanbeam() -{ - const cInt Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return Y; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeAllOutRecs(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeOutRec(PolyOutList::size_type index) -{ - OutRec *outRec = m_PolyOuts[index]; - if (outRec->Pts) DisposeOutPts(outRec->Pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - void Clipper::SetWindingCount(TEdge &edge) { TEdge *e = edge.PrevInAEL; @@ -1527,7 +1634,13 @@ void Clipper::SetWindingCount(TEdge &edge) while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; if (!e) { - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; edge.WindCnt2 = 0; e = m_ActiveEdges; //ie get ready to calc WindCnt2 } @@ -1759,13 +1872,16 @@ OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) prevE = e->PrevInAEL; } - if (prevE && prevE->OutIdx >= 0 && - (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && - SlopesEqual(*e, *prevE, m_UseFullRange) && - (e->WindDelta != 0) && (prevE->WindDelta != 0)) + if (prevE && prevE->OutIdx >= 0) { - OutPt* outPt = AddOutPt(prevE, Pt); - AddJoin(result, outPt, e->Top); + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } } return result; } @@ -1807,6 +1923,15 @@ void Clipper::AddEdgeToSEL(TEdge *edge) } //------------------------------------------------------------------------------ +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + void Clipper::CopyAELToSEL() { TEdge* e = m_ActiveEdges; @@ -1858,11 +1983,12 @@ void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - while (m_CurrentLM != m_MinimaList.end() && (m_CurrentLM->Y == botY)) + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) { - TEdge* lb = m_CurrentLM->LeftBound; - TEdge* rb = m_CurrentLM->RightBound; - PopLocalMinima(); + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + OutPt *Op1 = 0; if (!lb) { @@ -1894,8 +2020,13 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (rb) { - if(IsHorizontal(*rb)) AddEdgeToSEL(rb); - else InsertScanbeam( rb->Top.Y ); + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); } if (!lb || !rb) continue; @@ -1917,7 +2048,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (lb->OutIdx >= 0 && lb->PrevInAEL && lb->PrevInAEL->Curr.X == lb->Bot.X && lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); @@ -1928,7 +2059,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); @@ -1952,19 +2083,6 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) } //------------------------------------------------------------------------------ -void Clipper::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted - if( AelPrev ) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if( AelNext ) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - void Clipper::DeleteFromSEL(TEdge *e) { TEdge* SelPrev = e->PrevInSEL; @@ -2188,19 +2306,27 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { - bool IsHole = false; TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; while (e2) { if (e2->OutIdx >= 0 && e2->WindDelta != 0) { - IsHole = !IsHole; - if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; } e2 = e2->PrevInAEL; } - if (IsHole) outrec->IsHole = true; + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } } //------------------------------------------------------------------------------ @@ -2224,7 +2350,7 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) } //------------------------------------------------------------------------------ -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) { do { @@ -2251,9 +2377,9 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; - if (Param1RightOfParam2(outRec1, outRec2)) + if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); @@ -2266,7 +2392,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutPt* p2_lft = outRec2->Pts; OutPt* p2_rt = p2_lft->Prev; - EdgeSide Side; //join e2 poly onto e1 poly and delete pointers to e2 ... if( e1->Side == esLeft ) { @@ -2288,7 +2413,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_rt->Next = p2_lft; outRec1->Pts = p2_lft; } - Side = esLeft; } else { if( e2->Side == esRight ) @@ -2307,7 +2431,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_lft->Prev = p2_rt; p2_rt->Next = p1_lft; } - Side = esRight; } outRec1->BottomPt = 0; @@ -2333,7 +2456,7 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) if( e->OutIdx == ObsoleteIdx ) { e->OutIdx = OKIdx; - e->Side = Side; + e->Side = e1->Side; break; } e = e->NextInAEL; @@ -2343,21 +2466,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) } //------------------------------------------------------------------------------ -OutRec* Clipper::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; -} -//------------------------------------------------------------------------------ - OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { if( e->OutIdx < 0 ) @@ -2409,13 +2517,9 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - TEdge* horzEdge = m_SortedEdges; - while(horzEdge) - { - DeleteFromSEL(horzEdge); + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) ProcessHorizontal(horzEdge); - horzEdge = m_SortedEdges; - } } //------------------------------------------------------------------------------ @@ -2439,64 +2543,21 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) TEdge *GetMaximaPair(TEdge *e) { - TEdge* result = 0; if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - result = e->Next; + return e->Next; else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - result = e->Prev; - - if (result && (result->OutIdx == Skip || - //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) - return 0; - return result; + return e->Prev; + else return 0; } //------------------------------------------------------------------------------ -void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +TEdge *GetMaximaPairEx(TEdge *e) { - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if( Edge1->NextInAEL == Edge2 ) - { - TEdge* Next = Edge2->NextInAEL; - if( Next ) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if( Edge2->NextInAEL == Edge1 ) - { - TEdge* Next = Edge1->NextInAEL; - if( Next ) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; - else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; } //------------------------------------------------------------------------------ @@ -2582,7 +2643,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + bool IsOpen = (horzEdge->WindDelta == 0); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2765,29 +2826,6 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) } //------------------------------------------------------------------------------ -void Clipper::UpdateEdgeIntoAEL(TEdge *&e) -{ - if( !e->NextInLML ) - throw clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); -} -//------------------------------------------------------------------------------ - bool Clipper::ProcessIntersections(const cInt topY) { if( !m_ActiveEdges ) return true; @@ -2845,6 +2883,7 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.X > eNext->Curr.X) { IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); IntersectNode * newNode = new IntersectNode; newNode->Edge1 = e; newNode->Edge2 = eNext; @@ -2919,7 +2958,7 @@ bool Clipper::FixupIntersectionOrder() void Clipper::DoMaxima(TEdge *e) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); if (!eMaxPair) { if (e->OutIdx >= 0) @@ -2980,7 +3019,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); } @@ -3052,7 +3091,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if (ePrev && ePrev->Curr.X == e->Bot.X && ePrev->Curr.Y == e->Bot.Y && op && ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*e, *ePrev, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && (e->WindDelta != 0) && (ePrev->WindDelta != 0)) { OutPt* op2 = AddOutPt(ePrev, e->Bot); @@ -3061,7 +3100,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) else if (eNext && eNext->Curr.X == e->Bot.X && eNext->Curr.Y == e->Bot.Y && op && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*e, *eNext, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && (e->WindDelta != 0) && (eNext->WindDelta != 0)) { OutPt* op2 = AddOutPt(eNext, e->Bot); @@ -3588,9 +3627,8 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (!outRec->Pts || !outRec->FirstLeft) continue; OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft == OldOutRec) + if (outRec->Pts && firstLeft == OldOutRec) { if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; @@ -3599,13 +3637,41 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) } //---------------------------------------------------------------------- -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) -{ +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + // unused variable `firstLeft`: is this a bug? (dane) + //OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; } } //---------------------------------------------------------------------- @@ -3626,8 +3692,8 @@ void Clipper::JoinCommonEdges() //before calling JoinPoints() ... OutRec *holeStateRec; if (outRec1 == outRec2) holeStateRec = outRec1; - else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); if (!JoinPoints(join, outRec1, outRec2)) continue; @@ -3644,25 +3710,12 @@ void Clipper::JoinCommonEdges() //update all OutRec2.Pts Idx's ... UpdateOutPtIdxs(*outRec2); - //We now need to check every OutRec.FirstLeft pointer. If it points - //to OutRec1 it may need to point to OutRec2 instead ... - if (m_UsingPolyTree) - for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) - { - OutRec* oRec = m_PolyOuts[j]; - if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2)) - oRec->FirstLeft = outRec2; - } - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) { - //outRec2 is contained by outRec1 ... + //outRec1 contains outRec2 ... outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) @@ -3670,13 +3723,12 @@ void Clipper::JoinCommonEdges() } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) { - //outRec1 is contained by outRec2 ... + //outRec2 contains outRec1 ... outRec2->IsHole = outRec1->IsHole; outRec1->IsHole = !outRec2->IsHole; outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) @@ -3705,8 +3757,7 @@ void Clipper::JoinCommonEdges() outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); } } } @@ -4404,6 +4455,7 @@ void CleanPolygon(Path& poly, double distance) void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) { + out_polys.resize(in_polys.size()); for (Paths::size_type i = 0; i < in_polys.size(); ++i) CleanPolygon(in_polys[i], out_polys[i], distance); } diff --git a/clipper/clipper.hpp b/clipper/clipper.hpp index 139f1e2..d1c2880 100644 --- a/clipper/clipper.hpp +++ b/clipper/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -251,7 +251,7 @@ class ClipperBase public: ClipperBase(); virtual ~ClipperBase(); - bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); virtual void Clear(); IntRect GetBounds(); @@ -260,11 +260,18 @@ public: protected: void DisposeLocalMinimaList(); TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); - TEdge* DescendToMin(TEdge *&E); - void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); typedef std::vector MinimaList; MinimaList::iterator m_CurrentLM; @@ -272,8 +279,13 @@ protected: bool m_UseFullRange; EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; }; //------------------------------------------------------------------------------ @@ -281,7 +293,6 @@ class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); - ~Clipper(); bool Execute(ClipType clipType, Paths &solution, PolyFillType fillType = pftEvenOdd); @@ -305,19 +316,14 @@ public: void ZFillFunction(ZFillCallback zFillFunc); #endif protected: - void Reset(); virtual bool ExecuteInternal(); private: - PolyOutList m_PolyOuts; JoinList m_Joins; JoinList m_GhostJoins; IntersectList m_IntersectList; ClipType m_ClipType; - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; typedef std::list MaximaList; MaximaList m_Maxima; - TEdge *m_ActiveEdges; TEdge *m_SortedEdges; bool m_ExecuteLocked; PolyFillType m_ClipFillType; @@ -331,19 +337,15 @@ private: void SetWindingCount(TEdge& edge); bool IsEvenOddFillType(const TEdge& edge) const; bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertScanbeam(const cInt Y); - cInt PopScanbeam(); void InsertLocalMinimaIntoAEL(const cInt botY); void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); void CopyAELToSEL(); void DeleteFromSEL(TEdge *e); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); bool IsContributing(const TEdge& edge) const; bool IsTopHorz(const cInt XPos); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void DoMaxima(TEdge *e); void ProcessHorizontals(); void ProcessHorizontal(TEdge *horzEdge); @@ -352,11 +354,8 @@ private: OutRec* GetOutRec(int idx); void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); OutPt* GetLastOutPt(TEdge *e); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); bool ProcessIntersections(const cInt topY); void BuildIntersectList(const cInt topY); void ProcessIntersectList(); @@ -379,7 +378,8 @@ private: void JoinCommonEdges(); void DoSimplePolygons(); void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef use_xyz void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif