diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 1eac45a70..bac473d4b 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -48,7 +48,6 @@ #include #include #include -#include namespace ClipperLib { @@ -65,7 +64,60 @@ static int const Skip = -2; //edge that would otherwise close a path #define TOLERANCE (1.0e-20) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -// Point of an output polygon. +struct TEdge { + // Bottom point of this edge (with minimum Y). + IntPoint Bot; + // Current position. + IntPoint Curr; + // Top point of this edge (with maximum Y). + IntPoint Top; + // Vector from Bot to Top. + IntPoint Delta; + // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). + double Dx; + PolyType PolyTyp; + EdgeSide Side; + // Winding number delta. 1 or -1 depending on winding direction, 0 for open paths and flat closed paths. + int WindDelta; + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + // Next edge in the input path. + TEdge *Next; + // Previous edge in the input path. + TEdge *Prev; + // Next edge in the Local Minima List chain. + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + struct OutPt { int Idx; IntPoint Pt; @@ -73,41 +125,48 @@ struct OutPt { OutPt *Prev; }; -// Output polygon. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - //The 'FirstLeft' field points to another OutRec that contains or is the - //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is - //parsed left from the current edge (owning OutRec) until the owner OutRec - //is found. This field simplifies sorting the polygons into a tree structure - //which reflects the parent/child relationships of all polygons. - //This field should be renamed Parent, and will be later. - OutRec *FirstLeft; - // Used only by void Clipper::BuildResult2(PolyTree& polytree) - PolyNode *PolyNd; - // Linked list of output points, dynamically allocated. - OutPt *Pts; - OutPt *BottomPt; -}; - +//------------------------------------------------------------------------------ //------------------------------------------------------------------------------ inline cInt Round(double val) { - return static_cast((val < 0) ? (val - 0.5) : (val + 0.5)); + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; } //------------------------------------------------------------------------------ // PolyTree methods ... //------------------------------------------------------------------------------ +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + int PolyTree::Total() const { int result = (int)AllNodes.size(); //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && Childs.front() != &AllNodes.front()) result--; + if (result > 0 && Childs[0] != AllNodes[0]) result--; return result; } @@ -115,6 +174,17 @@ int PolyTree::Total() const // PolyNode methods ... //------------------------------------------------------------------------------ +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + void PolyNode::AddChild(PolyNode& child) { unsigned cnt = (unsigned)Childs.size(); @@ -124,6 +194,26 @@ void PolyNode::AddChild(PolyNode& child) } //------------------------------------------------------------------------------ +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + // Edge delimits a hole if it has an odd number of parent loops. bool PolyNode::IsHole() const { @@ -138,6 +228,12 @@ bool PolyNode::IsHole() const } //------------------------------------------------------------------------------ +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + #ifndef use_int32 //------------------------------------------------------------------------------ @@ -263,12 +359,9 @@ inline Int128 Int128Mul (long64 lhs, long64 rhs) ulong64 int2Hi = ulong64(rhs) >> 32; ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); - //because the high (sign) bits in both int1Hi & int2Hi have been zeroed, - //there's no risk of 64 bit overflow in the following assignment - //(ie: $7FFFFFFF*$FFFFFFFF + $7FFFFFFF*$FFFFFFFF < 64bits) + //nb: see comments in clipper.pas ulong64 a = int1Hi * int2Hi; ulong64 b = int1Lo * int2Lo; - //Result = A shl 64 + C shl 32 + B ... ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; Int128 tmp; @@ -378,13 +471,12 @@ int PointInPolygon(const IntPoint &pt, const Path &path) } //------------------------------------------------------------------------------ -// Called by Poly2ContainsPoly1() int PointInPolygon (const IntPoint &pt, OutPt *op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary int result = 0; OutPt* startOp = op; - do + for(;;) { if (op->Next->Pt.Y == pt.Y) { @@ -415,15 +507,14 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) } } op = op->Next; - } while (startOp != op); + if (startOp == op) break; + } return result; } //------------------------------------------------------------------------------ -// This is potentially very expensive! O(n^2)! bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { - PROFILE_FUNC(); OutPt* op = OutPt1; do { @@ -485,6 +576,22 @@ inline double GetDx(const IntPoint &pt1, const IntPoint &pt2) } //--------------------------------------------------------------------------- +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + inline cInt TopX(TEdge &edge, const cInt currentY) { return ( currentY == edge.Top.Y ) ? @@ -562,17 +669,16 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) } //------------------------------------------------------------------------------ -// Reverse a linked loop of points representing a closed polygon. -// This has a time complexity of O(n) void ReversePolyPtLinks(OutPt *pp) { if (!pp) return; - OutPt *pp1 = pp; + OutPt *pp1, *pp2; + pp1 = pp; do { - OutPt *pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; } while( pp1 != pp ); } //------------------------------------------------------------------------------ @@ -645,21 +751,29 @@ inline void ReverseHorizontal(TEdge &e) } //------------------------------------------------------------------------------ +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) { //precondition: segments are Collinear. - if (std::abs(pt1a.X - pt1b.X) > std::abs(pt1a.Y - pt1b.Y)) + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) { - if (pt1a.X > pt1b.X) std::swap(pt1a, pt1b); - if (pt2a.X > pt2b.X) std::swap(pt2a, pt2b); + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; return pt1.X < pt2.X; } else { - if (pt1a.Y < pt1b.Y) std::swap(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) std::swap(pt2a, pt2b); + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; return pt1.Y > pt2.Y; @@ -686,7 +800,6 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) } //------------------------------------------------------------------------------ -// Called by GetLowermostRec() OutPt* GetBottomPt(OutPt *pp) { OutPt* dups = 0; @@ -748,6 +861,18 @@ bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) // ClipperBase class methods ... //------------------------------------------------------------------------------ +ClipperBase::ClipperBase() //constructor +{ + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + // Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. inline void RangeTest(const IntPoint& Pt, bool& useFullRange) { @@ -903,7 +1028,6 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { - PROFILE_FUNC(); #ifdef use_lines if (!Closed && PolyTyp == ptClip) throw clipperException("AddPath: Open paths must be subject."); @@ -920,7 +1044,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; //create a new edge array ... - std::vector edges(highI + 1); + TEdge *edges = new TEdge [highI +1]; //1. Basic (first) edge initialization ... try @@ -938,6 +1062,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) } catch(...) { + delete [] edges; throw; //range test fails } TEdge *eStart = &edges[0]; @@ -978,6 +1103,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) { + delete [] edges; return false; } @@ -1007,6 +1133,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { if (Closed) { + delete [] edges; return false; } E->Prev->OutIdx = Skip; @@ -1024,11 +1151,11 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) E = E->Next; } m_MinimaList.push_back(locMin); - m_edges.emplace_back(std::move(edges)); + m_edges.push_back(edges); return true; } - m_edges.emplace_back(std::move(edges)); + m_edges.push_back(edges); bool leftBoundIsForward; TEdge* EMin = 0; @@ -1087,7 +1214,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) { - PROFILE_FUNC(); bool result = false; for (Paths::size_type i = 0; i < ppg.size(); ++i) if (AddPath(ppg[i], PolyTyp, Closed)) result = true; @@ -1097,8 +1223,14 @@ bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) void ClipperBase::Clear() { - PROFILE_FUNC(); m_MinimaList.clear(); + 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; + } m_edges.clear(); m_UseFullRange = false; m_HasOpenPaths = false; @@ -1109,7 +1241,6 @@ void ClipperBase::Clear() // Sort the LML entries, initialize the left / right bound edges of each Local Minima. void ClipperBase::Reset() { - PROFILE_FUNC(); if (m_MinimaList.empty()) return; //ie nothing to process std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); @@ -1138,7 +1269,6 @@ void ClipperBase::Reset() // Returns (0,0,0,0) for an empty rectangle. IntRect ClipperBase::GetBounds() { - PROFILE_FUNC(); IntRect result; auto lm = m_MinimaList.begin(); if (lm == m_MinimaList.end()) @@ -1194,9 +1324,22 @@ Clipper::Clipper(int initOptions) : ClipperBase() //constructor } //------------------------------------------------------------------------------ +Clipper::~Clipper() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + void Clipper::Reset() { - PROFILE_FUNC(); ClipperBase::Reset(); m_Scanbeam = std::priority_queue(); m_Maxima.clear(); @@ -1205,13 +1348,23 @@ void Clipper::Reset() for (auto lm = m_MinimaList.rbegin(); lm != m_MinimaList.rend(); ++lm) m_Scanbeam.push(lm->Y); } +//------------------------------------------------------------------------------ +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} //------------------------------------------------------------------------------ bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { - PROFILE_FUNC(); if (m_HasOpenPaths) throw clipperException("Error: PolyTree struct is needed for open path clipping."); solution.resize(0); @@ -1229,7 +1382,6 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, bool Clipper::Execute(ClipType clipType, PolyTree& polytree, PolyFillType subjFillType, PolyFillType clipFillType) { - PROFILE_FUNC(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; @@ -1241,23 +1393,34 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, } //------------------------------------------------------------------------------ +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + bool Clipper::ExecuteInternal() { - PROFILE_FUNC(); bool succeeded = true; try { - PROFILE_BLOCK(Clipper_ExecuteInternal_Process); Reset(); if (m_MinimaList.empty()) return true; - cInt botY = m_Scanbeam.top(); - do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && botY == m_Scanbeam.top()); + cInt botY = PopScanbeam(); do { InsertLocalMinimaIntoAEL(botY); ProcessHorizontals(); m_GhostJoins.clear(); if (m_Scanbeam.empty()) break; - cInt topY = m_Scanbeam.top(); - do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && topY == m_Scanbeam.top()); + cInt topY = PopScanbeam(); succeeded = ProcessIntersections(topY); if (!succeeded) break; ProcessEdgesAtTopOfScanbeam(topY); @@ -1271,37 +1434,28 @@ bool Clipper::ExecuteInternal() if (succeeded) { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); - //fix orientations ... - //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? - //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); } - JoinCommonEdges(); + if (!m_Joins.empty()) JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) - // Removes duplicate points. - FixupOutPolyline(*outRec); - else - // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. - FixupOutPolygon(*outRec); - } + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); } - // For each polygon, search for exactly duplicate non-successive points. - // If such a point is found, the loop is split into two pieces. - // Search for the duplicate points is O(n^2)! - // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm + if (m_StrictSimple) DoSimplePolygons(); } @@ -1311,16 +1465,31 @@ bool Clipper::ExecuteInternal() } //------------------------------------------------------------------------------ +cInt Clipper::PopScanbeam() +{ + const cInt Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return Y; +} +//------------------------------------------------------------------------------ + void Clipper::DisposeAllOutRecs(){ - for (OutRec *outRec : m_PolyOuts) { - if (outRec->Pts) - DisposeOutPts(outRec->Pts); - delete outRec; - } + 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) const { TEdge *e = edge.PrevInAEL; @@ -1368,7 +1537,7 @@ void Clipper::SetWindingCount(TEdge &edge) const { //prev edge is 'decreasing' WindCount (WC) toward zero //so we're outside the previous polygon ... - if (std::abs(e->WindCnt) > 1) + if (Abs(e->WindCnt) > 1) { //outside prev poly but still inside another. //when reversing direction of prev poly use the same WC @@ -1416,6 +1585,22 @@ void Clipper::SetWindingCount(TEdge &edge) const } //------------------------------------------------------------------------------ +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + bool Clipper::IsContributing(const TEdge& edge) const { PolyFillType pft, pft2; @@ -1436,7 +1621,7 @@ bool Clipper::IsContributing(const TEdge& edge) const if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; break; case pftNonZero: - if (std::abs(edge.WindCnt) != 1) return false; + if (Abs(edge.WindCnt) != 1) return false; break; case pftPositive: if (edge.WindCnt != 1) return false; @@ -1516,10 +1701,8 @@ bool Clipper::IsContributing(const TEdge& edge) const } //------------------------------------------------------------------------------ -// Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { - PROFILE_FUNC(); OutPt* result; TEdge *e, *prevE; if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) @@ -1608,10 +1791,8 @@ void Clipper::CopyAELToSEL() //------------------------------------------------------------------------------ -// Called from Clipper::ExecuteInternal() void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - PROFILE_FUNC(); while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) { TEdge* lb = m_MinimaList.back().LeftBound; @@ -1784,13 +1965,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) else if (e1->PolyTyp != e2->PolyTyp) { //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && std::abs(e2->WindCnt) == 1 && + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && (m_ClipType != ctUnion || e2->WindCnt2 == 0)) { AddOutPt(e1, Pt); if (e1Contributing) e1->OutIdx = Unassigned; } - else if ((e2->WindDelta == 0) && (std::abs(e1->WindCnt) == 1) && + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && (m_ClipType != ctUnion || e1->WindCnt2 == 0)) { AddOutPt(e2, Pt); @@ -1850,13 +2031,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { case pftPositive: e1Wc = e1->WindCnt; break; case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = std::abs(e1->WindCnt); + default: e1Wc = Abs(e1->WindCnt); } switch(e2FillType) { case pftPositive: e2Wc = e2->WindCnt; break; case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = std::abs(e2->WindCnt); + default: e2Wc = Abs(e2->WindCnt); } if ( e1Contributing && e2Contributing ) @@ -1870,8 +2051,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { AddOutPt(e1, Pt); AddOutPt(e2, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); } } else if ( e1Contributing ) @@ -1879,8 +2060,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) if (e2Wc == 0 || e2Wc == 1) { AddOutPt(e1, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); } } else if ( e2Contributing ) @@ -1888,8 +2069,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) if (e1Wc == 0 || e1Wc == 1) { AddOutPt(e2, Pt); - std::swap(e1->Side, e2->Side); - std::swap(e1->OutIdx, e2->OutIdx); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); } } else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) @@ -1901,13 +2082,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { case pftPositive: e1Wc2 = e1->WindCnt2; break; case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = std::abs(e1->WindCnt2); + default: e1Wc2 = Abs(e1->WindCnt2); } switch (e2FillType2) { case pftPositive: e2Wc2 = e2->WindCnt2; break; case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = std::abs(e2->WindCnt2); + default: e2Wc2 = Abs(e2->WindCnt2); } if (e1->PolyTyp != e2->PolyTyp) @@ -1933,7 +2114,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) AddLocalMinPoly(e1, e2, Pt); } else - std::swap(e1->Side, e2->Side); + SwapSides( *e1, *e2 ); } } //------------------------------------------------------------------------------ @@ -2161,7 +2342,6 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - PROFILE_FUNC(); TEdge* horzEdge = m_SortedEdges; while(horzEdge) { @@ -2172,6 +2352,12 @@ void Clipper::ProcessHorizontals() } //------------------------------------------------------------------------------ +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + inline bool IsMaxima(TEdge *e, const cInt Y) { return e && e->Top.Y == Y && !e->NextInLML; @@ -2184,7 +2370,7 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) } //------------------------------------------------------------------------------ -inline TEdge *GetMaximaPair(TEdge *e) +TEdge *GetMaximaPair(TEdge *e) { TEdge* result = 0; if ((e->Next->Top == e->Top) && !e->Next->NextInLML) @@ -2293,7 +2479,13 @@ void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) } //------------------------------------------------------------------------------ -inline void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) { if (HorzEdge.Bot.X < HorzEdge.Top.X) { @@ -2333,8 +2525,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - std::vector::const_iterator maxIt; - std::vector::const_reverse_iterator maxRit; + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; if (!m_Maxima.empty()) { //get the first maxima in range (X) ... @@ -2360,7 +2552,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = (dir == dLeftToRight) ? horzEdge->NextInAEL : horzEdge->PrevInAEL; + TEdge* e = GetNextInAEL(horzEdge, dir); while(e) { @@ -2436,7 +2628,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges( e, horzEdge, Pt); } - TEdge* eNext = (dir == dLeftToRight) ? e->NextInAEL : e->PrevInAEL; + TEdge* eNext = GetNextInAEL(e, dir); SwapPositionsInAEL( horzEdge, e ); e = eNext; } //end while(e) @@ -2532,25 +2724,18 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e) bool Clipper::ProcessIntersections(const cInt topY) { - PROFILE_FUNC(); if( !m_ActiveEdges ) return true; try { BuildIntersectList(topY); size_t IlSize = m_IntersectList.size(); if (IlSize == 0) return true; - if (IlSize == 1 || FixupIntersectionOrder()) { - for (IntersectNode &iNode : m_IntersectList) { - IntersectEdges( iNode.Edge1, iNode.Edge2, iNode.Pt); - SwapPositionsInAEL( iNode.Edge1 , iNode.Edge2 ); - } - m_IntersectList.clear(); - } + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); else return false; } catch(...) { m_SortedEdges = 0; - m_IntersectList.clear(); + DisposeIntersectNodes(); throw clipperException("ProcessIntersections error"); } m_SortedEdges = 0; @@ -2558,6 +2743,14 @@ bool Clipper::ProcessIntersections(const cInt topY) } //------------------------------------------------------------------------------ +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + void Clipper::BuildIntersectList(const cInt topY) { if ( !m_ActiveEdges ) return; @@ -2586,7 +2779,12 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.X > eNext->Curr.X) { IntersectPoint(*e, *eNext, Pt); - m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + SwapPositionsInSEL(e, eNext); isModified = true; } @@ -2602,6 +2800,21 @@ void Clipper::BuildIntersectList(const cInt topY) //------------------------------------------------------------------------------ +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + inline bool EdgesAdjacent(const IntersectNode &inode) { return (inode.Edge1->NextInSEL == inode.Edge2) || @@ -2615,19 +2828,19 @@ bool Clipper::FixupIntersectionOrder() //Now it's crucial that intersections are made only between adjacent edges, //so to ensure this the order of intersections may need adjusting ... CopyAELToSEL(); - std::sort(m_IntersectList.begin(), m_IntersectList.end(), [](IntersectNode &node1, IntersectNode &node2) { return node2.Pt.Y < node1.Pt.Y; }); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), [](IntersectNode* node1, IntersectNode* node2) { return node2->Pt.Y < node1->Pt.Y; }); size_t cnt = m_IntersectList.size(); for (size_t i = 0; i < cnt; ++i) { - if (!EdgesAdjacent(m_IntersectList[i])) + if (!EdgesAdjacent(*m_IntersectList[i])) { size_t j = i + 1; - while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; if (j == cnt) return false; std::swap(m_IntersectList[i], m_IntersectList[j]); } - SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2); + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); } return true; } @@ -2687,7 +2900,6 @@ void Clipper::DoMaxima(TEdge *e) void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { - PROFILE_FUNC(); TEdge* e = m_ActiveEdges; while( e ) { @@ -2748,7 +2960,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //3. Process horizontals at the Top of the scanbeam ... - std::sort(m_Maxima.begin(), m_Maxima.end()); + m_Maxima.sort(); ProcessHorizontals(); m_Maxima.clear(); @@ -2821,8 +3033,8 @@ void Clipper::FixupOutPolygon(OutRec &outrec) { //FixupOutPolygon() - removes duplicate points and simplifies consecutive //parallel edges by removing the middle vertex. - OutPt *lastOK = nullptr; - outrec.BottomPt = nullptr; + OutPt *lastOK = 0; + outrec.BottomPt = 0; OutPt *pp = outrec.Pts; bool preserveCol = m_PreserveCollinear || m_StrictSimple; @@ -2830,9 +3042,8 @@ void Clipper::FixupOutPolygon(OutRec &outrec) { if (pp->Prev == pp || pp->Prev == pp->Next) { - // Empty loop or a stick. Release the polygon. DisposeOutPts(pp); - outrec.Pts = nullptr; + outrec.Pts = 0; return; } @@ -2841,7 +3052,7 @@ void Clipper::FixupOutPolygon(OutRec &outrec) (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) { - lastOK = nullptr; + lastOK = 0; OutPt *tmp = pp; pp->Prev->Next = pp->Next; pp->Next->Prev = pp->Prev; @@ -2859,7 +3070,6 @@ void Clipper::FixupOutPolygon(OutRec &outrec) } //------------------------------------------------------------------------------ -// Count the number of points in a closed linked loop starting with Pts. int PointCount(OutPt *Pts) { if (!Pts) return 0; @@ -2878,21 +3088,20 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; + if (!m_PolyOuts[i]->Pts) continue; Path pg; - OutPt* p = outRec->Pts->Prev; + OutPt* p = m_PolyOuts[i]->Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); for (int i = 0; i < cnt; ++i) { - pg.emplace_back(p->Pt); + pg.push_back(p->Pt); p = p->Prev; } - polys.emplace_back(std::move(pg)); + polys.push_back(pg); } } //------------------------------------------------------------------------------ @@ -2902,26 +3111,15 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) { + OutRec* outRec = m_PolyOuts[i]; int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) - // Ignore an invalid output loop or a polyline. - continue; - - //skip OutRecs that (a) contain outermost polygons or - //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) - orfl = orfl->FirstLeft; - outRec->FirstLeft = orfl; - } - + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.emplace_back(PolyNode()); - PolyNode* pn = &polytree.AllNodes.back(); + polytree.AllNodes.push_back(pn); outRec->PolyNd = pn; pn->Parent = 0; pn->Index = 0; @@ -2929,15 +3127,16 @@ void Clipper::BuildResult2(PolyTree& polytree) OutPt *op = outRec->Pts->Prev; for (int j = 0; j < cnt; j++) { - pn->Contour.emplace_back(op->Pt); + pn->Contour.push_back(op->Pt); op = op->Prev; } } //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) { + OutRec* outRec = m_PolyOuts[i]; if (!outRec->PolyNd) continue; if (outRec->IsOpen) { @@ -2952,6 +3151,19 @@ void Clipper::BuildResult2(PolyTree& polytree) } //------------------------------------------------------------------------------ +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) { if (e2.Curr.X == e1.Curr.X) @@ -2981,7 +3193,6 @@ bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, } //------------------------------------------------------------------------------ -// Make all points of outrec point to outrec.Idx inline void UpdateOutPtIdxs(OutRec& outrec) { OutPt* op = outrec.Pts; @@ -3292,19 +3503,27 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) } //---------------------------------------------------------------------- -// This is potentially very expensive! O(n^3)! +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const { - PROFILE_FUNC(); //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (OutRec *outRec : m_PolyOuts) + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { + OutRec* outRec = m_PolyOuts[i]; if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = outRec->FirstLeft; - // Skip empty polygons. - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; + } } } //---------------------------------------------------------------------- @@ -3312,14 +3531,16 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const { //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (OutRec *outRec : m_PolyOuts) + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + } } //---------------------------------------------------------------------- void Clipper::JoinCommonEdges() { - PROFILE_FUNC(); for (Join &join : m_Joins) { OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); @@ -3353,12 +3574,10 @@ void Clipper::JoinCommonEdges() //We now need to check every OutRec.FirstLeft pointer. If it points //to OutRec1 it may need to point to OutRec2 instead ... if (m_UsingPolyTree) - for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) + for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) { OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec->Pts || firstLeft != outRec1 || + if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || oRec->IsHole == outRec1->IsHole) continue; if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) oRec->FirstLeft = outRec2; @@ -3370,7 +3589,7 @@ void Clipper::JoinCommonEdges() outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; - // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. + //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) @@ -3384,7 +3603,7 @@ void Clipper::JoinCommonEdges() outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; - // For each m_PolyOuts, replace FirstLeft from outRec1 to outRec2. + //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) @@ -3397,8 +3616,6 @@ void Clipper::JoinCommonEdges() outRec2->FirstLeft = outRec1->FirstLeft; //fixup FirstLeft pointers that may need reassigning to OutRec2 - // For each polygon of m_PolyOuts, replace FirstLeft from outRec1 to outRec2 if the polygon is inside outRec2. - //FIXME This is potentially very expensive! O(n^3)! if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); } @@ -3415,7 +3632,7 @@ void Clipper::JoinCommonEdges() outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; - // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. + //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); } } @@ -3450,6 +3667,12 @@ ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) } //------------------------------------------------------------------------------ +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + void ClipperOffset::Clear() { for (int i = 0; i < m_polyNodes.ChildCount(); ++i) @@ -3730,11 +3953,11 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { int j = len - 1; - pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * - delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); m_destPoly.push_back(pt1); - pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * - delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); m_destPoly.push_back(pt1); } else @@ -3759,11 +3982,11 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { - pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), - Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); m_destPoly.push_back(pt1); - pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), - Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); m_destPoly.push_back(pt1); } else @@ -3871,15 +4094,9 @@ void ClipperOffset::DoRound(int j, int k) // Miscellaneous public functions //------------------------------------------------------------------------------ -// Called by Clipper::ExecuteInternal() -// For each polygon, search for exactly duplicate non-successive points. -// If such a point is found, the loop is split into two pieces. -// Search for the duplicate points is O(n^2)! -// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm void Clipper::DoSimplePolygons() { - PROFILE_FUNC(); - size_t i = 0; + PolyOutList::size_type i = 0; while (i < m_PolyOuts.size()) { OutRec* outrec = m_PolyOuts[i++]; @@ -3909,7 +4126,6 @@ void Clipper::DoSimplePolygons() //OutRec2 is contained by OutRec1 ... outrec2->IsHole = !outrec->IsHole; outrec2->FirstLeft = outrec; - // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); } else @@ -3920,7 +4136,6 @@ void Clipper::DoSimplePolygons() outrec->IsHole = !outrec2->IsHole; outrec2->FirstLeft = outrec->FirstLeft; outrec->FirstLeft = outrec2; - // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); } else @@ -3928,10 +4143,8 @@ void Clipper::DoSimplePolygons() //the 2 polygons are separate ... outrec2->IsHole = outrec->IsHole; outrec2->FirstLeft = outrec->FirstLeft; - // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. - //FIXME This is potentially very expensive! O(n^3)! if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); - } + } op2 = op; //ie get ready for the Next iteration } op2 = op2->Next; @@ -4011,7 +4224,7 @@ bool SlopesNearCollinear(const IntPoint& pt1, //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 (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y)) + 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; @@ -4050,7 +4263,6 @@ OutPt* ExcludeOp(OutPt* op) } //------------------------------------------------------------------------------ -// Simplify a polygon using a linked list of points. void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) { //distance = proximity in units/pixels below which vertices @@ -4064,7 +4276,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) return; } - std::vector outPts(size); + OutPt* outPts = new OutPt[size]; for (size_t i = 0; i < size; ++i) { outPts[i].Pt = in_poly[i]; @@ -4107,6 +4319,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) out_poly[i] = op->Pt; op = op->Next; } + delete [] outPts; } //------------------------------------------------------------------------------ diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 11626e396..743373a86 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -50,7 +50,8 @@ //#define use_deprecated #include -#include +#include +#include #include #include #include @@ -135,22 +136,21 @@ typedef std::vector< PolyNode* > PolyNodes; class PolyNode { public: - PolyNode() : Childs(), Parent(0), Index(0), m_IsOpen(false) {} + PolyNode(); virtual ~PolyNode(){}; Path Contour; PolyNodes Childs; PolyNode* Parent; - // Traversal of the polygon tree in a depth first fashion. - PolyNode* GetNext() const { return Childs.empty() ? GetNextSiblingUp() : Childs.front(); } + PolyNode* GetNext() const; bool IsHole() const; - bool IsOpen() const { return m_IsOpen; } - int ChildCount() const { return (int)Childs.size(); } + bool IsOpen() const; + int ChildCount() const; private: unsigned Index; //node index in Parent.Childs bool m_IsOpen; JoinType m_jointype; EndType m_endtype; - PolyNode* GetNextSiblingUp() const { return Parent ? ((Index == Parent->Childs.size() - 1) ? Parent->GetNextSiblingUp() : Parent->Childs[Index + 1]) : nullptr; } + PolyNode* GetNextSiblingUp() const; void AddChild(PolyNode& child); friend class Clipper; //to access Index friend class ClipperOffset; @@ -176,13 +176,13 @@ public: Childs[i]->Parent = this; return *this; } - PolyNode* GetFirst() const { return Childs.empty() ? nullptr : Childs.front(); } - void Clear() { AllNodes.clear(); Childs.clear(); } + PolyNode* GetFirst() const; + void Clear(); int Total() const; private: PolyTree(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete; - std::vector AllNodes; + PolyNodes AllNodes; friend class Clipper; //to access AllNodes }; @@ -215,62 +215,24 @@ struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; //enums that are used internally ... enum EdgeSide { esLeft = 1, esRight = 2}; -// namespace Internal { - //forward declarations (for stuff used internally) ... - struct TEdge { - // Bottom point of this edge (with minimum Y). - IntPoint Bot; - // Current position. - IntPoint Curr; - // Top point of this edge (with maximum Y). - IntPoint Top; - // Vector from Bot to Top. - IntPoint Delta; - // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). - double Dx; - PolyType PolyTyp; - EdgeSide Side; - // Winding number delta. 1 or -1 depending on winding direction, 0 for open paths and flat closed paths. - int WindDelta; - int WindCnt; - int WindCnt2; //winding count of the opposite polytype - int OutIdx; - // Next edge in the input path. - TEdge *Next; - // Previous edge in the input path. - TEdge *Prev; - // Next edge in the Local Minima List chain. - TEdge *NextInLML; - TEdge *NextInAEL; - TEdge *PrevInAEL; - TEdge *NextInSEL; - TEdge *PrevInSEL; - }; +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join { + Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : + OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; - struct IntersectNode { - IntersectNode(TEdge *Edge1, TEdge *Edge2, IntPoint Pt) : - Edge1(Edge1), Edge2(Edge2), Pt(Pt) {} - TEdge *Edge1; - TEdge *Edge2; - IntPoint Pt; - }; - - struct LocalMinimum { - cInt Y; - TEdge *LeftBound; - TEdge *RightBound; - }; - - struct OutPt; - struct OutRec; - struct Join { - Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : - OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} - OutPt *OutPt1; - OutPt *OutPt2; - IntPoint OffPt; - }; -// }; // namespace Internal +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join > JoinList; +typedef std::vector < IntersectNode* > IntersectList; //------------------------------------------------------------------------------ @@ -280,8 +242,8 @@ enum EdgeSide { esLeft = 1, esRight = 2}; class ClipperBase { public: - ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {} - virtual ~ClipperBase() { Clear(); } + ClipperBase(); + virtual ~ClipperBase(); bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); virtual void Clear(); @@ -292,6 +254,7 @@ public: void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; protected: TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); TEdge* DescendToMin(TEdge *&E); @@ -304,7 +267,7 @@ protected: // False if the input polygons have abs values lower or equal to loRange. bool m_UseFullRange; // A vector of edges per each input path. - std::vector> m_edges; + EdgeList m_edges; // Don't remove intermediate vertices of a collinear sequence of points. bool m_PreserveCollinear; // Is any of the paths inserted by AddPath() or AddPaths() open? @@ -316,19 +279,17 @@ class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); - ~Clipper() { Clear(); } + ~Clipper(); bool Execute(ClipType clipType, Paths &solution, - PolyFillType fillType = pftEvenOdd) - { return Execute(clipType, solution, fillType, fillType); } + PolyFillType fillType = pftEvenOdd); bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType); bool Execute(ClipType clipType, PolyTree &polytree, - PolyFillType fillType = pftEvenOdd) - { return Execute(clipType, polytree, fillType, fillType); } + PolyFillType fillType = pftEvenOdd); bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType, @@ -339,21 +300,21 @@ public: 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(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } + void ZFillFunction(ZFillCallback zFillFunc); #endif protected: void Reset(); virtual bool ExecuteInternal(); private: - std::vector m_PolyOuts; - std::vector m_Joins; - std::vector m_GhostJoins; - std::vector m_IntersectList; + PolyOutList m_PolyOuts; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; ClipType m_ClipType; // A priority queue (a binary heap) of Y coordinates. std::priority_queue m_Scanbeam; - // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). - std::vector m_Maxima; + typedef std::list MaximaList; + MaximaList m_Maxima; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; PolyFillType m_ClipFillType; @@ -366,10 +327,9 @@ private: ZFillCallback m_ZFill; //custom callback #endif void SetWindingCount(TEdge& edge) const; - bool IsEvenOddFillType(const TEdge& edge) const - { return (edge.PolyTyp == ptSubject) ? m_SubjFillType == pftEvenOdd : m_ClipFillType == pftEvenOdd; } - bool IsEvenOddAltFillType(const TEdge& edge) const - { return (edge.PolyTyp == ptSubject) ? m_ClipFillType == pftEvenOdd : m_SubjFillType == pftEvenOdd; } + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + cInt PopScanbeam(); void InsertLocalMinimaIntoAEL(const cInt botY); void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void AddEdgeToSEL(TEdge *edge); @@ -393,12 +353,15 @@ private: OutPt* AddOutPt(TEdge *e, const IntPoint &pt); OutPt* GetLastOutPt(TEdge *e); void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); bool ProcessIntersections(const cInt topY); void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); void SetHoleState(TEdge *e, OutRec *outrec) const; + void DisposeIntersectNodes(); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); void FixupOutPolyline(OutRec &outrec); @@ -419,7 +382,7 @@ class ClipperOffset { public: ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset() { Clear(); } + ~ClipperOffset(); void AddPath(const Path& path, JoinType joinType, EndType endType); void AddPaths(const Paths& paths, JoinType joinType, EndType endType); void Execute(Paths& solution, double delta);