diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index bac473d4b..1eac45a70 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -48,6 +48,7 @@ #include #include #include +#include namespace ClipperLib { @@ -64,60 +65,7 @@ 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)) -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; -}; - +// Point of an output polygon. struct OutPt { int Idx; IntPoint Pt; @@ -125,48 +73,41 @@ 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) { - 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; + return static_cast((val < 0) ? (val - 0.5) : (val + 0.5)); } //------------------------------------------------------------------------------ // 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[0] != AllNodes[0]) result--; + if (result > 0 && Childs.front() != &AllNodes.front()) result--; return result; } @@ -174,17 +115,6 @@ 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(); @@ -194,26 +124,6 @@ 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 { @@ -228,12 +138,6 @@ bool PolyNode::IsHole() const } //------------------------------------------------------------------------------ -bool PolyNode::IsOpen() const -{ - return m_IsOpen; -} -//------------------------------------------------------------------------------ - #ifndef use_int32 //------------------------------------------------------------------------------ @@ -359,9 +263,12 @@ inline Int128 Int128Mul (long64 lhs, long64 rhs) ulong64 int2Hi = ulong64(rhs) >> 32; ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); - //nb: see comments in clipper.pas + //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) ulong64 a = int1Hi * int2Hi; ulong64 b = int1Lo * int2Lo; + //Result = A shl 64 + C shl 32 + B ... ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; Int128 tmp; @@ -471,12 +378,13 @@ 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; - for(;;) + do { if (op->Next->Pt.Y == pt.Y) { @@ -507,14 +415,15 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) } } op = op->Next; - if (startOp == op) break; - } + } while (startOp != op); return result; } //------------------------------------------------------------------------------ +// This is potentially very expensive! O(n^2)! bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { + PROFILE_FUNC(); OutPt* op = OutPt1; do { @@ -576,22 +485,6 @@ 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 ) ? @@ -669,16 +562,17 @@ 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, *pp2; - pp1 = pp; + OutPt *pp1 = pp; do { - pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; + OutPt *pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; } while( pp1 != pp ); } //------------------------------------------------------------------------------ @@ -751,29 +645,21 @@ 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 (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + if (std::abs(pt1a.X - pt1b.X) > std::abs(pt1a.Y - pt1b.Y)) { - if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); - if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt1b.X) std::swap(pt1a, pt1b); + if (pt2a.X > pt2b.X) std::swap(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) SwapPoints(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt1b.Y) std::swap(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) std::swap(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; @@ -800,6 +686,7 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) } //------------------------------------------------------------------------------ +// Called by GetLowermostRec() OutPt* GetBottomPt(OutPt *pp) { OutPt* dups = 0; @@ -861,18 +748,6 @@ 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) { @@ -1028,6 +903,7 @@ 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."); @@ -1044,7 +920,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 ... - TEdge *edges = new TEdge [highI +1]; + std::vector edges(highI + 1); //1. Basic (first) edge initialization ... try @@ -1062,7 +938,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) } catch(...) { - delete [] edges; throw; //range test fails } TEdge *eStart = &edges[0]; @@ -1103,7 +978,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) { - delete [] edges; return false; } @@ -1133,7 +1007,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { if (Closed) { - delete [] edges; return false; } E->Prev->OutIdx = Skip; @@ -1151,11 +1024,11 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) E = E->Next; } m_MinimaList.push_back(locMin); - m_edges.push_back(edges); + m_edges.emplace_back(std::move(edges)); return true; } - m_edges.push_back(edges); + m_edges.emplace_back(std::move(edges)); bool leftBoundIsForward; TEdge* EMin = 0; @@ -1214,6 +1087,7 @@ 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; @@ -1223,14 +1097,8 @@ 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; @@ -1241,6 +1109,7 @@ 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; }); @@ -1269,6 +1138,7 @@ 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()) @@ -1324,22 +1194,9 @@ 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(); @@ -1348,23 +1205,13 @@ 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); @@ -1382,6 +1229,7 @@ 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; @@ -1393,34 +1241,23 @@ 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 = PopScanbeam(); + cInt botY = m_Scanbeam.top(); + do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && botY == m_Scanbeam.top()); do { InsertLocalMinimaIntoAEL(botY); ProcessHorizontals(); m_GhostJoins.clear(); if (m_Scanbeam.empty()) break; - cInt topY = PopScanbeam(); + cInt topY = m_Scanbeam.top(); + do { m_Scanbeam.pop(); } while (! m_Scanbeam.empty() && topY == m_Scanbeam.top()); succeeded = ProcessIntersections(topY); if (!succeeded) break; ProcessEdgesAtTopOfScanbeam(topY); @@ -1434,28 +1271,37 @@ bool Clipper::ExecuteInternal() if (succeeded) { + PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); + //fix orientations ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + //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! { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts || outRec->IsOpen) continue; - if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + 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); } - if (!m_Joins.empty()) JoinCommonEdges(); + JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts) continue; - if (outRec->IsOpen) - FixupOutPolyline(*outRec); - else - FixupOutPolygon(*outRec); + 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); + } } - + // 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(); } @@ -1465,31 +1311,16 @@ 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 (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); + for (OutRec *outRec : m_PolyOuts) { + if (outRec->Pts) + DisposeOutPts(outRec->Pts); + delete outRec; + } 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; @@ -1537,7 +1368,7 @@ void Clipper::SetWindingCount(TEdge &edge) const { //prev edge is 'decreasing' WindCount (WC) toward zero //so we're outside the previous polygon ... - if (Abs(e->WindCnt) > 1) + if (std::abs(e->WindCnt) > 1) { //outside prev poly but still inside another. //when reversing direction of prev poly use the same WC @@ -1585,22 +1416,6 @@ 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; @@ -1621,7 +1436,7 @@ bool Clipper::IsContributing(const TEdge& edge) const if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; break; case pftNonZero: - if (Abs(edge.WindCnt) != 1) return false; + if (std::abs(edge.WindCnt) != 1) return false; break; case pftPositive: if (edge.WindCnt != 1) return false; @@ -1701,8 +1516,10 @@ 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 )) @@ -1791,8 +1608,10 @@ 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; @@ -1965,13 +1784,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) && abs(e2->WindCnt) == 1 && + if ((e1->WindDelta == 0) && std::abs(e2->WindCnt) == 1 && (m_ClipType != ctUnion || e2->WindCnt2 == 0)) { AddOutPt(e1, Pt); if (e1Contributing) e1->OutIdx = Unassigned; } - else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + else if ((e2->WindDelta == 0) && (std::abs(e1->WindCnt) == 1) && (m_ClipType != ctUnion || e1->WindCnt2 == 0)) { AddOutPt(e2, Pt); @@ -2031,13 +1850,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { case pftPositive: e1Wc = e1->WindCnt; break; case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = Abs(e1->WindCnt); + default: e1Wc = std::abs(e1->WindCnt); } switch(e2FillType) { case pftPositive: e2Wc = e2->WindCnt; break; case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = Abs(e2->WindCnt); + default: e2Wc = std::abs(e2->WindCnt); } if ( e1Contributing && e2Contributing ) @@ -2051,8 +1870,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { AddOutPt(e1, Pt); AddOutPt(e2, Pt); - SwapSides( *e1 , *e2 ); - SwapPolyIndexes( *e1 , *e2 ); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); } } else if ( e1Contributing ) @@ -2060,8 +1879,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) if (e2Wc == 0 || e2Wc == 1) { AddOutPt(e1, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); } } else if ( e2Contributing ) @@ -2069,8 +1888,8 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) if (e1Wc == 0 || e1Wc == 1) { AddOutPt(e2, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); + std::swap(e1->Side, e2->Side); + std::swap(e1->OutIdx, e2->OutIdx); } } else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) @@ -2082,13 +1901,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { case pftPositive: e1Wc2 = e1->WindCnt2; break; case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = Abs(e1->WindCnt2); + default: e1Wc2 = std::abs(e1->WindCnt2); } switch (e2FillType2) { case pftPositive: e2Wc2 = e2->WindCnt2; break; case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = Abs(e2->WindCnt2); + default: e2Wc2 = std::abs(e2->WindCnt2); } if (e1->PolyTyp != e2->PolyTyp) @@ -2114,7 +1933,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) AddLocalMinPoly(e1, e2, Pt); } else - SwapSides( *e1, *e2 ); + std::swap(e1->Side, e2->Side); } } //------------------------------------------------------------------------------ @@ -2342,6 +2161,7 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { + PROFILE_FUNC(); TEdge* horzEdge = m_SortedEdges; while(horzEdge) { @@ -2352,12 +2172,6 @@ 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; @@ -2370,7 +2184,7 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) } //------------------------------------------------------------------------------ -TEdge *GetMaximaPair(TEdge *e) +inline TEdge *GetMaximaPair(TEdge *e) { TEdge* result = 0; if ((e->Next->Top == e->Top) && !e->Next->NextInLML) @@ -2479,13 +2293,7 @@ void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) } //------------------------------------------------------------------------------ -TEdge* GetNextInAEL(TEdge *e, Direction dir) -{ - return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; -} -//------------------------------------------------------------------------------ - -void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +inline void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) { if (HorzEdge.Bot.X < HorzEdge.Top.X) { @@ -2525,8 +2333,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; + std::vector::const_iterator maxIt; + std::vector::const_reverse_iterator maxRit; if (!m_Maxima.empty()) { //get the first maxima in range (X) ... @@ -2552,7 +2360,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = GetNextInAEL(horzEdge, dir); + TEdge* e = (dir == dLeftToRight) ? horzEdge->NextInAEL : horzEdge->PrevInAEL; while(e) { @@ -2628,7 +2436,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntersectEdges( e, horzEdge, Pt); } - TEdge* eNext = GetNextInAEL(e, dir); + TEdge* eNext = (dir == dLeftToRight) ? e->NextInAEL : e->PrevInAEL; SwapPositionsInAEL( horzEdge, e ); e = eNext; } //end while(e) @@ -2724,18 +2532,25 @@ 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()) ProcessIntersectList(); + if (IlSize == 1 || FixupIntersectionOrder()) { + for (IntersectNode &iNode : m_IntersectList) { + IntersectEdges( iNode.Edge1, iNode.Edge2, iNode.Pt); + SwapPositionsInAEL( iNode.Edge1 , iNode.Edge2 ); + } + m_IntersectList.clear(); + } else return false; } catch(...) { m_SortedEdges = 0; - DisposeIntersectNodes(); + m_IntersectList.clear(); throw clipperException("ProcessIntersections error"); } m_SortedEdges = 0; @@ -2743,14 +2558,6 @@ 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; @@ -2779,12 +2586,7 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.X > eNext->Curr.X) { IntersectPoint(*e, *eNext, Pt); - IntersectNode * newNode = new IntersectNode; - newNode->Edge1 = e; - newNode->Edge2 = eNext; - newNode->Pt = Pt; - m_IntersectList.push_back(newNode); - + m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); SwapPositionsInSEL(e, eNext); isModified = true; } @@ -2800,21 +2602,6 @@ 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) || @@ -2828,19 +2615,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; } @@ -2900,6 +2687,7 @@ void Clipper::DoMaxima(TEdge *e) void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { + PROFILE_FUNC(); TEdge* e = m_ActiveEdges; while( e ) { @@ -2960,7 +2748,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } //3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); + std::sort(m_Maxima.begin(), m_Maxima.end()); ProcessHorizontals(); m_Maxima.clear(); @@ -3033,8 +2821,8 @@ 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 *lastOK = nullptr; + outrec.BottomPt = nullptr; OutPt *pp = outrec.Pts; bool preserveCol = m_PreserveCollinear || m_StrictSimple; @@ -3042,8 +2830,9 @@ 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 = 0; + outrec.Pts = nullptr; return; } @@ -3052,7 +2841,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 = 0; + lastOK = nullptr; OutPt *tmp = pp; pp->Prev->Next = pp->Next; pp->Next->Prev = pp->Prev; @@ -3070,6 +2859,7 @@ 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; @@ -3088,20 +2878,21 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + for (OutRec* outRec : m_PolyOuts) { - if (!m_PolyOuts[i]->Pts) continue; + assert(! outRec->IsOpen); + if (!outRec->Pts) continue; Path pg; - OutPt* p = m_PolyOuts[i]->Pts->Prev; + OutPt* p = outRec->Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); for (int i = 0; i < cnt; ++i) { - pg.push_back(p->Pt); + pg.emplace_back(p->Pt); p = p->Prev; } - polys.push_back(pg); + polys.emplace_back(std::move(pg)); } } //------------------------------------------------------------------------------ @@ -3111,15 +2902,26 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + for (OutRec* outRec : m_PolyOuts) { - OutRec* outRec = m_PolyOuts[i]; int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; - FixHoleLinkage(*outRec); - PolyNode* pn = new PolyNode(); + 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; + } + //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.push_back(pn); + polytree.AllNodes.emplace_back(PolyNode()); + PolyNode* pn = &polytree.AllNodes.back(); outRec->PolyNd = pn; pn->Parent = 0; pn->Index = 0; @@ -3127,16 +2929,15 @@ void Clipper::BuildResult2(PolyTree& polytree) OutPt *op = outRec->Pts->Prev; for (int j = 0; j < cnt; j++) { - pn->Contour.push_back(op->Pt); + pn->Contour.emplace_back(op->Pt); op = op->Prev; } } //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + for (OutRec* outRec : m_PolyOuts) { - OutRec* outRec = m_PolyOuts[i]; if (!outRec->PolyNd) continue; if (outRec->IsOpen) { @@ -3151,19 +2952,6 @@ 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) @@ -3193,6 +2981,7 @@ 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; @@ -3503,27 +3292,19 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) } //---------------------------------------------------------------------- -static OutRec* ParseFirstLeft(OutRec* FirstLeft) -{ - while (FirstLeft && !FirstLeft->Pts) - FirstLeft = FirstLeft->FirstLeft; - return FirstLeft; -} -//------------------------------------------------------------------------------ - +// This is potentially very expensive! O(n^3)! void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const { + PROFILE_FUNC(); //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + for (OutRec *outRec : m_PolyOuts) { - OutRec* outRec = m_PolyOuts[i]; if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft == OldOutRec) - { - if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + OutRec* firstLeft = outRec->FirstLeft; + // Skip empty polygons. + while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; + if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; - } } } //---------------------------------------------------------------------- @@ -3531,16 +3312,14 @@ 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 (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; + for (OutRec *outRec : m_PolyOuts) if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; - } } //---------------------------------------------------------------------- void Clipper::JoinCommonEdges() { + PROFILE_FUNC(); for (Join &join : m_Joins) { OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); @@ -3574,10 +3353,12 @@ 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 (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) + for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) { OutRec* oRec = m_PolyOuts[j]; - if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || + OutRec* firstLeft = oRec->FirstLeft; + while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; + if (!oRec->Pts || firstLeft != outRec1 || oRec->IsHole == outRec1->IsHole) continue; if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) oRec->FirstLeft = outRec2; @@ -3589,7 +3370,7 @@ void Clipper::JoinCommonEdges() outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 + // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) @@ -3603,7 +3384,7 @@ void Clipper::JoinCommonEdges() outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; - //fixup FirstLeft pointers that may need reassigning to OutRec1 + // For each m_PolyOuts, replace FirstLeft from outRec1 to outRec2. if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) @@ -3616,6 +3397,8 @@ 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); } @@ -3632,7 +3415,7 @@ void Clipper::JoinCommonEdges() outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 + // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); } } @@ -3667,12 +3450,6 @@ ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) } //------------------------------------------------------------------------------ -ClipperOffset::~ClipperOffset() -{ - Clear(); -} -//------------------------------------------------------------------------------ - void ClipperOffset::Clear() { for (int i = 0; i < m_polyNodes.ChildCount(); ++i) @@ -3953,11 +3730,11 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { int j = len - 1; - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * + delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * + delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); m_destPoly.push_back(pt1); } else @@ -3982,11 +3759,11 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), + Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), + Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); m_destPoly.push_back(pt1); } else @@ -4094,9 +3871,15 @@ 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() { - PolyOutList::size_type i = 0; + PROFILE_FUNC(); + size_t i = 0; while (i < m_PolyOuts.size()) { OutRec* outrec = m_PolyOuts[i++]; @@ -4126,6 +3909,7 @@ 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 @@ -4136,6 +3920,7 @@ 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 @@ -4143,8 +3928,10 @@ 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; @@ -4224,7 +4011,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 (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + if (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y)) { if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; @@ -4263,6 +4050,7 @@ 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 @@ -4276,7 +4064,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) return; } - OutPt* outPts = new OutPt[size]; + std::vector outPts(size); for (size_t i = 0; i < size; ++i) { outPts[i].Pt = in_poly[i]; @@ -4319,7 +4107,6 @@ 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 743373a86..11626e396 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -50,8 +50,7 @@ //#define use_deprecated #include -#include -#include +#include #include #include #include @@ -136,21 +135,22 @@ typedef std::vector< PolyNode* > PolyNodes; class PolyNode { public: - PolyNode(); + PolyNode() : Childs(), Parent(0), Index(0), m_IsOpen(false) {} virtual ~PolyNode(){}; Path Contour; PolyNodes Childs; PolyNode* Parent; - PolyNode* GetNext() const; + // Traversal of the polygon tree in a depth first fashion. + PolyNode* GetNext() const { return Childs.empty() ? GetNextSiblingUp() : Childs.front(); } bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; + bool IsOpen() const { return m_IsOpen; } + int ChildCount() const { return (int)Childs.size(); } private: unsigned Index; //node index in Parent.Childs bool m_IsOpen; JoinType m_jointype; EndType m_endtype; - PolyNode* GetNextSiblingUp() const; + PolyNode* GetNextSiblingUp() const { return Parent ? ((Index == Parent->Childs.size() - 1) ? Parent->GetNextSiblingUp() : Parent->Childs[Index + 1]) : nullptr; } 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; - void Clear(); + PolyNode* GetFirst() const { return Childs.empty() ? nullptr : Childs.front(); } + void Clear() { AllNodes.clear(); Childs.clear(); } int Total() const; private: PolyTree(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete; - PolyNodes AllNodes; + std::vector AllNodes; friend class Clipper; //to access AllNodes }; @@ -215,24 +215,62 @@ struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; //enums that are used internally ... enum EdgeSide { esLeft = 1, esRight = 2}; -//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; -}; +// 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; + }; -typedef std::vector < OutRec* > PolyOutList; -typedef std::vector < TEdge* > EdgeList; -typedef std::vector < Join > JoinList; -typedef std::vector < IntersectNode* > IntersectList; + 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 //------------------------------------------------------------------------------ @@ -242,8 +280,8 @@ typedef std::vector < IntersectNode* > IntersectList; class ClipperBase { public: - ClipperBase(); - virtual ~ClipperBase(); + ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {} + virtual ~ClipperBase() { Clear(); } bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); virtual void Clear(); @@ -254,7 +292,6 @@ 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); @@ -267,7 +304,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. - EdgeList m_edges; + std::vector> 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? @@ -279,17 +316,19 @@ class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); - ~Clipper(); + ~Clipper() { Clear(); } bool Execute(ClipType clipType, Paths &solution, - PolyFillType fillType = pftEvenOdd); + PolyFillType fillType = pftEvenOdd) + { return Execute(clipType, solution, fillType, fillType); } bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType); bool Execute(ClipType clipType, PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); + PolyFillType fillType = pftEvenOdd) + { return Execute(clipType, polytree, fillType, fillType); } bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType, @@ -300,21 +339,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); + void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } #endif protected: void Reset(); virtual bool ExecuteInternal(); private: - PolyOutList m_PolyOuts; - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; + std::vector m_PolyOuts; + std::vector m_Joins; + std::vector m_GhostJoins; + std::vector m_IntersectList; ClipType m_ClipType; // A priority queue (a binary heap) of Y coordinates. std::priority_queue m_Scanbeam; - typedef std::list MaximaList; - MaximaList m_Maxima; + // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). + std::vector m_Maxima; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; PolyFillType m_ClipFillType; @@ -327,9 +366,10 @@ private: ZFillCallback m_ZFill; //custom callback #endif void SetWindingCount(TEdge& edge) const; - bool IsEvenOddFillType(const TEdge& edge) const; - bool IsEvenOddAltFillType(const TEdge& edge) const; - cInt PopScanbeam(); + 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; } void InsertLocalMinimaIntoAEL(const cInt botY); void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void AddEdgeToSEL(TEdge *edge); @@ -353,15 +393,12 @@ 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); @@ -382,7 +419,7 @@ class ClipperOffset { public: ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset(); + ~ClipperOffset() { Clear(); } void AddPath(const Path& path, JoinType joinType, EndType endType); void AddPaths(const Paths& paths, JoinType joinType, EndType endType); void Execute(Paths& solution, double delta);