diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index b75553369d..cea2087d87 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.3.0 * +* Date : 19 April 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -325,7 +325,7 @@ inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cI #endif inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) - { return SlopesEqual(e1.Delta.x(), e1.Delta.y(), e2.Delta.x(), e2.Delta.y(), UseFullInt64Range); } + { return SlopesEqual(e1.Top.x() - e1.Bot.x(), e1.Top.y() - e1.Bot.y(), e2.Top.x() - e2.Bot.x(), e2.Top.y() - e2.Bot.y(), UseFullInt64Range); } inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range) { return SlopesEqual(pt1.x()-pt2.x(), pt1.y()-pt2.y(), pt2.x()-pt3.x(), pt2.y()-pt3.y(), UseFullInt64Range); } inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, const IntPoint &pt4, bool UseFullInt64Range) @@ -335,7 +335,7 @@ inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint inline bool IsHorizontal(TEdge &e) { - return e.Delta.y() == 0; + return e.Dx == HORIZONTAL; } //------------------------------------------------------------------------------ @@ -368,7 +368,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) } int64_t y; - if (Edge1.Delta.x() == 0) + if (Edge1.Dx == 0) { ip.x() = Edge1.Bot.x(); y = IsHorizontal(Edge2) ? @@ -376,7 +376,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) Round(ip.x() / Edge2.Dx + Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx)); } - else if (Edge2.Delta.x() == 0) + else if (Edge2.Dx == 0) { ip.x() = Edge2.Bot.x(); y = IsHorizontal(Edge1) ? @@ -448,11 +448,9 @@ void InitEdge2(TEdge& e, PolyType Pt) e.Bot = e.Next->Curr; } - 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; e.PolyTyp = Pt; } @@ -934,8 +932,6 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b 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) @@ -1204,7 +1200,13 @@ void Clipper::SetWindingCount(TEdge &edge) const 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 } @@ -1421,13 +1423,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); - m_Joins.emplace_back(Join(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(IntPoint2d(xPrev, Pt.y()), prevE->Top, IntPoint2d(xE, Pt.y()), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + m_Joins.emplace_back(Join(result, outPt, e->Top)); + } } return result; } @@ -1543,7 +1548,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); @@ -1554,7 +1559,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); @@ -1814,19 +1819,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; + } } //------------------------------------------------------------------------------ @@ -1850,7 +1863,7 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) } //------------------------------------------------------------------------------ -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) { do { @@ -1877,9 +1890,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); @@ -1892,7 +1905,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 ) { @@ -1914,7 +1926,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_rt->Next = p2_lft; outRec1->Pts = p2_lft; } - Side = esLeft; } else { if( e2->Side == esRight ) @@ -1933,7 +1944,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_lft->Prev = p2_rt; p2_rt->Next = p1_lft; } - Side = esRight; } outRec1->BottomPt = 0; @@ -1959,7 +1969,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; @@ -2059,17 +2069,21 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) inline 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; + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ - 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; +inline TEdge *GetMaximaPairEx(TEdge *e) +{ + //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; } //------------------------------------------------------------------------------ @@ -2196,7 +2210,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); @@ -2464,6 +2478,8 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.x() > eNext->Curr.x()) { IntersectPoint(*e, *eNext, Pt); + + if (Pt.y() < topY) Pt = IntPoint2d(TopX(*e, topY), topY); m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); SwapPositionsInSEL(e, eNext); isModified = true; @@ -2513,7 +2529,7 @@ bool Clipper::FixupIntersectionOrder() void Clipper::DoMaxima(TEdge *e) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); if (!eMaxPair) { if (e->OutIdx >= 0) @@ -2574,7 +2590,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); } @@ -3172,27 +3188,59 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) } //---------------------------------------------------------------------- +inline OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + // This is potentially very expensive! O(n^3)! void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { //tests if NewOutRec contains the polygon before reassigning FirstLeft for (OutRec &outRec : m_PolyOuts) { - if (!outRec.Pts || !outRec.FirstLeft) continue; - OutRec* firstLeft = outRec.FirstLeft; - // Skip empty polygons. - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) + OutRec* firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts && firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) outRec.FirstLeft = 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 (OutRec &outRec : m_PolyOuts) + { + 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 (OutRec &outRec : m_PolyOuts) - if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec; + { + OutRec* firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts && outRec.FirstLeft == OldOutRec) + outRec.FirstLeft = NewOutRec; + } } //---------------------------------------------------------------------- @@ -3210,8 +3258,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; @@ -3228,23 +3276,9 @@ 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 (size_t j = 0; j < m_PolyOuts.size() - 1; j++) - { - OutRec &oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec.FirstLeft; - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec.Pts || 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; @@ -3256,7 +3290,7 @@ 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; @@ -3294,7 +3328,7 @@ void Clipper::JoinCommonEdges() outRec2->FirstLeft = outRec1; // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); } } } diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index c88545454e..0fb20a78ef 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.3.0 * +* Date : 19 April 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -234,8 +234,6 @@ enum EdgeSide { esLeft = 1, esRight = 2}; IntPoint Curr; // Top point of this edge (with maximum Y). IntPoint Top; - // Vector from Bot to Top. - IntPoint Delta; // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). double Dx; PolyType PolyTyp; @@ -529,7 +527,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 CLIPPERLIB_USE_XYZ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif