diff options
Diffstat (limited to 'src/3rdparty')
-rw-r--r-- | src/3rdparty/clipper/clipper.cpp | 1689 | ||||
-rw-r--r-- | src/3rdparty/clipper/clipper.h | 148 |
2 files changed, 930 insertions, 907 deletions
diff --git a/src/3rdparty/clipper/clipper.cpp b/src/3rdparty/clipper/clipper.cpp index 7a27bc44..d4c82344 100644 --- a/src/3rdparty/clipper/clipper.cpp +++ b/src/3rdparty/clipper/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.3a * -* Date : 22 January 2014 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2014 * +* Copyright : Angus Johnson 2010-2015 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -50,15 +50,6 @@ namespace ClipperLib { -#ifdef use_int32 - static cInt const loRange = 46340; - static cInt const hiRange = 46340; -#else - static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - typedef unsigned long long ulong64; -#endif - static double const pi = 3.141592653589793238; static double const two_pi = pi *2; static double const def_arc_tolerance = 0.25; @@ -74,12 +65,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,15 +89,16 @@ struct IntersectNode { IntPoint Pt; }; -struct LocalMinima { +struct LocalMinimum { cInt Y; TEdge *LeftBound; TEdge *RightBound; - LocalMinima *Next; }; 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; @@ -131,6 +122,14 @@ struct Join { IntPoint OffPt; }; +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -170,7 +169,10 @@ PolyNode* PolyTree::GetFirst() const int PolyTree::Total() const { - return (int)AllNodes.size(); + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; } //------------------------------------------------------------------------------ @@ -240,8 +242,8 @@ bool PolyNode::IsOpen() const //------------------------------------------------------------------------------ // Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((cInt)9223372036854775807); //ie 2^63 -1 -// Int128 val2((cInt)9223372036854775807); +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); // Int128 val3 = val1 * val2; // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) //------------------------------------------------------------------------------ @@ -249,22 +251,21 @@ bool PolyNode::IsOpen() const class Int128 { public: + ulong64 lo; + long64 hi; - cUInt lo; - cInt hi; - - Int128(cInt _lo = 0) + Int128(long64 _lo = 0) { - lo = (cUInt)_lo; + lo = (ulong64)_lo; if (_lo < 0) hi = -1; else hi = 0; } Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - Int128& operator = (const cInt &val) + Int128& operator = (const long64 &val) { lo = (ulong64)val; if (val < 0) hi = -1; else hi = 0; @@ -330,81 +331,18 @@ class Int128 Int128 operator-() const //unary negation { if (lo == 0) - return Int128(-hi,0); - else - return Int128(~hi,~lo +1); - } - - Int128 operator/ (const Int128 &rhs) const - { - if (rhs.lo == 0 && rhs.hi == 0) - throw "Int128 operator/: divide by zero"; - - bool negate = (rhs.hi < 0) != (hi < 0); - Int128 dividend = *this; - Int128 divisor = rhs; - if (dividend.hi < 0) dividend = -dividend; - if (divisor.hi < 0) divisor = -divisor; - - if (divisor < dividend) - { - Int128 result = Int128(0); - Int128 cntr = Int128(1); - while (divisor.hi >= 0 && !(divisor > dividend)) - { - divisor.hi <<= 1; - if ((cInt)divisor.lo < 0) divisor.hi++; - divisor.lo <<= 1; - - cntr.hi <<= 1; - if ((cInt)cntr.lo < 0) cntr.hi++; - cntr.lo <<= 1; - } - divisor.lo >>= 1; - if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; - divisor.hi = (ulong64)divisor.hi >> 1; - - cntr.lo >>= 1; - if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; - cntr.hi >>= 1; - - while (cntr.hi != 0 || cntr.lo != 0) - { - if (!(dividend < divisor)) - { - dividend -= divisor; - result.hi |= cntr.hi; - result.lo |= cntr.lo; - } - divisor.lo >>= 1; - if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; - divisor.hi >>= 1; - - cntr.lo >>= 1; - if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; - cntr.hi >>= 1; - } - if (negate) result = -result; - return result; - } - else if (rhs.hi == this->hi && rhs.lo == this->lo) - return Int128(negate ? -1: 1); + return Int128(-hi, 0); else - return Int128(0); + return Int128(~hi, ~lo + 1); } - double AsDouble() const + operator double() const { const double shift64 = 18446744073709551616.0; //2^64 if (hi < 0) { - cUInt lo_ = ~lo + 1; - if (lo_ == 0) return (double)hi * shift64; - else return -(double)(lo_ + ~hi * shift64); + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); } else return (double)(lo + hi * shift64); @@ -413,7 +351,7 @@ class Int128 }; //------------------------------------------------------------------------------ -Int128 Int128Mul (cInt lhs, cInt rhs) +Int128 Int128Mul (long64 lhs, long64 rhs) { bool negate = (lhs < 0) != (rhs < 0); @@ -431,9 +369,9 @@ Int128 Int128Mul (cInt lhs, cInt rhs) ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; Int128 tmp; - tmp.hi = cInt(a + (c >> 32)); - tmp.lo = cInt(c << 32); - tmp.lo += cInt(b); + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); if (tmp.lo < b) tmp.hi++; if (negate) tmp = -tmp; return tmp; @@ -465,19 +403,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; @@ -491,10 +435,11 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp) } //------------------------------------------------------------------------------ -int PointInPolygon (const IntPoint &pt, const Path &path) +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf int result = 0; size_t cnt = path.size(); if (cnt < 3) return 0; @@ -539,7 +484,6 @@ int PointInPolygon (const IntPoint &pt, const Path &path) int PointInPolygon (const IntPoint &pt, OutPt *op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf int result = 0; OutPt* startOp = op; for(;;) @@ -584,8 +528,9 @@ bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) OutPt* op = OutPt1; do { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon int res = PointInPolygon(op->Pt, OutPt2); - if (res >= 0) return res != 0; + if (res >= 0) return res > 0; op = op->Next; } while (op != OutPt1); @@ -597,10 +542,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); } //------------------------------------------------------------------------------ @@ -630,7 +577,7 @@ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, inline bool IsHorizontal(TEdge &e) { - return e.Delta.Y == 0; + return e.Dx == HORIZONTAL; } //------------------------------------------------------------------------------ @@ -643,11 +590,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; } //--------------------------------------------------------------------------- @@ -674,22 +619,20 @@ inline cInt TopX(TEdge &edge, const cInt currentY) } //------------------------------------------------------------------------------ -bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, - IntPoint &ip, bool UseFullInt64Range) +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) { #ifdef use_xyz ip.Z = 0; #endif + double b1, b2; - //nb: with very large coordinate values, it's possible for SlopesEqual() to - //return false but for the edge.Dx value be equal due to double precision rounding. - if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx) + if (Edge1.Dx == Edge2.Dx) { - if (Edge2.Bot.Y > Edge1.Bot.Y) ip = Edge2.Bot; - else ip = Edge1.Bot; - return false; + ip.Y = Edge1.Curr.Y; + 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)) @@ -700,7 +643,7 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, 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)) @@ -734,7 +677,15 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, else ip.X = TopX(Edge2, ip.Y); } - return true; + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } } //------------------------------------------------------------------------------ @@ -807,13 +758,9 @@ inline void ReverseHorizontal(TEdge &e) //swap horizontal edges' Top and Bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - cInt tmp = e.Top.X; - e.Top.X = e.Bot.X; - e.Bot.X = tmp; + std::swap(e.Top.X, e.Bot.X); #ifdef use_xyz - tmp = e.Top.Z; - e.Top.Z = e.Bot.Z; - e.Bot.Z = tmp; + std::swap(e.Top.Z, e.Bot.Z); #endif } //------------------------------------------------------------------------------ @@ -863,7 +810,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); } //------------------------------------------------------------------------------ @@ -905,26 +857,6 @@ OutPt* GetBottomPt(OutPt *pp) } //------------------------------------------------------------------------------ -bool FindSegment(OutPt* &pp, bool UseFullInt64Range, - IntPoint &pt1, IntPoint &pt2) -{ - //OutPt1 & OutPt2 => the overlap segment (if the function returns true) - if (!pp) return false; - OutPt* pp2 = pp; - IntPoint pt1a = pt1, pt2a = pt2; - do - { - if (SlopesEqual(pt1a, pt2a, pp->Pt, pp->Prev->Pt, UseFullInt64Range) && - SlopesEqual(pt1a, pt2a, pp->Pt, UseFullInt64Range) && - GetOverlapSegment(pt1a, pt2a, pp->Pt, pp->Prev->Pt, pt1, pt2)) - return true; - pp = pp->Next; - } - while (pp != pp2); - return false; -} -//------------------------------------------------------------------------------ - bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, const IntPoint pt2, const IntPoint pt3) { @@ -937,50 +869,20 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, } //------------------------------------------------------------------------------ -OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt) -{ - if (p1 == p2) throw "JoinError"; - OutPt* result = new OutPt; - result->Pt = Pt; - if (p2 == p1->Next) - { - p1->Next = result; - p2->Prev = result; - result->Next = p2; - result->Prev = p1; - } else - { - p2->Next = result; - p1->Prev = result; - result->Next = p1; - result->Prev = p2; - } - return result; -} -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, - const IntPoint& pt2a, const IntPoint& pt2b) +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) { - //precondition: both segments are horizontal - if ((pt1a.X > pt2a.X) == (pt1a.X < pt2b.X)) return true; - else if ((pt1b.X > pt2a.X) == (pt1b.X < pt2b.X)) return true; - else if ((pt2a.X > pt1a.X) == (pt2a.X < pt1b.X)) return true; - else if ((pt2b.X > pt1a.X) == (pt2b.X < pt1b.X)) return true; - else if ((pt1a.X == pt2a.X) && (pt1b.X == pt2b.X)) return true; - else if ((pt1a.X == pt2b.X) && (pt1b.X == pt2a.X)) return true; - else return false; + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); } - //------------------------------------------------------------------------------ // ClipperBase class methods ... //------------------------------------------------------------------------------ ClipperBase::ClipperBase() //constructor { - m_MinimaList = 0; - m_CurrentLM = 0; + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here m_UseFullRange = false; } //------------------------------------------------------------------------------ @@ -996,7 +898,7 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange) if (useFullRange) { if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw "Coordinate outside allowed range"; + throw clipperException("Coordinate outside allowed range"); } else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) { @@ -1023,114 +925,119 @@ TEdge* FindNextLocMin(TEdge* E) } //------------------------------------------------------------------------------ -TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) { - TEdge *EStart = E, *Result = E; + TEdge *Result = E; TEdge *Horz = 0; - cInt StartX; - if (IsHorizontal(*E)) - { - //it's possible for adjacent overlapping horz edges to start heading left - //before finishing right, so ... - if (IsClockwise) StartX = E->Prev->Bot.X; - else StartX = E->Next->Bot.X; - if (E->Bot.X != StartX) ReverseHorizontal(*E); - } - - if (Result->OutIdx != Skip) - { - if (IsClockwise) - { - while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) - Result = Result->Next; - if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) - { - //nb: at the top of a bound, horizontals are added to the bound - //only when the preceding edge attaches to the horizontal's left vertex - //unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X == Result->Next->Top.X) - { - if (!IsClockwise) Result = Horz->Prev; - } - else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; - } - while (E != Result) - { - E->NextInLML = E->Next; - if (IsHorizontal(*E) && E != EStart && - E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - E = E->Next; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) - ReverseHorizontal(*E); - Result = Result->Next; //move to the edge just beyond current bound - } else - { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) - Result = Result->Prev; - if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) - { - Horz = Result; - while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X) - { - if (!IsClockwise) Result = Horz->Next; - } - else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; - } - while (E != Result) - { - E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - E = E->Prev; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - Result = Result->Prev; //move to the edge just beyond current bound - } - } - - if (Result->OutIdx == Skip) + if (E->OutIdx == Skip) { //if edges still remain in the current bound beyond the skip edge then //create another LocMin and call ProcessBound once more - E = Result; - if (IsClockwise) + if (NextIsForward) { while (E->Top.Y == E->Next->Bot.Y) E = E->Next; //don't include top horizontals when parsing a bound a second time, //they will be contained in the opposite bound ... while (E != Result && IsHorizontal(*E)) E = E->Prev; - } else + } + else { while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; while (E != Result && IsHorizontal(*E)) E = E->Next; } + if (E == Result) { - if (IsClockwise) Result = E->Next; + if (NextIsForward) Result = E->Next; else Result = E->Prev; - } else + } + else { //there are more edges in the bound beyond result starting with E - if (IsClockwise) - E = Result->Next; + if (NextIsForward) + E = Result->Next; else E = Result->Prev; - LocalMinima* locMin = new LocalMinima; - locMin->Next = 0; - locMin->Y = E->Bot.Y; - locMin->LeftBound = 0; - locMin->RightBound = E; - locMin->RightBound->WindDelta = 0; - Result = ProcessBound(locMin->RightBound, IsClockwise); - InsertLocalMinima(locMin); + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); } + return Result; } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + return Result; } //------------------------------------------------------------------------------ @@ -1179,7 +1086,8 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) TEdge *E = eStart, *eLoopStop = eStart; for (;;) { - if ((E->Curr == E->Next->Curr)) + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) { if (E == E->Next) break; if (E == eStart) eStart = E->Next; @@ -1205,7 +1113,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) continue; } E = E->Next; - if (E == eLoopStop) break; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; } if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) @@ -1242,28 +1150,32 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; } E->Prev->OutIdx = Skip; - if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); - LocalMinima* locMin = new LocalMinima(); - locMin->Next = 0; - locMin->Y = E->Bot.Y; - locMin->LeftBound = 0; - locMin->RightBound = E; - locMin->RightBound->Side = esRight; - locMin->RightBound->WindDelta = 0; - while (E->Next->OutIdx != Skip) + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) { - E->NextInLML = E->Next; if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; E = E->Next; } - InsertLocalMinima(locMin); + m_MinimaList.push_back(locMin); m_edges.push_back(edges); return true; } m_edges.push_back(edges); - bool clockwise; + bool leftBoundIsForward; TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + for (;;) { E = FindNextLocMin(E); @@ -1272,38 +1184,38 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) //E and E.Prev now share a local minima (left aligned if horizontal). //Compare their slopes to find which starts which bound ... - LocalMinima* locMin = new LocalMinima; - locMin->Next = 0; - locMin->Y = E->Bot.Y; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; if (E->Dx < E->Prev->Dx) { - locMin->LeftBound = E->Prev; - locMin->RightBound = E; - clockwise = false; //Q.nextInLML = Q.prev + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev } else { - locMin->LeftBound = E; - locMin->RightBound = E->Prev; - clockwise = true; //Q.nextInLML = Q.next + locMin.LeftBound = E; + 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) - locMin->LeftBound->WindDelta = -1; - else locMin->LeftBound->WindDelta = 1; - locMin->RightBound->WindDelta = -locMin->LeftBound->WindDelta; - - E = ProcessBound(locMin->LeftBound, clockwise); - TEdge* E2 = ProcessBound(locMin->RightBound, !clockwise); - - if (locMin->LeftBound->OutIdx == Skip) - locMin->LeftBound = 0; - else if (locMin->RightBound->OutIdx == Skip) - locMin->RightBound = 0; - InsertLocalMinima(locMin); - if (!clockwise) E = E2; + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; } return true; } @@ -1318,34 +1230,11 @@ bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) } //------------------------------------------------------------------------------ -void ClipperBase::InsertLocalMinima(LocalMinima *newLm) -{ - if( ! m_MinimaList ) - { - m_MinimaList = newLm; - } - else if( newLm->Y >= m_MinimaList->Y ) - { - newLm->Next = m_MinimaList; - m_MinimaList = newLm; - } else - { - LocalMinima* tmpLm = m_MinimaList; - while( tmpLm->Next && ( newLm->Y < tmpLm->Next->Y ) ) - tmpLm = tmpLm->Next; - newLm->Next = tmpLm->Next; - tmpLm->Next = newLm; - } -} -//------------------------------------------------------------------------------ - 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; } @@ -1357,13 +1246,15 @@ void ClipperBase::Clear() void ClipperBase::Reset() { - m_CurrentLM = m_MinimaList; - if( !m_CurrentLM ) return; //ie nothing to process + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue //reset all edges ... - LocalMinima* lm = m_MinimaList; - while( lm ) + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) { + InsertScanbeam(lm->Y); TEdge* e = lm->LeftBound; if (e) { @@ -1379,35 +1270,33 @@ void ClipperBase::Reset() e->Side = esRight; e->OutIdx = Unassigned; } - lm = lm->Next; } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); } //------------------------------------------------------------------------------ void ClipperBase::DisposeLocalMinimaList() { - while( m_MinimaList ) - { - LocalMinima* tmpLm = m_MinimaList->Next; - delete m_MinimaList; - m_MinimaList = tmpLm; - } - m_CurrentLM = 0; + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); } //------------------------------------------------------------------------------ -void ClipperBase::PopLocalMinima() +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) { - if( ! m_CurrentLM ) return; - m_CurrentLM = m_CurrentLM->Next; + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; } //------------------------------------------------------------------------------ IntRect ClipperBase::GetBounds() { IntRect result; - LocalMinima* lm = m_MinimaList; - if (!lm) + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) { result.left = result.top = result.right = result.bottom = 0; return result; @@ -1416,10 +1305,10 @@ IntRect ClipperBase::GetBounds() result.top = lm->LeftBound->Bot.Y; result.right = lm->LeftBound->Bot.X; result.bottom = lm->LeftBound->Bot.Y; - while (lm) + while (lm != m_MinimaList.end()) { - if (lm->LeftBound->Bot.Y > result.bottom) - result.bottom = lm->LeftBound->Bot.Y; + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); TEdge* e = lm->LeftBound; for (;;) { TEdge* bottomE = e; @@ -1429,19 +1318,154 @@ IntRect ClipperBase::GetBounds() if (e->Bot.X > result.right) result.right = e->Bot.X; e = e->NextInLML; } - if (e->Bot.X < result.left) result.left = e->Bot.X; - if (e->Bot.X > result.right) result.right = e->Bot.X; - if (e->Top.X < result.left) result.left = e->Top.X; - if (e->Top.X > result.right) result.right = e->Top.X; - if (e->Top.Y < result.top) result.top = e->Top.Y; - + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); if (bottomE == lm->LeftBound) e = lm->RightBound; else break; } - lm = lm->Next; + ++lm; } 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 ... @@ -1449,8 +1473,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); @@ -1463,33 +1485,23 @@ Clipper::Clipper(int initOptions) : ClipperBase() //constructor } //------------------------------------------------------------------------------ -Clipper::~Clipper() //destructor -{ - Clear(); - m_Scanbeam.clear(); -} -//------------------------------------------------------------------------------ - #ifdef use_xyz -void Clipper::ZFillFunction(TZFillCallback zFillFunc) +void Clipper::ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } //------------------------------------------------------------------------------ #endif -void Clipper::Reset() +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) { - ClipperBase::Reset(); - m_Scanbeam.clear(); - m_ActiveEdges = 0; - m_SortedEdges = 0; - LocalMinima* lm = m_MinimaList; - while (lm) - { - InsertScanbeam(lm->Y); - lm = lm->Next; - } + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); } //------------------------------------------------------------------------------ @@ -1498,7 +1510,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, { if( m_ExecuteLocked ) return false; if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is need for open path clipping."); + throw clipperException("Error: PolyTree struct is needed for open path clipping."); m_ExecuteLocked = true; solution.resize(0); m_SubjFillType = subjFillType; @@ -1550,19 +1562,26 @@ bool Clipper::ExecuteInternal() bool succeeded = true; try { Reset(); - if (!m_CurrentLM) return false; - cInt botY = PopScanbeam(); - do { - InsertLocalMinimaIntoAEL(botY); - ClearGhostJoins(); - ProcessHorizontals(false); - if (m_Scanbeam.empty()) break; - cInt topY = PopScanbeam(); - succeeded = ProcessIntersections(botY, topY); - if (!succeeded) break; + 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 (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } ProcessEdgesAtTopOfScanbeam(topY); botY = topY; - } while (!m_Scanbeam.empty() || m_CurrentLM); + InsertLocalMinimaIntoAEL(botY); + } } catch(...) { @@ -1586,7 +1605,10 @@ bool Clipper::ExecuteInternal() for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec *outRec = m_PolyOuts[i]; - if (outRec->Pts && !outRec->IsOpen) + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else FixupOutPolygon(*outRec); } @@ -1599,36 +1621,6 @@ bool Clipper::ExecuteInternal() } //------------------------------------------------------------------------------ -void Clipper::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.insert(Y); -} -//------------------------------------------------------------------------------ - -cInt Clipper::PopScanbeam() -{ - cInt Y = *m_Scanbeam.begin(); - m_Scanbeam.erase(m_Scanbeam.begin()); - 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; @@ -1636,7 +1628,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 } @@ -1868,13 +1866,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; } @@ -1916,6 +1917,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; @@ -1967,11 +1977,12 @@ void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - while( m_CurrentLM && ( 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) { @@ -2003,8 +2014,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; @@ -2018,7 +2034,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) Join* jr = m_GhostJoins[i]; //if the horizontal Rb and a 'ghost' horizontal overlap, then convert //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr->OutPt1->Pt, jr->OffPt, rb->Bot, rb->Top)) + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) AddJoin(jr->OutPt1, Op1, jr->OffPt); } } @@ -2026,7 +2042,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); @@ -2037,7 +2053,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); @@ -2061,19 +2077,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; @@ -2088,45 +2091,34 @@ void Clipper::DeleteFromSEL(TEdge *e) //------------------------------------------------------------------------------ #ifdef use_xyz - -void Clipper::SetZ(IntPoint& pt, TEdge& e) +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) { - pt.Z = 0; - if (m_ZFill) - { - //put the 'preferred' point as first parameter ... - if (e.OutIdx < 0) - (*m_ZFill)(e.Bot, e.Top, pt); //outside a path so presume entering - else - (*m_ZFill)(e.Top, e.Bot, pt); //inside a path so presume exiting - } + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); } //------------------------------------------------------------------------------ #endif -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &Pt, bool protect) +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { - //e1 will be to the Left of e2 BELOW the intersection. Therefore e1 is before - //e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1stops = !protect && !e1->NextInLML && - e1->Top.X == Pt.X && e1->Top.Y == Pt.Y; - bool e2stops = !protect && !e2->NextInLML && - e2->Top.X == Pt.X && e2->Top.Y == Pt.Y; bool e1Contributing = ( e1->OutIdx >= 0 ); bool e2Contributing = ( e2->OutIdx >= 0 ); +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + #ifdef use_lines //if either edge is on an OPEN path ... if (e1->WindDelta == 0 || e2->WindDelta == 0) { //ignore subject-subject open path intersections UNLESS they //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) - { - if ((e1stops || e2stops) && e1Contributing && e2Contributing) - AddLocalMaxPoly(e1, e2, Pt); - } + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; //if intersecting a subj line with a subj poly ... else if (e1->PolyTyp == e2->PolyTyp && @@ -2165,13 +2157,6 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if (e2Contributing) e2->OutIdx = Unassigned; } } - - if (e1stops) - if (e1->OutIdx < 0) DeleteFromAEL(e1); - else throw clipperException("Error intersecting polylines"); - if (e2stops) - if (e2->OutIdx < 0) DeleteFromAEL(e2); - else throw clipperException("Error intersecting polylines"); return; } #endif @@ -2236,10 +2221,11 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if ( e1Contributing && e2Contributing ) { - if ( e1stops || e2stops || - (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, Pt); + { + AddLocalMaxPoly(e1, e2, Pt); + } else { AddOutPt(e1, Pt); @@ -2266,8 +2252,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, SwapPolyIndexes(*e1, *e2); } } - else if ( (e1Wc == 0 || e1Wc == 1) && - (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) { //neither edge is currently contributing ... @@ -2286,7 +2271,9 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } if (e1->PolyTyp != e2->PolyTyp) - AddLocalMinPoly(e1, e2, Pt); + { + AddLocalMinPoly(e1, e2, Pt); + } else if (e1Wc == 1 && e2Wc == 1) switch( m_ClipType ) { case ctIntersection: @@ -2308,35 +2295,32 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, else SwapSides( *e1, *e2 ); } - - if( (e1stops != e2stops) && - ( (e1stops && (e1->OutIdx >= 0)) || (e2stops && (e2->OutIdx >= 0)) ) ) - { - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - - //finally, delete any non-contributing maxima edges ... - if( e1stops ) DeleteFromAEL( e1 ); - if( e2stops ) DeleteFromAEL( e2 ); } //------------------------------------------------------------------------------ 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; + } } //------------------------------------------------------------------------------ @@ -2360,7 +2344,7 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) } //------------------------------------------------------------------------------ -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) { do { @@ -2387,9 +2371,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); @@ -2402,7 +2386,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 ) { @@ -2424,7 +2407,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_rt->Next = p2_lft; outRec1->Pts = p2_lft; } - Side = esLeft; } else { if( e2->Side == esRight ) @@ -2443,7 +2425,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) p1_lft->Prev = p2_rt; p2_rt->Next = p1_lft; } - Side = esRight; } outRec1->BottomPt = 0; @@ -2469,7 +2450,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; @@ -2479,24 +2460,8 @@ 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) { - bool ToFront = (e->Side == esLeft); if( e->OutIdx < 0 ) { OutRec *outRec = CreateOutRec(); @@ -2509,12 +2474,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) newOp->Prev = newOp; if (!outRec->IsOpen) SetHoleState(e, outRec); -#ifdef use_xyz - if (pt == e->Bot) newOp->Pt = e->Bot; - else if (pt == e->Top) newOp->Pt = e->Top; - else SetZ(newOp->Pt, *e); -#endif - e->OutIdx = outRec->Idx; //nb: do this after SetZ ! + e->OutIdx = outRec->Idx; return newOp; } else { @@ -2522,7 +2482,8 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; - if (ToFront && (pt == op->Pt)) return op; + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; OutPt* newOp = new OutPt; @@ -2533,25 +2494,26 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) newOp->Prev->Next = newOp; op->Prev = newOp; if (ToFront) outRec->Pts = newOp; -#ifdef use_xyz - if (pt == e->Bot) newOp->Pt = e->Bot; - else if (pt == e->Top) newOp->Pt = e->Top; - else SetZ(newOp->Pt, *e); -#endif return newOp; } } //------------------------------------------------------------------------------ -void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) +OutPt* Clipper::GetLastOutPt(TEdge *e) { - TEdge* horzEdge = m_SortedEdges; - while(horzEdge) - { - DeleteFromSEL(horzEdge); - ProcessHorizontal(horzEdge, IsTopOfScanbeam); - horzEdge = m_SortedEdges; - } + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); } //------------------------------------------------------------------------------ @@ -2575,64 +2537,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; } //------------------------------------------------------------------------------ @@ -2704,37 +2623,6 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) } //------------------------------------------------------------------------ -void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) -{ - //get the last Op for this horizontal edge - //the point may be anywhere along the horizontal ... - OutPt* outPt = m_PolyOuts[horzEdge->OutIdx]->Pts; - if (horzEdge->Side != esLeft) outPt = outPt->Prev; - - //First, match up overlapping horizontal edges (eg when one polygon's - //intermediate horz edge overlaps an intermediate horz edge of another, or - //when one polygon sits on top of another) ... - //for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - //{ - // Join* j = m_GhostJoins[i]; - // if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) - // AddJoin(j->OutPt1, outPt, j->OffPt); - //} - - //Also, since horizontal edges at the top of one SB are often removed from - //the AEL before we process the horizontal edges at the bottom of the next, - //we need to create 'ghost' Join records of 'contrubuting' horizontals that - //we can compare with horizontals at the bottom of the next SB. - if (isTopOfScanbeam) - { - if (outPt->Pt == horzEdge->Top) - AddGhostJoin(outPt, horzEdge->Bot); - else - AddGhostJoin(outPt, horzEdge->Top); - } -} -//------------------------------------------------------------------------------ - /******************************************************************************* * Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * * Bottom of a scanbeam) are processed as if layered. The order in which HEs * @@ -2745,10 +2633,11 @@ void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * *******************************************************************************/ -void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) +void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2758,69 +2647,146 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - for (;;) + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + bool IsLastHorz = (horzEdge == eLastHorz); TEdge* e = GetNextInAEL(horzEdge, dir); while(e) { - //Break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later - - if ((dir == dLeftToRight && e->Curr.X <= horzRight) || - (dir == dRightToLeft && e->Curr.X >= horzLeft)) - { - if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) - PrepareHorzJoins(horzEdge, isTopOfScanbeam); - //so far we're still in range of the horizontal Edge but make sure + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair if(e == eMaxPair && IsLastHorz) { - if (dir == dLeftToRight) - IntersectEdges(horzEdge, e, e->Top); - else - IntersectEdges(e, horzEdge, e->Top); - if (eMaxPair->OutIdx >= 0) throw clipperException("ProcessHorizontal error"); + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); return; } - else if(dir == dLeftToRight) + + if(dir == dLeftToRight) { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt, true); + IntersectEdges(horzEdge, e, Pt); } else { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt, true); + IntersectEdges( e, horzEdge, Pt); } + TEdge* eNext = GetNextInAEL(e, dir); SwapPositionsInAEL( horzEdge, e ); - } - else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || - (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; - e = eNext; - } //end while + e = eNext; + } //end while(e) - if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) - PrepareHorzJoins(horzEdge, isTopOfScanbeam); + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) - { - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - } else - break; } //end for (;;) - if(horzEdge->NextInLML) + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) { if(horzEdge->OutIdx >= 0) { - OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + op1 = AddOutPt( horzEdge, horzEdge->Top); UpdateEdgeIntoAEL(horzEdge); if (horzEdge->WindDelta == 0) return; //nb: HorzEdge is no longer horizontal here @@ -2846,22 +2812,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) else UpdateEdgeIntoAEL(horzEdge); } - else if (eMaxPair) - { - if (eMaxPair->OutIdx >= 0) - { - if (dir == dLeftToRight) - IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); - else - IntersectEdges(eMaxPair, horzEdge, horzEdge->Top); - if (eMaxPair->OutIdx >= 0) - throw clipperException("ProcessHorizontal error"); - } else - { - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - } - } else + else { if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); DeleteFromAEL(horzEdge); @@ -2869,34 +2820,11 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) } //------------------------------------------------------------------------------ -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 botY, const cInt topY) +bool Clipper::ProcessIntersections(const cInt topY) { if( !m_ActiveEdges ) return true; try { - BuildIntersectList(botY, topY); + BuildIntersectList(topY); size_t IlSize = m_IntersectList.size(); if (IlSize == 0) return true; if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); @@ -2921,7 +2849,7 @@ void Clipper::DisposeIntersectNodes() } //------------------------------------------------------------------------------ -void Clipper::BuildIntersectList(const cInt botY, const cInt topY) +void Clipper::BuildIntersectList(const cInt topY) { if ( !m_ActiveEdges ) return; @@ -2948,16 +2876,8 @@ void Clipper::BuildIntersectList(const cInt botY, const cInt topY) IntPoint Pt; if(e->Curr.X > eNext->Curr.X) { - if (!IntersectPoint(*e, *eNext, Pt, m_UseFullRange) && e->Curr.X > eNext->Curr.X +1) - throw clipperException("Intersection error"); - if (Pt.Y > botY) - { - Pt.Y = botY; - if (std::fabs(e->Dx) > std::fabs(eNext->Dx)) - Pt.X = TopX(*eNext, botY); else - Pt.X = TopX(*e, botY); - } - + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); IntersectNode * newNode = new IntersectNode; newNode->Edge1 = e; newNode->Edge2 = eNext; @@ -2985,7 +2905,7 @@ void Clipper::ProcessIntersectList() { IntersectNode* iNode = m_IntersectList[i]; { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt, true); + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); } delete iNode; @@ -3032,7 +2952,7 @@ bool Clipper::FixupIntersectionOrder() void Clipper::DoMaxima(TEdge *e) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); if (!eMaxPair) { if (e->OutIdx >= 0) @@ -3044,7 +2964,7 @@ void Clipper::DoMaxima(TEdge *e) TEdge* eNext = e->NextInAEL; while(eNext && eNext != eMaxPair) { - IntersectEdges(e, eNext, e->Top, true); + IntersectEdges(e, eNext, e->Top); SwapPositionsInAEL(e, eNext); eNext = e->NextInAEL; } @@ -3056,7 +2976,9 @@ void Clipper::DoMaxima(TEdge *e) } else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) { - IntersectEdges( e, eMaxPair, e->Top); + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); } #ifdef use_lines else if (e->WindDelta == 0) @@ -3091,12 +3013,13 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); } if(IsMaximaEdge) { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -3118,15 +3041,21 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) e->Curr.Y = topY; } + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... if (m_StrictSimple) { TEdge* ePrev = e->PrevInAEL; if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) { - OutPt* op = AddOutPt(ePrev, e->Curr); - OutPt* op2 = AddOutPt(e, e->Curr); - AddJoin(op, op2, e->Curr); //StrictlySimple (type-3) join + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join } } @@ -3135,7 +3064,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //3. Process horizontals at the Top of the scanbeam ... - ProcessHorizontals(true); + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); //4. Promote intermediate vertices ... e = m_ActiveEdges; @@ -3154,7 +3085,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); @@ -3163,7 +3094,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); @@ -3175,44 +3106,71 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //------------------------------------------------------------------------------ -void Clipper::FixupOutPolygon(OutRec &outrec) +void Clipper::FixupOutPolyline(OutRec &outrec) { - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = 0; - outrec.BottomPt = 0; OutPt *pp = outrec.Pts; - - for (;;) + OutPt *lastPP = pp->Prev; + while (pp != lastPP) { - if (pp->Prev == pp || pp->Prev == pp->Next ) + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) { - DisposeOutPts(pp); - outrec.Pts = 0; - return; + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; } + } - //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || - (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) - { - lastOK = 0; - OutPt *tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - delete tmp; - } - else if (pp == lastOK) break; - else + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) { - if (!lastOK) lastOK = pp; - pp = pp->Next; + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } } - } - outrec.Pts = pp; + outrec.Pts = pp; } //------------------------------------------------------------------------------ @@ -3496,7 +3454,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) OutPt *op2 = j->OutPt2, *op2b; //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same //location at the Bottom of the overlapping segment (& Join.OffPt is above). @@ -3508,6 +3466,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) (j->OffPt == j->OutPt2->Pt)) { //Strictly Simple join ... + if (outRec1 != outRec2) return false; op1b = j->OutPt1->Next; while (op1b != op1 && (op1b->Pt == j->OffPt)) op1b = op1b->Next; @@ -3648,13 +3607,22 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) } //---------------------------------------------------------------------- +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { - + //tests if NewOutRec contains the polygon before reassigning FirstLeft for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (outRec->Pts && outRec->FirstLeft == OldOutRec) + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) { if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; @@ -3663,23 +3631,43 @@ 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->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + + 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; } } //---------------------------------------------------------------------- - -static OutRec* ParseFirstLeft(OutRec* FirstLeft) +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) { - while (FirstLeft && !FirstLeft->Pts) - FirstLeft = FirstLeft->FirstLeft; - return FirstLeft; + //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]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------- void Clipper::JoinCommonEdges() { @@ -3691,13 +3679,14 @@ void Clipper::JoinCommonEdges() OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; //get the polygon fragment with the correct hole state (FirstLeft) //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; @@ -3714,25 +3703,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)) @@ -3740,13 +3716,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)) @@ -3775,8 +3750,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); } } } @@ -3848,8 +3822,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType (path[i].Y == newNode->Contour[k].Y && path[i].X < newNode->Contour[k].X)) k = j; } - if ((endType == etClosedPolygon && j < 2) || - (endType != etClosedPolygon && j < 0)) + if (endType == etClosedPolygon && j < 2) { delete newNode; return; @@ -3859,7 +3832,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType //if this path's lowest pt is lower than all the others then update m_lowest if (endType != etClosedPolygon) return; if (m_lowest.X < 0) - m_lowest = IntPoint(0, k); + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); else { IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; @@ -3965,6 +3938,7 @@ void ClipperOffset::Execute(PolyTree& solution, double delta) PolyNode* outerNode = solution.Childs[0]; solution.Childs.reserve(outerNode->ChildCount()); solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; for (int i = 1; i < outerNode->ChildCount(); ++i) solution.AddChild(*outerNode->Childs[i]); } @@ -4149,8 +4123,20 @@ void ClipperOffset::DoOffset(double delta) void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) { + //cross product ... m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (m_sinA < 0.00005 && m_sinA > -0.00005) return; + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } else if (m_sinA > 1.0) m_sinA = 1.0; else if (m_sinA < -1.0) m_sinA = -1.0; @@ -4204,7 +4190,7 @@ void ClipperOffset::DoRound(int j, int k) { double a = std::atan2(m_sinA, m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); - int steps = (int)Round(m_StepsPerRad * std::fabs(a)); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); double X = m_normals[k].X, Y = m_normals[k].Y, X2; for (int i = 0; i < steps; ++i) @@ -4232,7 +4218,7 @@ void Clipper::DoSimplePolygons() { OutRec* outrec = m_PolyOuts[i++]; OutPt* op = outrec->Pts; - if (!op) continue; + if (!op || outrec->IsOpen) continue; do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; @@ -4257,6 +4243,7 @@ void Clipper::DoSimplePolygons() //OutRec2 is contained by OutRec1 ... outrec2->IsHole = !outrec->IsHole; outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); } else if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) @@ -4266,12 +4253,15 @@ void Clipper::DoSimplePolygons() outrec->IsHole = !outrec2->IsHole; outrec2->FirstLeft = outrec->FirstLeft; outrec->FirstLeft = outrec2; - } else + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else { //the 2 polygons are separate ... outrec2->IsHole = outrec->IsHole; outrec2->FirstLeft = outrec->FirstLeft; - } + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } op2 = op; //ie get ready for the Next iteration } op2 = op2->Next; @@ -4331,7 +4321,12 @@ inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) double DistanceFromLineSqrd( const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) { - //see https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance double A = double(ln1.Y - ln2.Y); double B = double(ln2.X - ln1.X); double C = A * ln1.X + B * ln1.Y; @@ -4343,7 +4338,27 @@ double DistanceFromLineSqrd( bool SlopesNearCollinear(const IntPoint& pt1, const IntPoint& pt2, const IntPoint& pt3, double distSqrd) { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } } //------------------------------------------------------------------------------ @@ -4433,6 +4448,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); } @@ -4471,8 +4487,8 @@ void Minkowski(const Path& poly, const Path& path, pp.push_back(p); } - Paths quads; - quads.reserve((pathCnt + delta) * (polyCnt + 1)); + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); for (size_t i = 0; i < pathCnt - 1 + delta; ++i) for (size_t j = 0; j < polyCnt; ++j) { @@ -4483,23 +4499,30 @@ void Minkowski(const Path& poly, const Path& path, quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); if (!Orientation(quad)) ReversePath(quad); - quads.push_back(quad); + solution.push_back(quad); } +} +//------------------------------------------------------------------------------ +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); Clipper c; - c.AddPaths(quads, ptSubject, true); + c.AddPaths(solution, ptSubject, true); c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +void TranslatePath(const Path& input, Path& output, const IntPoint delta) { - Minkowski(pattern, path, solution, true, pathIsClosed); + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); } //------------------------------------------------------------------------------ -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, - PolyFillType pathFillType, bool pathIsClosed) +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) { Clipper c; for (size_t i = 0; i < paths.size(); ++i) @@ -4507,21 +4530,29 @@ void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, Paths tmp; Minkowski(pattern, paths[i], tmp, true, pathIsClosed); c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } } - if (pathIsClosed) c.AddPaths(paths, ptClip, true); - c.Execute(ctUnion, solution, pathFillType, pathFillType); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) { Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ enum NodeType {ntAny, ntOpen, ntClosed}; -void AddPolyNodeToPolygons(const PolyNode& polynode, NodeType nodetype, Paths& paths) +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) { bool match = true; if (nodetype == ntClosed) match = !polynode.IsOpen(); @@ -4530,7 +4561,7 @@ void AddPolyNodeToPolygons(const PolyNode& polynode, NodeType nodetype, Paths& p if (!polynode.Contour.empty() && match) paths.push_back(polynode.Contour); for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPolygons(*polynode.Childs[i], nodetype, paths); + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); } //------------------------------------------------------------------------------ @@ -4538,7 +4569,7 @@ void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) { paths.resize(0); paths.reserve(polytree.Total()); - AddPolyNodeToPolygons(polytree, ntAny, paths); + AddPolyNodeToPaths(polytree, ntAny, paths); } //------------------------------------------------------------------------------ @@ -4546,7 +4577,7 @@ void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) { paths.resize(0); paths.reserve(polytree.Total()); - AddPolyNodeToPolygons(polytree, ntClosed, paths); + AddPolyNodeToPaths(polytree, ntClosed, paths); } //------------------------------------------------------------------------------ @@ -4588,18 +4619,4 @@ std::ostream& operator <<(std::ostream &s, const Paths &p) } //------------------------------------------------------------------------------ -#ifdef use_deprecated - -void OffsetPaths(const Paths &in_polys, Paths &out_polys, - double delta, JoinType jointype, EndType_ endtype, double limit) -{ - ClipperOffset co(limit, limit); - co.AddPaths(in_polys, jointype, (EndType)endtype); - co.Execute(out_polys, delta); -} -//------------------------------------------------------------------------------ - -#endif - - } //ClipperLib namespace diff --git a/src/3rdparty/clipper/clipper.h b/src/3rdparty/clipper/clipper.h index 84870141..2472ac77 100644 --- a/src/3rdparty/clipper/clipper.h +++ b/src/3rdparty/clipper/clipper.h @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.3a * -* Date : 22 January 2014 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2014 * +* Copyright : Angus Johnson 2010-2015 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.1.3" +#define CLIPPER_VERSION "6.2.6" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -44,19 +44,20 @@ //#define use_xyz //use_lines: Enables line clipping. Adds a very minor cost to performance. -//#define use_lines +#define use_lines -//use_deprecated: Enables support for the obsolete OffsetPaths() function -//which has been replace with the ClipperOffset class. -#define use_deprecated +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated #include <vector> +#include <list> #include <set> #include <stdexcept> #include <cstring> #include <cstdlib> #include <ostream> #include <functional> +#include <queue> namespace ClipperLib { @@ -69,11 +70,16 @@ enum PolyType { ptSubject, ptClip }; enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; #ifdef use_int32 -typedef int cInt; -typedef unsigned int cUInt; + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; #else -typedef signed long long cInt; -typedef unsigned long long cUInt; + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + #endif struct IntPoint { @@ -117,15 +123,12 @@ struct DoublePoint //------------------------------------------------------------------------------ #ifdef use_xyz -typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt); +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); #endif enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; enum JoinType {jtSquare, jtRound, jtMiter}; enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; -#ifdef use_deprecated - enum EndType_ {etClosed, etButt = 2, etSquare, etRound}; -#endif class PolyNode; typedef std::vector< PolyNode* > PolyNodes; @@ -134,6 +137,7 @@ class PolyNode { public: PolyNode(); + virtual ~PolyNode(){}; Path Contour; PolyNodes Childs; PolyNode* Parent; @@ -168,11 +172,6 @@ bool Orientation(const Path &poly); double Area(const Path &poly); int PointInPolygon(const IntPoint &pt, const Path &path); -#ifdef use_deprecated - void OffsetPaths(const Paths &in_polys, Paths &out_polys, - double delta, JoinType jointype, EndType_ endtype, double limit = 0); -#endif - void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); @@ -183,8 +182,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1. void CleanPolygons(Paths& polys, double distance = 1.415); void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, - Paths& solution, PolyFillType pathFillType, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); @@ -202,8 +200,7 @@ enum EdgeSide { esLeft = 1, esRight = 2}; //forward declarations (for stuff used internally) ... struct TEdge; struct IntersectNode; -struct LocalMinima; -struct Scanbeam; +struct LocalMinimum; struct OutPt; struct OutRec; struct Join; @@ -213,7 +210,6 @@ typedef std::vector < TEdge* > EdgeList; typedef std::vector < Join* > JoinList; typedef std::vector < IntersectNode* > IntersectList; - //------------------------------------------------------------------------------ //ClipperBase is the ancestor to the Clipper class. It should not be @@ -224,7 +220,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(); @@ -233,19 +229,32 @@ public: protected: void DisposeLocalMinimaList(); TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); - void InsertLocalMinima(LocalMinima *newLm); - void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); - TEdge* DescendToMin(TEdge *&E); - void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); - LocalMinima *m_CurrentLM; - LocalMinima *m_MinimaList; + 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<LocalMinimum> MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + 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<cInt> ScanbeamList; + ScanbeamList m_Scanbeam; }; //------------------------------------------------------------------------------ @@ -253,34 +262,37 @@ class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); - ~Clipper(); bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); - bool ReverseSolution() {return m_ReverseOutput;}; + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; void ReverseSolution(bool value) {m_ReverseOutput = value;}; bool StrictlySimple() {return m_StrictSimple;}; void StrictlySimple(bool value) {m_StrictSimple = value;}; //set the callback function for z value filling on intersections (otherwise Z is 0) #ifdef use_xyz - void ZFillFunction(TZFillCallback zFillFunc); + 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; - std::set< cInt, std::greater<cInt> > m_Scanbeam; - TEdge *m_ActiveEdges; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list<cInt> MaximaList; + MaximaList m_Maxima; TEdge *m_SortedEdges; bool m_ExecuteLocked; PolyFillType m_ClipFillType; @@ -289,40 +301,32 @@ private: bool m_UsingPolyTree; bool m_StrictSimple; #ifdef use_xyz - TZFillCallback m_ZFill; //custom callback + ZFillCallback m_ZFill; //custom callback #endif 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 PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam); - void ProcessHorizontals(bool IsTopOfScanbeam); - void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, bool protect = false); - OutRec* CreateOutRec(); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); - bool ProcessIntersections(const cInt botY, const cInt topY); - void BuildIntersectList(const cInt botY, const cInt topY); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); void ProcessIntersectList(); void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); @@ -331,6 +335,7 @@ private: void DisposeIntersectNodes(); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); bool IsHole(TEdge *e); bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); void FixHoleLinkage(OutRec &outrec); @@ -342,9 +347,10 @@ 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& e); + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif }; //------------------------------------------------------------------------------ |