This commit is contained in:
Eric Fischer
2016-02-17 13:02:56 -08:00
parent eecdf7747a
commit 51fffb7710
2 changed files with 316 additions and 163 deletions
+254 -141
View File
@@ -1,10 +1,10 @@
/******************************************************************************* /*******************************************************************************
* * * *
* Author : Angus Johnson * * Author : Angus Johnson *
* Version : 6.2.1 * * Version : 6.2.9 *
* Date : 31 October 2014 * * Date : 16 February 2015 *
* Website : http://www.angusj.com * * Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 * * Copyright : Angus Johnson 2010-2015 *
* * * *
* License: * * License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. * * Use, modification & distribution is subject to Boost Software License Ver 1. *
@@ -47,6 +47,7 @@
#include <cstdlib> #include <cstdlib>
#include <ostream> #include <ostream>
#include <functional> #include <functional>
#include <sstream>
namespace ClipperLib { namespace ClipperLib {
@@ -381,13 +382,6 @@ Int128 Int128Mul (long64 lhs, long64 rhs)
// Miscellaneous global functions // Miscellaneous global functions
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Swap(cInt& val1, cInt& val2)
{
cInt tmp = val1;
val1 = val2;
val2 = tmp;
}
//------------------------------------------------------------------------------
bool Orientation(const Path &poly) bool Orientation(const Path &poly)
{ {
return Area(poly) >= 0; return Area(poly) >= 0;
@@ -435,11 +429,11 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int PointInPolygon (const IntPoint &pt, const Path &path) //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
int PointInPolygon(const IntPoint &pt, const Path &path)
{ {
//returns 0 if false, +1 if true, -1 if pt ON polygon boundary //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
int result = 0; int result = 0;
size_t cnt = path.size(); size_t cnt = path.size();
if (cnt < 3) return 0; if (cnt < 3) return 0;
@@ -758,9 +752,9 @@ inline void ReverseHorizontal(TEdge &e)
//swap horizontal edges' Top and Bottom x's so they follow the natural //swap horizontal edges' Top and Bottom x's so they follow the natural
//progression of the bounds - ie so their xbots will align with the //progression of the bounds - ie so their xbots will align with the
//adjoining lower edge. [Helpful in the ProcessHorizontal() method.] //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
Swap(e.Top.X, e.Bot.X); std::swap(e.Top.X, e.Bot.X);
#ifdef use_xyz #ifdef use_xyz
Swap(e.Top.Z, e.Bot.Z); std::swap(e.Top.Z, e.Bot.Z);
#endif #endif
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -866,8 +860,8 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
{ {
if (seg1a > seg1b) Swap(seg1a, seg1b); if (seg1a > seg1b) std::swap(seg1a, seg1b);
if (seg2a > seg2b) Swap(seg2a, seg2b); if (seg2a > seg2b) std::swap(seg2a, seg2b);
return (seg1a < seg2b) && (seg2a < seg1b); return (seg1a < seg2b) && (seg2a < seg1b);
} }
@@ -892,8 +886,13 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange)
{ {
if (useFullRange) if (useFullRange)
{ {
if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
throw "Coordinate outside allowed range"; {
std::stringstream s;
s << "Coordinate outside allowed range: ";
s << std::fixed << Pt.X << " " << Pt.Y << " " << -Pt.X << " " << -Pt.Y;
throw clipperException(s.str().c_str());
}
} }
else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
{ {
@@ -976,16 +975,13 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
EStart = E->Prev; EStart = E->Prev;
else else
EStart = E->Next; EStart = E->Next;
if (EStart->OutIdx != Skip) if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
{
if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
{ {
if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
ReverseHorizontal(*E); ReverseHorizontal(*E);
} }
else if (EStart->Bot.X != E->Bot.X) else if (EStart->Bot.X != E->Bot.X)
ReverseHorizontal(*E); ReverseHorizontal(*E);
}
} }
EStart = E; EStart = E;
@@ -1000,11 +996,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
//unless a Skip edge is encountered when that becomes the top divide //unless a Skip edge is encountered when that becomes the top divide
Horz = Result; Horz = Result;
while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
if (Horz->Prev->Top.X == Result->Next->Top.X) if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
{
if (!NextIsForward) Result = Horz->Prev;
}
else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
} }
while (E != Result) while (E != Result)
{ {
@@ -1024,11 +1016,8 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
{ {
Horz = Result; Horz = Result;
while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
if (Horz->Next->Top.X == Result->Prev->Top.X) if (Horz->Next->Top.X == Result->Prev->Top.X ||
{ Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
if (!NextIsForward) Result = Horz->Next;
}
else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
} }
while (E != Result) while (E != Result)
@@ -1080,7 +1069,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
} }
} }
catch(...) catch(std::exception const&)
{ {
delete [] edges; delete [] edges;
throw; //range test fails throw; //range test fails
@@ -1155,17 +1144,17 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
return false; return false;
} }
E->Prev->OutIdx = Skip; E->Prev->OutIdx = Skip;
if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev);
MinimaList::value_type locMin; MinimaList::value_type locMin;
locMin.Y = E->Bot.Y; locMin.Y = E->Bot.Y;
locMin.LeftBound = 0; locMin.LeftBound = 0;
locMin.RightBound = E; locMin.RightBound = E;
locMin.RightBound->Side = esRight; locMin.RightBound->Side = esRight;
locMin.RightBound->WindDelta = 0; locMin.RightBound->WindDelta = 0;
while (E->Next->OutIdx != Skip) for (;;)
{ {
E->NextInLML = E->Next;
if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
if (E->Next->OutIdx == Skip) break;
E->NextInLML = E->Next;
E = E->Next; E = E->Next;
} }
m_MinimaList.push_back(locMin); m_MinimaList.push_back(locMin);
@@ -1257,7 +1246,7 @@ void ClipperBase::Reset()
{ {
m_CurrentLM = m_MinimaList.begin(); m_CurrentLM = m_MinimaList.begin();
if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process
std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); std::stable_sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter());
//reset all edges ... //reset all edges ...
for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
@@ -1371,6 +1360,7 @@ void Clipper::Reset()
{ {
ClipperBase::Reset(); ClipperBase::Reset();
m_Scanbeam = ScanbeamList(); m_Scanbeam = ScanbeamList();
m_Maxima = MaximaList();
m_ActiveEdges = 0; m_ActiveEdges = 0;
m_SortedEdges = 0; m_SortedEdges = 0;
for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
@@ -1378,12 +1368,24 @@ void Clipper::Reset()
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
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, bool Clipper::Execute(ClipType clipType, Paths &solution,
PolyFillType subjFillType, PolyFillType clipFillType) PolyFillType subjFillType, PolyFillType clipFillType)
{ {
if( m_ExecuteLocked ) return false; if( m_ExecuteLocked ) return false;
if (m_HasOpenPaths) if (m_HasOpenPaths)
throw clipperException("Error: PolyTree struct is need for open path clipping."); throw clipperException("Error: PolyTree struct is needed for open path clipping.");
m_ExecuteLocked = true; m_ExecuteLocked = true;
solution.resize(0); solution.resize(0);
m_SubjFillType = subjFillType; m_SubjFillType = subjFillType;
@@ -1439,9 +1441,9 @@ bool Clipper::ExecuteInternal()
cInt botY = PopScanbeam(); cInt botY = PopScanbeam();
do { do {
InsertLocalMinimaIntoAEL(botY); InsertLocalMinimaIntoAEL(botY);
ClearGhostJoins(); ProcessHorizontals();
ProcessHorizontals(false); ClearGhostJoins();
if (m_Scanbeam.empty()) break; if (m_Scanbeam.empty()) break;
cInt topY = PopScanbeam(); cInt topY = PopScanbeam();
succeeded = ProcessIntersections(topY); succeeded = ProcessIntersections(topY);
if (!succeeded) break; if (!succeeded) break;
@@ -1449,7 +1451,7 @@ bool Clipper::ExecuteInternal()
botY = topY; botY = topY;
} while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end()); } while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end());
} }
catch(...) catch(std::exception const&)
{ {
succeeded = false; succeeded = false;
} }
@@ -1471,7 +1473,10 @@ bool Clipper::ExecuteInternal()
for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
{ {
OutRec *outRec = m_PolyOuts[i]; OutRec *outRec = m_PolyOuts[i];
if (outRec->Pts && !outRec->IsOpen) if (!outRec->Pts) continue;
if (outRec->IsOpen)
FixupOutPolyline(*outRec);
else
FixupOutPolygon(*outRec); FixupOutPolygon(*outRec);
} }
@@ -1486,17 +1491,16 @@ bool Clipper::ExecuteInternal()
void Clipper::InsertScanbeam(const cInt Y) void Clipper::InsertScanbeam(const cInt Y)
{ {
//if (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) return;// avoid duplicates. m_Scanbeam.push(Y);
m_Scanbeam.push(Y);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
cInt Clipper::PopScanbeam() cInt Clipper::PopScanbeam()
{ {
const cInt Y = m_Scanbeam.top(); const cInt Y = m_Scanbeam.top();
m_Scanbeam.pop(); m_Scanbeam.pop();
while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
return Y; return Y;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -1898,7 +1902,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
//if any output polygons share an edge, they'll need joining later ... //if any output polygons share an edge, they'll need joining later ...
if (Op1 && IsHorizontal(*rb) && if (Op1 && IsHorizontal(*rb) &&
m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) !m_GhostJoins.empty() && (rb->WindDelta != 0))
{ {
for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)
{ {
@@ -2356,7 +2360,6 @@ OutRec* Clipper::CreateOutRec()
OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
{ {
bool ToFront = (e->Side == esLeft);
if( e->OutIdx < 0 ) if( e->OutIdx < 0 )
{ {
OutRec *outRec = CreateOutRec(); OutRec *outRec = CreateOutRec();
@@ -2377,7 +2380,8 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
OutPt* op = outRec->Pts; OutPt* op = outRec->Pts;
if (ToFront && (pt == op->Pt)) return op; bool ToFront = (e->Side == esLeft);
if (ToFront && (pt == op->Pt)) return op;
else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
OutPt* newOp = new OutPt; OutPt* newOp = new OutPt;
@@ -2393,13 +2397,23 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) OutPt* Clipper::GetLastOutPt(TEdge *e)
{
OutRec *outRec = m_PolyOuts[e->OutIdx];
if (e->Side == esLeft)
return outRec->Pts;
else
return outRec->Pts->Prev;
}
//------------------------------------------------------------------------------
void Clipper::ProcessHorizontals()
{ {
TEdge* horzEdge = m_SortedEdges; TEdge* horzEdge = m_SortedEdges;
while(horzEdge) while(horzEdge)
{ {
DeleteFromSEL(horzEdge); DeleteFromSEL(horzEdge);
ProcessHorizontal(horzEdge, IsTopOfScanbeam); ProcessHorizontal(horzEdge);
horzEdge = m_SortedEdges; horzEdge = m_SortedEdges;
} }
} }
@@ -2564,10 +2578,11 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
*******************************************************************************/ *******************************************************************************/
void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) void Clipper::ProcessHorizontal(TEdge *horzEdge)
{ {
Direction dir; Direction dir;
cInt horzLeft, horzRight; cInt horzLeft, horzRight;
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen);
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
@@ -2577,50 +2592,100 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
if (!eLastHorz->NextInLML) if (!eLastHorz->NextInLML)
eMaxPair = GetMaximaPair(eLastHorz); eMaxPair = GetMaximaPair(eLastHorz);
for (;;) MaximaList::const_iterator maxIt;
MaximaList::const_reverse_iterator maxRit;
if (!m_Maxima.empty())
{ {
//get the first maxima in range (X) ...
if (dir == dLeftToRight)
{
maxIt = m_Maxima.begin();
while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;
if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)
maxIt = m_Maxima.end();
}
else
{
maxRit = m_Maxima.rbegin();
while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;
if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)
maxRit = m_Maxima.rend();
}
}
OutPt* op1 = 0;
for (;;) //loop through consec. horizontal edges
{
bool IsLastHorz = (horzEdge == eLastHorz); bool IsLastHorz = (horzEdge == eLastHorz);
TEdge* e = GetNextInAEL(horzEdge, dir); TEdge* e = GetNextInAEL(horzEdge, dir);
while(e) while(e)
{ {
//Break if we've got to the end of an intermediate horizontal edge ...
//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
e->Dx < horzEdge->NextInLML->Dx) break;
TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later //this code block inserts extra coords into horizontal edges (in output
//polygons) whereever maxima touch these horizontal edges. This helps
//'simplifying' polygons (ie if the Simplify property is set).
if (!m_Maxima.empty())
{
if (dir == dLeftToRight)
{
while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X)
{
if (horzEdge->OutIdx >= 0 && !IsOpen)
AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));
maxIt++;
}
}
else
{
while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)
{
if (horzEdge->OutIdx >= 0 && !IsOpen)
AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));
maxRit++;
}
}
};
if ((dir == dLeftToRight && e->Curr.X <= horzRight) || if ((dir == dLeftToRight && e->Curr.X > horzRight) ||
(dir == dRightToLeft && e->Curr.X >= horzLeft)) (dir == dRightToLeft && e->Curr.X < horzLeft)) break;
{
//so far we're still in range of the horizontal Edge but make sure //Also break if we've got to the end of an intermediate horizontal edge ...
//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
e->Dx < horzEdge->NextInLML->Dx) break;
if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
{
op1 = AddOutPt(horzEdge, e->Curr);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
{
if (eNextHorz->OutIdx >= 0 &&
HorzSegmentsOverlap(horzEdge->Bot.X,
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
{
OutPt* op2 = GetLastOutPt(eNextHorz);
AddJoin(op2, op1, eNextHorz->Top);
}
eNextHorz = eNextHorz->NextInSEL;
}
AddGhostJoin(op1, horzEdge->Bot);
}
//OK, so far we're still in range of the horizontal Edge but make sure
//we're at the last of consec. horizontals when matching with eMaxPair //we're at the last of consec. horizontals when matching with eMaxPair
if(e == eMaxPair && IsLastHorz) if(e == eMaxPair && IsLastHorz)
{ {
if (horzEdge->OutIdx >= 0) if (horzEdge->OutIdx >= 0)
{
OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
{
if (eNextHorz->OutIdx >= 0 &&
HorzSegmentsOverlap(horzEdge->Bot.X,
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
{
OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot);
AddJoin(op2, op1, eNextHorz->Top);
}
eNextHorz = eNextHorz->NextInSEL;
}
AddGhostJoin(op1, horzEdge->Bot);
AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
}
DeleteFromAEL(horzEdge); DeleteFromAEL(horzEdge);
DeleteFromAEL(eMaxPair); DeleteFromAEL(eMaxPair);
return; return;
} }
else if(dir == dLeftToRight)
if(dir == dLeftToRight)
{ {
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
IntersectEdges(horzEdge, e, Pt); IntersectEdges(horzEdge, e, Pt);
@@ -2630,28 +2695,43 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
IntersectEdges( e, horzEdge, Pt); IntersectEdges( e, horzEdge, Pt);
} }
TEdge* eNext = GetNextInAEL(e, dir);
SwapPositionsInAEL( horzEdge, e ); SwapPositionsInAEL( horzEdge, e );
} e = eNext;
else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || } //end while(e)
(dir == dRightToLeft && e->Curr.X <= horzLeft) ) break;
e = eNext; //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
} //end while if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;
UpdateEdgeIntoAEL(horzEdge);
if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML))
{
UpdateEdgeIntoAEL(horzEdge);
if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
} else
break;
} //end for (;;) } //end for (;;)
if(horzEdge->NextInLML) if (horzEdge->OutIdx >= 0 && !op1)
{
op1 = GetLastOutPt(horzEdge);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
{
if (eNextHorz->OutIdx >= 0 &&
HorzSegmentsOverlap(horzEdge->Bot.X,
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
{
OutPt* op2 = GetLastOutPt(eNextHorz);
AddJoin(op2, op1, eNextHorz->Top);
}
eNextHorz = eNextHorz->NextInSEL;
}
AddGhostJoin(op1, horzEdge->Top);
}
if (horzEdge->NextInLML)
{ {
if(horzEdge->OutIdx >= 0) if(horzEdge->OutIdx >= 0)
{ {
OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); op1 = AddOutPt( horzEdge, horzEdge->Top);
if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot);
UpdateEdgeIntoAEL(horzEdge); UpdateEdgeIntoAEL(horzEdge);
if (horzEdge->WindDelta == 0) return; if (horzEdge->WindDelta == 0) return;
//nb: HorzEdge is no longer horizontal here //nb: HorzEdge is no longer horizontal here
@@ -2687,8 +2767,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
void Clipper::UpdateEdgeIntoAEL(TEdge *&e) void Clipper::UpdateEdgeIntoAEL(TEdge *&e)
{ {
if( !e->NextInLML ) throw if( !e->NextInLML )
clipperException("UpdateEdgeIntoAEL: invalid call"); throw clipperException("UpdateEdgeIntoAEL: invalid call");
e->NextInLML->OutIdx = e->OutIdx; e->NextInLML->OutIdx = e->OutIdx;
TEdge* AelPrev = e->PrevInAEL; TEdge* AelPrev = e->PrevInAEL;
@@ -2718,11 +2798,11 @@ bool Clipper::ProcessIntersections(const cInt topY)
if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
else return false; else return false;
} }
catch(...) catch(std::exception const& ex)
{ {
m_SortedEdges = 0; m_SortedEdges = 0;
DisposeIntersectNodes(); DisposeIntersectNodes();
throw clipperException("ProcessIntersections error"); throw clipperException((std::string("ProcessIntersections error ") + ex.what()).c_str());
} }
m_SortedEdges = 0; m_SortedEdges = 0;
return true; return true;
@@ -2820,7 +2900,7 @@ bool Clipper::FixupIntersectionOrder()
//Now it's crucial that intersections are made only between adjacent edges, //Now it's crucial that intersections are made only between adjacent edges,
//so to ensure this the order of intersections may need adjusting ... //so to ensure this the order of intersections may need adjusting ...
CopyAELToSEL(); CopyAELToSEL();
std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); std::stable_sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort);
size_t cnt = m_IntersectList.size(); size_t cnt = m_IntersectList.size();
for (size_t i = 0; i < cnt; ++i) for (size_t i = 0; i < cnt; ++i)
{ {
@@ -2906,6 +2986,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
if(IsMaximaEdge) if(IsMaximaEdge)
{ {
if (m_StrictSimple) m_Maxima.push_back(e->Top.X);
TEdge* ePrev = e->PrevInAEL; TEdge* ePrev = e->PrevInAEL;
DoMaxima(e); DoMaxima(e);
if( !ePrev ) e = m_ActiveEdges; if( !ePrev ) e = m_ActiveEdges;
@@ -2927,6 +3008,8 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
e->Curr.Y = topY; e->Curr.Y = topY;
} }
//When StrictlySimple and 'e' is being touched by another edge, then
//make sure both edges have a vertex here ...
if (m_StrictSimple) if (m_StrictSimple)
{ {
TEdge* ePrev = e->PrevInAEL; TEdge* ePrev = e->PrevInAEL;
@@ -2948,7 +3031,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
} }
//3. Process horizontals at the Top of the scanbeam ... //3. Process horizontals at the Top of the scanbeam ...
ProcessHorizontals(true); m_Maxima.sort();
ProcessHorizontals();
m_Maxima.clear();
//4. Promote intermediate vertices ... //4. Promote intermediate vertices ...
e = m_ActiveEdges; e = m_ActiveEdges;
@@ -2988,44 +3073,71 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Clipper::FixupOutPolygon(OutRec &outrec) void Clipper::FixupOutPolyline(OutRec &outrec)
{ {
//FixupOutPolygon() - removes duplicate points and simplifies consecutive
//parallel edges by removing the middle vertex.
OutPt *lastOK = 0;
outrec.BottomPt = 0;
OutPt *pp = outrec.Pts; OutPt *pp = outrec.Pts;
OutPt *lastPP = pp->Prev;
for (;;) while (pp != lastPP)
{ {
if (pp->Prev == pp || pp->Prev == pp->Next ) pp = pp->Next;
if (pp->Pt == pp->Prev->Pt)
{ {
DisposeOutPts(pp); if (pp == lastPP) lastPP = pp->Prev;
outrec.Pts = 0; OutPt *tmpPP = pp->Prev;
return; tmpPP->Next = pp->Next;
} pp->Next->Prev = tmpPP;
delete pp;
//test for duplicate points and collinear edges ... pp = tmpPP;
if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
(SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
(!m_PreserveCollinear ||
!Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
{
lastOK = 0;
OutPt *tmp = pp;
pp->Prev->Next = pp->Next;
pp->Next->Prev = pp->Prev;
pp = pp->Prev;
delete tmp;
}
else if (pp == lastOK) break;
else
{
if (!lastOK) lastOK = pp;
pp = pp->Next;
} }
} }
outrec.Pts = pp;
if (pp == pp->Prev)
{
DisposeOutPts(pp);
outrec.Pts = 0;
return;
}
}
//------------------------------------------------------------------------------
void Clipper::FixupOutPolygon(OutRec &outrec)
{
//FixupOutPolygon() - removes duplicate points and simplifies consecutive
//parallel edges by removing the middle vertex.
OutPt *lastOK = 0;
outrec.BottomPt = 0;
OutPt *pp = outrec.Pts;
bool preserveCol = m_PreserveCollinear || m_StrictSimple;
for (;;)
{
if (pp->Prev == pp || pp->Prev == pp->Next)
{
DisposeOutPts(pp);
outrec.Pts = 0;
return;
}
//test for duplicate points and collinear edges ...
if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
(SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
(!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
{
lastOK = 0;
OutPt *tmp = pp;
pp->Prev->Next = pp->Next;
pp->Next->Prev = pp->Prev;
pp = pp->Prev;
delete tmp;
}
else if (pp == lastOK) break;
else
{
if (!lastOK) lastOK = pp;
pp = pp->Next;
}
}
outrec.Pts = pp;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -3055,7 +3167,7 @@ void Clipper::BuildResult(Paths &polys)
int cnt = PointCount(p); int cnt = PointCount(p);
if (cnt < 2) continue; if (cnt < 2) continue;
pg.reserve(cnt); pg.reserve(cnt);
for (int i = 0; i < cnt; ++i) for (int j = 0; j < cnt; ++j)
{ {
pg.push_back(p->Pt); pg.push_back(p->Pt);
p = p->Prev; p = p->Prev;
@@ -3309,7 +3421,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
OutPt *op2 = j->OutPt2, *op2b; OutPt *op2 = j->OutPt2, *op2b;
//There are 3 kinds of joins for output polygons ... //There are 3 kinds of joins for output polygons ...
//1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
//along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
//2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
//location at the Bottom of the overlapping segment (& Join.OffPt is above). //location at the Bottom of the overlapping segment (& Join.OffPt is above).
@@ -3508,6 +3620,7 @@ void Clipper::JoinCommonEdges()
OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
if (!outRec1->Pts || !outRec2->Pts) continue; if (!outRec1->Pts || !outRec2->Pts) continue;
if (outRec1->IsOpen || outRec2->IsOpen) continue;
//get the polygon fragment with the correct hole state (FirstLeft) //get the polygon fragment with the correct hole state (FirstLeft)
//before calling JoinPoints() ... //before calling JoinPoints() ...
@@ -3745,7 +3858,7 @@ void ClipperOffset::Execute(Paths& solution, double delta)
clpr.AddPath(outer, ptSubject, true); clpr.AddPath(outer, ptSubject, true);
clpr.ReverseSolution(true); clpr.ReverseSolution(true);
clpr.Execute(ctUnion, solution, pftNegative, pftNegative); clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
if (solution.size() > 0) solution.erase(solution.begin()); if (!solution.empty()) solution.erase(solution.begin());
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -4355,7 +4468,7 @@ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool p
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void TranslatePath(const Path& input, Path& output, IntPoint delta) void TranslatePath(const Path& input, Path& output, const IntPoint delta)
{ {
//precondition: input != output //precondition: input != output
output.resize(input.size()); output.resize(input.size());
+62 -22
View File
@@ -1,10 +1,10 @@
/******************************************************************************* /*******************************************************************************
* * * *
* Author : Angus Johnson * * Author : Angus Johnson *
* Version : 6.2.1 * * Version : 6.2.9 *
* Date : 31 October 2014 * * Date : 16 February 2015 *
* Website : http://www.angusj.com * * Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 * * Copyright : Angus Johnson 2010-2015 *
* * * *
* License: * * License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. * * Use, modification & distribution is subject to Boost Software License Ver 1. *
@@ -34,7 +34,7 @@
#ifndef clipper_hpp #ifndef clipper_hpp
#define clipper_hpp #define clipper_hpp
#define CLIPPER_VERSION "6.2.0" #define CLIPPER_VERSION "6.2.6"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This //use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340 //improve performance but coordinate values are limited to the range +/- 46340
@@ -50,6 +50,7 @@
//#define use_deprecated //#define use_deprecated
#include <vector> #include <vector>
#include <list>
#include <set> #include <set>
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
@@ -57,6 +58,9 @@
#include <ostream> #include <ostream>
#include <functional> #include <functional>
#include <queue> #include <queue>
#if defined(CLIPPER_IMPL_INCLUDE)
#include CLIPPER_IMPL_INCLUDE
#endif
namespace ClipperLib { namespace ClipperLib {
@@ -73,7 +77,7 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
static cInt const loRange = 0x7FFF; static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF; static cInt const hiRange = 0x7FFF;
#else #else
typedef signed long long cInt; typedef std::int64_t cInt;
static cInt const loRange = 0x3FFFFFFF; static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class typedef signed long long long64; //used by Int128 class
@@ -81,6 +85,12 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#endif #endif
#if defined(CLIPPER_INTPOINT_IMPL)
typedef CLIPPER_INTPOINT_IMPL IntPoint;
#else
struct IntPoint { struct IntPoint {
cInt X; cInt X;
cInt Y; cInt Y;
@@ -100,11 +110,33 @@ struct IntPoint {
return a.X != b.X || a.Y != b.Y; return a.X != b.X || a.Y != b.Y;
} }
}; };
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(CLIPPER_PATH_IMPL)
typedef CLIPPER_PATH_IMPL Path;
#else
typedef std::vector< IntPoint > Path; typedef std::vector< IntPoint > Path;
#endif
#if defined(CLIPPER_PATHS_IMPL)
typedef CLIPPER_PATHS_IMPL Paths;
#else
typedef std::vector< Path > Paths; typedef std::vector< Path > Paths;
#endif
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
@@ -200,7 +232,6 @@ enum EdgeSide { esLeft = 1, esRight = 2};
struct TEdge; struct TEdge;
struct IntersectNode; struct IntersectNode;
struct LocalMinimum; struct LocalMinimum;
struct Scanbeam;
struct OutPt; struct OutPt;
struct OutRec; struct OutRec;
struct Join; struct Join;
@@ -232,7 +263,6 @@ protected:
void PopLocalMinima(); void PopLocalMinima();
virtual void Reset(); virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise); TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E); TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
@@ -253,14 +283,20 @@ public:
Clipper(int initOptions = 0); Clipper(int initOptions = 0);
~Clipper(); ~Clipper();
bool Execute(ClipType clipType, bool Execute(ClipType clipType,
Paths &solution, Paths &solution,
PolyFillType subjFillType = pftEvenOdd, PolyFillType fillType = pftEvenOdd);
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType, bool Execute(ClipType clipType,
PolyTree &polytree, Paths &solution,
PolyFillType subjFillType = pftEvenOdd, PolyFillType subjFillType,
PolyFillType clipFillType = pftEvenOdd); PolyFillType clipFillType);
bool ReverseSolution() {return m_ReverseOutput;}; bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType fillType = pftEvenOdd);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType,
PolyFillType clipFillType);
bool ReverseSolution() { return m_ReverseOutput; };
void ReverseSolution(bool value) {m_ReverseOutput = value;}; void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool StrictlySimple() {return m_StrictSimple;}; bool StrictlySimple() {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;}; void StrictlySimple(bool value) {m_StrictSimple = value;};
@@ -272,13 +308,15 @@ protected:
void Reset(); void Reset();
virtual bool ExecuteInternal(); virtual bool ExecuteInternal();
private: private:
PolyOutList m_PolyOuts; PolyOutList m_PolyOuts;
JoinList m_Joins; JoinList m_Joins;
JoinList m_GhostJoins; JoinList m_GhostJoins;
IntersectList m_IntersectList; IntersectList m_IntersectList;
ClipType m_ClipType; ClipType m_ClipType;
typedef std::priority_queue<cInt> ScanbeamList; typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam; ScanbeamList m_Scanbeam;
typedef std::list<cInt> MaximaList;
MaximaList m_Maxima;
TEdge *m_ActiveEdges; TEdge *m_ActiveEdges;
TEdge *m_SortedEdges; TEdge *m_SortedEdges;
bool m_ExecuteLocked; bool m_ExecuteLocked;
@@ -307,8 +345,8 @@ private:
bool IsTopHorz(const cInt XPos); bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e); void DoMaxima(TEdge *e);
void ProcessHorizontals(bool IsTopOfScanbeam); void ProcessHorizontals();
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx); OutRec* GetOutRec(int idx);
@@ -316,6 +354,7 @@ private:
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec(); OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt); OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
OutPt* GetLastOutPt(TEdge *e);
void DisposeAllOutRecs(); void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index); void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt topY); bool ProcessIntersections(const cInt topY);
@@ -328,6 +367,7 @@ private:
void DisposeIntersectNodes(); void DisposeIntersectNodes();
bool FixupIntersectionOrder(); bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec); void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec);
bool IsHole(TEdge *e); bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec); void FixHoleLinkage(OutRec &outrec);