ClipperLib: Further optimization of memory allocation using scalable_allocator.

ClipperLib: SimplifyPolygon() - changed default winding number to positive,
      added strictly_simple parameter.
ClipperUtlis simplify_polygons() - removed "remove_collinear" parameter
This commit is contained in:
Vojtech Bubnik 2023-05-02 12:59:20 +02:00
parent 03608580c0
commit a7e17df25f
5 changed files with 137 additions and 145 deletions

View File

@ -73,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path
#define TOLERANCE (1.0e-20) #define TOLERANCE (1.0e-20)
#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
// 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 IntPoint IntPoint2d(cInt x, cInt y) inline IntPoint IntPoint2d(cInt x, cInt y)
@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds()
Clipper::Clipper(int initOptions) : Clipper::Clipper(int initOptions) :
ClipperBase(), ClipperBase(),
m_OutPtsFree(nullptr), m_OutPtsFree(nullptr),
m_OutPtsChunkSize(32), m_OutPtsChunkLast(m_OutPtsChunkSize),
m_OutPtsChunkLast(32),
m_ActiveEdges(nullptr), m_ActiveEdges(nullptr),
m_SortedEdges(nullptr) m_SortedEdges(nullptr)
{ {
@ -1153,23 +1133,23 @@ bool Clipper::ExecuteInternal()
//FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //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! //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable!
{ {
for (OutRec *outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0))
ReversePolyPtLinks(outRec->Pts); ReversePolyPtLinks(outRec.Pts);
} }
JoinCommonEdges(); JoinCommonEdges();
//unfortunately FixupOutPolygon() must be done after JoinCommonEdges() //unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
{ {
for (OutRec *outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
if (outRec->Pts) { if (outRec.Pts) {
if (outRec->IsOpen) if (outRec.IsOpen)
// Removes duplicate points. // Removes duplicate points.
FixupOutPolyline(*outRec); FixupOutPolyline(outRec);
else else
// Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex.
FixupOutPolygon(*outRec); FixupOutPolygon(outRec);
} }
} }
// For each polygon, search for exactly duplicate non-successive points. // For each polygon, search for exactly duplicate non-successive points.
@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt()
m_OutPtsFree = pt->Next; m_OutPtsFree = pt->Next;
} else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) {
// Get a point from the last chunk. // Get a point from the last chunk.
pt = m_OutPts.back() + (m_OutPtsChunkLast ++); pt = &m_OutPts.back()[m_OutPtsChunkLast ++];
} else { } else {
// The last chunk is full. Allocate a new one. // The last chunk is full. Allocate a new one.
m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); m_OutPts.push_back({});
m_OutPtsChunkLast = 1; m_OutPtsChunkLast = 1;
pt = m_OutPts.back(); pt = &m_OutPts.back().front();
} }
return pt; return pt;
} }
void Clipper::DisposeAllOutRecs() void Clipper::DisposeAllOutRecs()
{ {
for (OutPt *pts : m_OutPts)
delete[] pts;
for (OutRec *rec : m_PolyOuts)
delete rec;
m_OutPts.clear(); m_OutPts.clear();
m_OutPtsFree = nullptr; m_OutPtsFree = nullptr;
m_OutPtsChunkLast = m_OutPtsChunkSize; m_OutPtsChunkLast = m_OutPtsChunkSize;
@ -1832,7 +1808,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
{ {
bool IsHole = false; bool IsHole = false;
TEdge *e2 = e->PrevInAEL; TEdge *e2 = e->PrevInAEL;
@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const
{ {
IsHole = !IsHole; IsHole = !IsHole;
if (! outrec->FirstLeft) if (! outrec->FirstLeft)
outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; outrec->FirstLeft = &m_PolyOuts[e2->OutIdx];
} }
e2 = e2->PrevInAEL; e2 = e2->PrevInAEL;
} }
@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2)
OutRec* Clipper::GetOutRec(int Idx) OutRec* Clipper::GetOutRec(int Idx)
{ {
OutRec* outrec = m_PolyOuts[Idx]; OutRec* outrec = &m_PolyOuts[Idx];
while (outrec != m_PolyOuts[outrec->Idx]) while (outrec != &m_PolyOuts[outrec->Idx])
outrec = m_PolyOuts[outrec->Idx]; outrec = &m_PolyOuts[outrec->Idx];
return outrec; return outrec;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
{ {
//get the start and ends of both output polygons ... //get the start and ends of both output polygons ...
OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; OutRec *outRec1 = &m_PolyOuts[e1->OutIdx];
OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; OutRec *outRec2 = &m_PolyOuts[e2->OutIdx];
OutRec *holeStateRec; OutRec *holeStateRec;
if (Param1RightOfParam2(outRec1, outRec2)) if (Param1RightOfParam2(outRec1, outRec2))
@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const
OutRec* Clipper::CreateOutRec() OutRec* Clipper::CreateOutRec()
{ {
OutRec* result = new OutRec; m_PolyOuts.push_back({});
result->IsHole = false; OutRec &result = m_PolyOuts.back();
result->IsOpen = false; result.IsHole = false;
result->FirstLeft = 0; result.IsOpen = false;
result->Pts = 0; result.FirstLeft = 0;
result->BottomPt = 0; result.Pts = 0;
result->PolyNd = 0; result.BottomPt = 0;
m_PolyOuts.push_back(result); result.PolyNd = 0;
result->Idx = (int)m_PolyOuts.size()-1; result.Idx = (int)m_PolyOuts.size()-1;
return result; return &result;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
return newOp; return newOp;
} else } else
{ {
OutRec *outRec = m_PolyOuts[e->OutIdx]; OutRec *outRec = &m_PolyOuts[e->OutIdx];
//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;
@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
OutPt* Clipper::GetLastOutPt(TEdge *e) OutPt* Clipper::GetLastOutPt(TEdge *e)
{ {
OutRec *outRec = m_PolyOuts[e->OutIdx]; OutRec *outRec = &m_PolyOuts[e->OutIdx];
if (e->Side == esLeft) if (e->Side == esLeft)
return outRec->Pts; return outRec->Pts;
else else
@ -2216,7 +2192,7 @@ 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); bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx].IsOpen);
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts)
void Clipper::BuildResult(Paths &polys) void Clipper::BuildResult(Paths &polys)
{ {
polys.reserve(m_PolyOuts.size()); polys.reserve(m_PolyOuts.size());
for (OutRec* outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
{ {
assert(! outRec->IsOpen); assert(! outRec.IsOpen);
if (!outRec->Pts) continue; if (!outRec.Pts) continue;
Path pg; Path pg;
OutPt* p = outRec->Pts->Prev; OutPt* p = outRec.Pts->Prev;
int cnt = PointCount(p); int cnt = PointCount(p);
if (cnt < 2) continue; if (cnt < 2) continue;
pg.reserve(cnt); pg.reserve(cnt);
@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree)
polytree.Clear(); polytree.Clear();
polytree.AllNodes.reserve(m_PolyOuts.size()); polytree.AllNodes.reserve(m_PolyOuts.size());
//add each output polygon/contour to polytree ... //add each output polygon/contour to polytree ...
for (OutRec* outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
{ {
int cnt = PointCount(outRec->Pts); int cnt = PointCount(outRec.Pts);
if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3))
// Ignore an invalid output loop or a polyline. // Ignore an invalid output loop or a polyline.
continue; continue;
//skip OutRecs that (a) contain outermost polygons or //skip OutRecs that (a) contain outermost polygons or
//(b) already have the correct owner/child linkage ... //(b) already have the correct owner/child linkage ...
if (outRec->FirstLeft && if (outRec.FirstLeft &&
(outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { (outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) {
OutRec* orfl = outRec->FirstLeft; OutRec* orfl = outRec.FirstLeft;
while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts))
orfl = orfl->FirstLeft; orfl = orfl->FirstLeft;
outRec->FirstLeft = orfl; outRec.FirstLeft = orfl;
} }
//nb: polytree takes ownership of all the PolyNodes //nb: polytree takes ownership of all the PolyNodes
polytree.AllNodes.emplace_back(PolyNode()); polytree.AllNodes.emplace_back(PolyNode());
PolyNode* pn = &polytree.AllNodes.back(); PolyNode* pn = &polytree.AllNodes.back();
outRec->PolyNd = pn; outRec.PolyNd = pn;
pn->Parent = 0; pn->Parent = 0;
pn->Index = 0; pn->Index = 0;
pn->Contour.reserve(cnt); pn->Contour.reserve(cnt);
OutPt *op = outRec->Pts->Prev; OutPt *op = outRec.Pts->Prev;
for (int j = 0; j < cnt; j++) for (int j = 0; j < cnt; j++)
{ {
pn->Contour.emplace_back(op->Pt); pn->Contour.emplace_back(op->Pt);
@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree)
//fixup PolyNode links etc ... //fixup PolyNode links etc ...
polytree.Childs.reserve(m_PolyOuts.size()); polytree.Childs.reserve(m_PolyOuts.size());
for (OutRec* outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
{ {
if (!outRec->PolyNd) continue; if (!outRec.PolyNd) continue;
if (outRec->IsOpen) if (outRec.IsOpen)
{ {
outRec->PolyNd->m_IsOpen = true; outRec.PolyNd->m_IsOpen = true;
polytree.AddChild(*outRec->PolyNd); polytree.AddChild(*outRec.PolyNd);
} }
else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd)
outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd);
else else
polytree.AddChild(*outRec->PolyNd); polytree.AddChild(*outRec.PolyNd);
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3193,26 +3169,26 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// This is potentially very expensive! O(n^3)! // This is potentially very expensive! O(n^3)!
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
{ {
//tests if NewOutRec contains the polygon before reassigning FirstLeft //tests if NewOutRec contains the polygon before reassigning FirstLeft
for (OutRec *outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
{ {
if (!outRec->Pts || !outRec->FirstLeft) continue; if (!outRec.Pts || !outRec.FirstLeft) continue;
OutRec* firstLeft = outRec->FirstLeft; OutRec* firstLeft = outRec.FirstLeft;
// Skip empty polygons. // Skip empty polygons.
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts))
outRec->FirstLeft = NewOutRec; outRec.FirstLeft = NewOutRec;
} }
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec)
{ {
//reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
for (OutRec *outRec : m_PolyOuts) for (OutRec &outRec : m_PolyOuts)
if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges()
if (m_UsingPolyTree) if (m_UsingPolyTree)
for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) for (size_t j = 0; j < m_PolyOuts.size() - 1; j++)
{ {
OutRec* oRec = m_PolyOuts[j]; OutRec &oRec = m_PolyOuts[j];
OutRec* firstLeft = oRec->FirstLeft; OutRec* firstLeft = oRec.FirstLeft;
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
if (!oRec->Pts || firstLeft != outRec1 || if (!oRec.Pts || firstLeft != outRec1 ||
oRec->IsHole == outRec1->IsHole) continue; oRec.IsHole == outRec1->IsHole) continue;
if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2))
oRec->FirstLeft = outRec2; oRec.FirstLeft = outRec2;
} }
if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons()
size_t i = 0; size_t i = 0;
while (i < m_PolyOuts.size()) while (i < m_PolyOuts.size())
{ {
OutRec* outrec = m_PolyOuts[i++]; OutRec &outrec = m_PolyOuts[i++];
OutPt* op = outrec->Pts; OutPt* op = outrec.Pts;
if (!op || outrec->IsOpen) continue; if (!op || outrec.IsOpen) continue;
do //for each Pt in Polygon until duplicate found do ... do //for each Pt in Polygon until duplicate found do ...
{ {
OutPt* op2 = op->Next; OutPt* op2 = op->Next;
while (op2 != outrec->Pts) while (op2 != outrec.Pts)
{ {
if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
{ {
@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons()
op2->Prev = op3; op2->Prev = op3;
op3->Next = op2; op3->Next = op2;
outrec->Pts = op; outrec.Pts = op;
OutRec* outrec2 = CreateOutRec(); OutRec* outrec2 = CreateOutRec();
outrec2->Pts = op2; outrec2->Pts = op2;
UpdateOutPtIdxs(*outrec2); UpdateOutPtIdxs(*outrec2);
if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts))
{ {
//OutRec2 is contained by OutRec1 ... //OutRec2 is contained by OutRec1 ...
outrec2->IsHole = !outrec->IsHole; outrec2->IsHole = !outrec.IsHole;
outrec2->FirstLeft = outrec; outrec2->FirstLeft = &outrec;
// For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec.
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec);
} }
else else
if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts))
{ {
//OutRec1 is contained by OutRec2 ... //OutRec1 is contained by OutRec2 ...
outrec2->IsHole = outrec->IsHole; outrec2->IsHole = outrec.IsHole;
outrec->IsHole = !outrec2->IsHole; outrec.IsHole = !outrec2->IsHole;
outrec2->FirstLeft = outrec->FirstLeft; outrec2->FirstLeft = outrec.FirstLeft;
outrec->FirstLeft = outrec2; outrec.FirstLeft = outrec2;
// For each m_PolyOuts, replace FirstLeft from outrec to outrec2. // For each m_PolyOuts, replace FirstLeft from outrec to outrec2.
if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2);
} }
else else
{ {
//the 2 polygons are separate ... //the 2 polygons are separate ...
outrec2->IsHole = outrec->IsHole; outrec2->IsHole = outrec.IsHole;
outrec2->FirstLeft = outrec->FirstLeft; outrec2->FirstLeft = outrec.FirstLeft;
// For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. // 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)! //FIXME This is potentially very expensive! O(n^3)!
if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2);
} }
op2 = op; //ie get ready for the Next iteration op2 = op; //ie get ready for the Next iteration
} }
@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons()
} }
op = op->Next; op = op->Next;
} }
while (op != outrec->Pts); while (op != outrec.Pts);
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3845,10 +3821,10 @@ void ReversePaths(Paths& p)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType) Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */)
{ {
Clipper c; Clipper c;
c.StrictlySimple(true); c.StrictlySimple(strictly_simple);
c.AddPath(in_poly, ptSubject, true); c.AddPath(in_poly, ptSubject, true);
Paths out; Paths out;
c.Execute(ctUnion, out, fillType, fillType); c.Execute(ctUnion, out, fillType, fillType);

View File

@ -52,6 +52,7 @@
//use_deprecated: Enables temporary support for the obsolete functions //use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated //#define use_deprecated
#include <array>
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <stdexcept> #include <stdexcept>
@ -199,7 +200,8 @@ double Area(const Path &poly);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path); int PointInPolygon(const IntPoint &pt, const Path &path);
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd); // Union with "strictly simple" fix enabled.
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415);
@ -284,7 +286,25 @@ enum EdgeSide { esLeft = 1, esRight = 2};
using OutPts = std::vector<OutPt, Allocator<OutPt>>; using OutPts = std::vector<OutPt, Allocator<OutPt>>;
struct OutRec; // 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;
};
struct Join { struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {}
@ -432,12 +452,12 @@ protected:
private: private:
// Output polygons. // Output polygons.
std::vector<OutRec*, Allocator<OutRec*>> m_PolyOuts; std::deque<OutRec, Allocator<OutRec>> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize. // Output points, allocated by a continuous sets of m_OutPtsChunkSize.
std::vector<OutPt*, Allocator<OutPt*>> m_OutPts; static constexpr const size_t m_OutPtsChunkSize = 32;
std::deque<std::array<OutPt, m_OutPtsChunkSize>, Allocator<std::array<OutPt, m_OutPtsChunkSize>>> m_OutPts;
// List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk.
OutPt *m_OutPtsFree; OutPt *m_OutPtsFree;
size_t m_OutPtsChunkSize;
size_t m_OutPtsChunkLast; size_t m_OutPtsChunkLast;
std::vector<Join, Allocator<Join>> m_Joins; std::vector<Join, Allocator<Join>> m_Joins;
@ -482,7 +502,7 @@ private:
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);
void AppendPolygon(TEdge *e1, TEdge *e2) const; void AppendPolygon(TEdge *e1, TEdge *e2);
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);
@ -498,7 +518,7 @@ private:
void ProcessEdgesAtTopOfScanbeam(const cInt topY); void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys); void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree); void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec) const; void SetHoleState(TEdge *e, OutRec *outrec);
bool FixupIntersectionOrder(); bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec); void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec); void FixupOutPolyline(OutRec &outrec);
@ -508,8 +528,8 @@ private:
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
void JoinCommonEdges(); void JoinCommonEdges();
void DoSimplePolygons(); void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef CLIPPERLIB_USE_XYZ #ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif #endif
@ -567,10 +587,11 @@ class clipperException : public std::exception
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Union with "strictly simple" fix enabled.
template<typename PathsProvider> template<typename PathsProvider>
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) { inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) {
Clipper c; Clipper c;
c.StrictlySimple(true); c.StrictlySimple(strictly_simple);
c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true); c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true);
Paths out; Paths out;
c.Execute(ctUnion, out, fillType, fillType); c.Execute(ctUnion, out, fillType, fillType);

View File

@ -232,7 +232,7 @@ std::unique_ptr<LocToLineGrid> cre
void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
{ {
if (epsilon < 1) { if (epsilon < 1) {
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
return; return;
} }
@ -273,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
} }
} }
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
} }
/*! /*!

View File

@ -964,21 +964,18 @@ Polygons union_pt_chained_outside_in(const Polygons &subject)
return retval; return retval;
} }
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) Polygons simplify_polygons(const Polygons &subject)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths output; ClipperLib::Paths output;
if (preserve_collinear) { ClipperLib::Clipper c;
ClipperLib::Clipper c; // c.PreserveCollinear(true);
c.PreserveCollinear(true); //FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true); c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} else {
output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero);
}
// convert into Slic3r polygons // convert into Slic3r polygons
return to_polygons(std::move(output)); return to_polygons(std::move(output));
} }
@ -987,12 +984,10 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
if (! preserve_collinear)
return union_ex(simplify_polygons(subject, false));
ClipperLib::PolyTree polytree; ClipperLib::PolyTree polytree;
ClipperLib::Clipper c; ClipperLib::Clipper c;
c.PreserveCollinear(true); // c.PreserveCollinear(true);
//FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true); c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);

View File

@ -596,8 +596,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
/* OTHER */ /* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject);
Polygons top_level_islands(const Slic3r::Polygons &polygons); Polygons top_level_islands(const Slic3r::Polygons &polygons);