From a7e17df25f66d3fb4fce213574ba25ebea1d238d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 12:59:20 +0200 Subject: [PATCH 01/15] 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 --- src/clipper/clipper.cpp | 206 +++++++++++------------- src/clipper/clipper.hpp | 43 +++-- src/libslic3r/Arachne/WallToolPaths.cpp | 4 +- src/libslic3r/ClipperUtils.cpp | 25 ++- src/libslic3r/ClipperUtils.hpp | 4 +- 5 files changed, 137 insertions(+), 145 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index c775a3226..5da79d3a1 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -73,25 +73,6 @@ 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)) -// 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) @@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase(), m_OutPtsFree(nullptr), - m_OutPtsChunkSize(32), - m_OutPtsChunkLast(32), + m_OutPtsChunkLast(m_OutPtsChunkSize), m_ActiveEdges(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: The area is calculated with floats, it may not be numerically stable! { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0)) + ReversePolyPtLinks(outRec.Pts); } JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts) { + if (outRec.IsOpen) // Removes duplicate points. - FixupOutPolyline(*outRec); + FixupOutPolyline(outRec); else // 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. @@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt() m_OutPtsFree = pt->Next; } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { // Get a point from the last chunk. - pt = m_OutPts.back() + (m_OutPtsChunkLast ++); + pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); + m_OutPts.push_back({}); m_OutPtsChunkLast = 1; - pt = m_OutPts.back(); + pt = &m_OutPts.back().front(); } return pt; } void Clipper::DisposeAllOutRecs() { - for (OutPt *pts : m_OutPts) - delete[] pts; - for (OutRec *rec : m_PolyOuts) - delete rec; m_OutPts.clear(); m_OutPtsFree = nullptr; 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; TEdge *e2 = e->PrevInAEL; @@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const { IsHole = !IsHole; if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + outrec->FirstLeft = &m_PolyOuts[e2->OutIdx]; } e2 = e2->PrevInAEL; } @@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) OutRec* Clipper::GetOutRec(int Idx) { - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; + OutRec* outrec = &m_PolyOuts[Idx]; + while (outrec != &m_PolyOuts[outrec->Idx]) + outrec = &m_PolyOuts[outrec->Idx]; 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 ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + OutRec *outRec1 = &m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = &m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; if (Param1RightOfParam2(outRec1, outRec2)) @@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutRec* Clipper::CreateOutRec() { - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; + m_PolyOuts.push_back({}); + OutRec &result = m_PolyOuts.back(); + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = 0; + result.Pts = 0; + result.BottomPt = 0; + result.PolyNd = 0; + result.Idx = (int)m_PolyOuts.size()-1; + return &result; } //------------------------------------------------------------------------------ @@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) return newOp; } 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' OutPt* op = outRec->Pts; @@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) OutPt* Clipper::GetLastOutPt(TEdge *e) { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; if (e->Side == esLeft) return outRec->Pts; else @@ -2216,7 +2192,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; 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); @@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; + assert(! outRec.IsOpen); + if (!outRec.Pts) continue; Path pg; - OutPt* p = outRec->Pts->Prev; + OutPt* p = outRec.Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); @@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) + int cnt = PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3)) // Ignore an invalid output loop or a polyline. continue; //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) + 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; + outRec.FirstLeft = orfl; } //nb: polytree takes ownership of all the PolyNodes polytree.AllNodes.emplace_back(PolyNode()); PolyNode* pn = &polytree.AllNodes.back(); - outRec->PolyNd = pn; + outRec.PolyNd = pn; pn->Parent = 0; pn->Index = 0; pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; + OutPt *op = outRec.Pts->Prev; for (int j = 0; j < cnt; j++) { pn->Contour.emplace_back(op->Pt); @@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree) //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) + if (!outRec.PolyNd) continue; + if (outRec.IsOpen) { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); + outRec.PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec.PolyNd); } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd) + outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd); 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)! -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { //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; - OutRec* firstLeft = outRec->FirstLeft; + if (!outRec.Pts || !outRec.FirstLeft) continue; + OutRec* firstLeft = outRec.FirstLeft; // Skip empty polygons. while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; + if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) + 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 - for (OutRec *outRec : m_PolyOuts) - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + for (OutRec &outRec : m_PolyOuts) + if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec; } //---------------------------------------------------------------------- @@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges() if (m_UsingPolyTree) for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) { - OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; + OutRec &oRec = m_PolyOuts[j]; + 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; + if (!oRec.Pts || firstLeft != outRec1 || + oRec.IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2)) + oRec.FirstLeft = outRec2; } if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) @@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons() size_t i = 0; while (i < m_PolyOuts.size()) { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; + OutRec &outrec = m_PolyOuts[i++]; + OutPt* op = outrec.Pts; + if (!op || outrec.IsOpen) continue; do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; - while (op2 != outrec->Pts) + while (op2 != outrec.Pts) { if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) { @@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons() op2->Prev = op3; op3->Next = op2; - outrec->Pts = op; + outrec.Pts = op; OutRec* outrec2 = CreateOutRec(); outrec2->Pts = op2; UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts)) { //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; + outrec2->IsHole = !outrec.IsHole; + outrec2->FirstLeft = &outrec; // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec); } else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts)) { //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; + outrec2->IsHole = outrec.IsHole; + 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); + if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2); } else { //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; + 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); + if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2); } op2 = op; //ie get ready for the Next iteration } @@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons() } 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; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPath(in_poly, ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index d190d09b5..c88545454 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -52,6 +52,7 @@ //use_deprecated: Enables temporary support for the obsolete functions //#define use_deprecated +#include #include #include #include @@ -199,7 +200,8 @@ double Area(const Path &poly); inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } 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(Path& poly, double distance = 1.415); @@ -284,7 +286,25 @@ enum EdgeSide { esLeft = 1, esRight = 2}; using OutPts = std::vector>; - 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 { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} @@ -432,12 +452,12 @@ protected: private: // Output polygons. - std::vector> m_PolyOuts; + std::deque> m_PolyOuts; // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector> m_OutPts; + static constexpr const size_t m_OutPtsChunkSize = 32; + std::deque, Allocator>> m_OutPts; // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. OutPt *m_OutPtsFree; - size_t m_OutPtsChunkSize; size_t m_OutPtsChunkLast; std::vector> m_Joins; @@ -482,7 +502,7 @@ private: void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; + void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); @@ -498,7 +518,7 @@ private: void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; + void SetHoleState(TEdge *e, OutRec *outrec); bool FixupIntersectionOrder(); void FixupOutPolygon(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); void JoinCommonEdges(); void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef CLIPPERLIB_USE_XYZ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif @@ -567,10 +587,11 @@ class clipperException : public std::exception }; //------------------------------------------------------------------------------ +// Union with "strictly simple" fix enabled. template -inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) { +inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPaths(std::forward(in_polys), ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index fce69d5e4..6c5dafdac 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -232,7 +232,7 @@ std::unique_ptr cre void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) { if (epsilon < 1) { - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); 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); } /*! diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 95a533718..d10e14cc5 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -964,21 +964,18 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) 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); ClipperLib::Paths output; - if (preserve_collinear) { - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - } else { - output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero); - } - + ClipperLib::Clipper c; +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? + c.StrictlySimple(true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + // convert into Slic3r polygons 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); - if (! preserve_collinear) - return union_ex(simplify_polygons(subject, false)); - ClipperLib::PolyTree polytree; ClipperLib::Clipper c; - c.PreserveCollinear(true); +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? c.StrictlySimple(true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index aaa06107d..ab967fd8f 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -596,8 +596,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) /* OTHER */ -Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); -Slic3r::ExPolygons simplify_polygons_ex(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); Polygons top_level_islands(const Slic3r::Polygons &polygons); From 0836b06c7360462e3b8719864de4a446887c0d9e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:48:24 +0200 Subject: [PATCH 02/15] Little refactoring of douglas_peucker() --- src/libslic3r/ExPolygon.cpp | 4 ++-- src/libslic3r/Geometry.cpp | 16 ++++++------- src/libslic3r/MultiPoint.cpp | 2 +- src/libslic3r/MultiPoint.hpp | 2 +- src/libslic3r/Polygon.cpp | 45 +++++++++++++++++++++++++----------- src/libslic3r/Polygon.hpp | 3 ++- src/libslic3r/Polyline.cpp | 2 +- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 42f026e0b..19489bddb 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -184,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const { Polygon p = this->contour; p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } // holes for (Polygon p : this->holes) { p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 5542d73ee..f2860ea8e 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point); void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) { - Polygons pp; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { - Polygon p = *it; - p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - p.points.pop_back(); - pp.push_back(p); + Polygons simplified_raw; + for (const Polygon &source_polygon : polygons) { + Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance); + if (simplified.size() > 3) { + simplified.pop_back(); + simplified_raw.push_back(Polygon{ std::move(simplified) }); + } } - *retval = Slic3r::simplify_polygons(pp); + *retval = Slic3r::simplify_polygons(simplified_raw); } double linint(double value, double oldmin, double oldmax, double newmin, double newmax) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index bb4d62cc0..ce54a54c0 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -103,7 +103,7 @@ bool MultiPoint::remove_duplicate_points() return false; } -Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance) +Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; double tolerance_sq = tolerance * tolerance; diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 4cf4b5e14..62b53255b 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -81,7 +81,7 @@ public: } } - static Points _douglas_peucker(const Points &points, const double tolerance); + static Points douglas_peucker(const Points &points, const double tolerance); static Points visivalingam(const Points& pts, const double& tolerance); inline auto begin() { return points.begin(); } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 299e22adc..88ac1b03f 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -96,7 +96,7 @@ bool Polygon::make_clockwise() void Polygon::douglas_peucker(double tolerance) { this->points.push_back(this->points.front()); - Points p = MultiPoint::_douglas_peucker(this->points, tolerance); + Points p = MultiPoint::douglas_peucker(this->points, tolerance); p.pop_back(); this->points = std::move(p); } @@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const // on the whole polygon Points points = this->points; points.push_back(points.front()); - Polygon p(MultiPoint::_douglas_peucker(points, tolerance)); + Polygon p(MultiPoint::douglas_peucker(points, tolerance)); p.points.pop_back(); Polygons pp; @@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys) remove_collinear(poly); } -Polygons polygons_simplify(const Polygons &source_polygons, double tolerance) +static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out) +{ + Points simplified = MultiPoint::douglas_peucker(points, tolerance); + // then remove the last (repeated) point. + simplified.pop_back(); + // Simplify the decimated contour by ClipperLib. + bool ccw = ClipperLib::Area(simplified) > 0.; + for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) { + if (!ccw) + // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. + std::reverse(path.begin(), path.end()); + out.emplace_back(std::move(path)); + } +} + +Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */) +{ + Polygons out; + out.reserve(source_polygons.size()); + for (Polygon &source_polygon : source_polygons) { + // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), + source_polygon.points.emplace_back(source_polygon.points.front()); + simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out); + } + return out; +} + +Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */) { Polygons out; out.reserve(source_polygons.size()); for (const Polygon &source_polygon : source_polygons) { // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), - Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance); - // then remove the last (repeated) point. - simplified.pop_back(); - // Simplify the decimated contour by ClipperLib. - bool ccw = ClipperLib::Area(simplified) > 0.; - for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) { - if (! ccw) - // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. - std::reverse(path.begin(), path.end()); - out.emplace_back(std::move(path)); - } + simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out); } return out; } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index bf4a087b0..e0c3958fd 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -149,7 +149,8 @@ inline void polygons_append(Polygons &dst, Polygons &&src) } } -Polygons polygons_simplify(const Polygons &polys, double tolerance); +Polygons polygons_simplify(Polygons &&polys, double tolerance, bool strictly_simple = true); +Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true); inline void polygons_rotate(Polygons &polys, double angle) { diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 5743e38bd..524736575 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const void Polyline::simplify(double tolerance) { - this->points = MultiPoint::_douglas_peucker(this->points, tolerance); + this->points = MultiPoint::douglas_peucker(this->points, tolerance); } #if 0 From 31a5daa5e4ab315d668b1b63f1691ae4961235a6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:53:08 +0200 Subject: [PATCH 03/15] Optimization of triangle mesh slicing: scalable_allocator and hashing of shared mutexes. --- src/libslic3r/TriangleMeshSlicer.cpp | 42 ++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 460cd901e..0261b6121 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -15,6 +15,9 @@ #include #include +#include + +#include #ifndef NDEBUG // #define EXPENSIVE_DEBUG_CHECKS @@ -139,7 +142,7 @@ public: #endif }; -using IntersectionLines = std::vector; +using IntersectionLines = std::vector>; enum class FacetSliceType { NoSlice = 0, @@ -351,6 +354,21 @@ inline FacetSliceType slice_facet( return FacetSliceType::NoSlice; } +class LinesMutexes { +public: + std::mutex& operator()(size_t slice_id) { + ankerl::unordered_dense::hash hash; + return m_mutexes[hash(slice_id) % m_mutexes.size()].mutex; + } + +private: + struct CacheLineAlignedMutex + { + alignas(std::hardware_destructive_interference_size) std::mutex mutex; + }; + std::array m_mutexes; +}; + template void slice_facet_at_zs( // Scaled or unscaled vertices. transform_vertex_fn may scale zs. @@ -361,7 +379,7 @@ void slice_facet_at_zs( // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well. const std::vector &zs, std::vector &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) }; @@ -380,7 +398,7 @@ void slice_facet_at_zs( if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); size_t slice_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[slice_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slice_id)); lines[slice_id].emplace_back(il); } } @@ -395,8 +413,8 @@ static inline std::vector slice_make_lines( const std::vector &zs, const ThrowOnCancel throw_on_cancel_fn) { - std::vector lines(zs.size(), IntersectionLines()); - std::array lines_mutex; + std::vector lines(zs.size(), IntersectionLines{}); + LinesMutexes lines_mutex; tbb::parallel_for( tbb::blocked_range(0, int(indices.size())), [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range &range) { @@ -475,7 +493,7 @@ void slice_facet_with_slabs( const int num_edges, const std::vector &zs, SlabLines &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx]; stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] }; @@ -494,7 +512,7 @@ void slice_facet_with_slabs( auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) { if (reverse) il.reverse(); - boost::lock_guard l(lines_mutex[(slab_id + lines_mutex.size() / 2) % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slab_id)); lines.between_slices[slab_id].emplace_back(il); }; @@ -530,7 +548,7 @@ void slice_facet_with_slabs( }; // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining. // if (! ProjectionFromTop) il.reverse(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } else { @@ -649,7 +667,7 @@ void slice_facet_with_slabs( if (! ProjectionFromTop) il.reverse(); size_t line_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } @@ -804,8 +822,8 @@ inline std::pair slice_slabs_make_lines( std::pair out; SlabLines &lines_top = out.first; SlabLines &lines_bottom = out.second; - std::array lines_mutex_top; - std::array lines_mutex_bottom; + LinesMutexes lines_mutex_top; + LinesMutexes lines_mutex_bottom; if (top) { lines_top.at_slice.assign(zs.size(), IntersectionLines()); @@ -1540,7 +1558,7 @@ static std::vector make_slab_loops( } // Used to cut the mesh into two halves. -static ExPolygons make_expolygons_simple(std::vector &lines) +static ExPolygons make_expolygons_simple(IntersectionLines &lines) { ExPolygons slices; Polygons holes; From 04f557693b8369d7019e861a4bf26bab44ee4464 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:26:16 +0200 Subject: [PATCH 04/15] Follow-up to 31a5daa5e4ab315d668b1b63f1691ae4961235a6: Fixed missing include --- src/libslic3r/TriangleMeshSlicer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 0261b6121..2c6570219 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include From a077f669ee5409c7550df538dd00c6b294d7019f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:32:03 +0200 Subject: [PATCH 05/15] Slight optimization of distance_to_squared() --- src/libslic3r/Line.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 1edd1f5e9..d90757bed 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -40,11 +40,12 @@ template auto get_b(L &&l) { return Traits>::get_b(l) // Distance to the closest point of line. template -double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) +inline double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) { - const Vec, double> v = (get_b(line) - get_a(line)).template cast(); - const Vec, double> va = (point - get_a(line)).template cast(); - const double l2 = v.squaredNorm(); // avoid a sqrt + using VecType = Vec, double>; + const VecType v = (get_b(line) - get_a(line)).template cast(); + const VecType va = (point - get_a(line)).template cast(); + const double l2 = v.squaredNorm(); if (l2 == 0.0) { // a == b case *nearest_point = get_a(line); @@ -53,19 +54,20 @@ double distance_to_squared(const L &line, const Vec, Scalar> &point, V // Consider the line extending the segment, parameterized as a + t (b - a). // We find projection of this point onto the line. // It falls where t = [(this-a) . (b-a)] / |b-a|^2 - const double t = va.dot(v) / l2; + const double t = va.dot(v); if (t <= 0.0) { // beyond the 'a' end of the segment *nearest_point = get_a(line); return va.squaredNorm(); - } else if (t >= 1.0) { + } else if (t >= l2) { // beyond the 'b' end of the segment *nearest_point = get_b(line); return (point - get_b(line)).template cast().squaredNorm(); } - *nearest_point = (get_a(line).template cast() + t * v).template cast>(); - return (t * v - va).squaredNorm(); + const VecType w = ((t / l2) * v).eval(); + *nearest_point = (get_a(line).template cast() + w).template cast>(); + return (w - va).squaredNorm(); } // Distance to the closest point of line. From e7f4704ddc1c64a55b1a9bc57ecb3ffc3e6631b6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:35:39 +0200 Subject: [PATCH 06/15] Organic supports, TreeModelVolumes: Cheaper simplification. --- src/libslic3r/TreeModelVolumes.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 5a1fcec56..3c82b5c6c 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -34,6 +34,8 @@ using namespace std::literals; // had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() #define error_level_not_in_cache error +static constexpr const bool polygons_strictly_simple = false; + TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object) { const PrintConfig &print_config = print_object.print()->config(); @@ -175,7 +177,7 @@ TreeModelVolumes::TreeModelVolumes( tbb::parallel_for(tbb::blocked_range(num_raft_layers, num_layers, std::min(1, std::max(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution)); + outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution, polygons_strictly_simple); }); } #endif @@ -585,7 +587,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex if (processing_last_mesh) { if (! dst.empty()) collisions = union_(collisions, dst); - dst = polygons_simplify(collisions, min_resolution); + dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple); } else append(dst, std::move(collisions)); throw_on_cancel(); @@ -609,7 +611,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex if (processing_last_mesh) { if (! dst.empty()) placable = union_(placable, dst); - dst = polygons_simplify(placable, min_resolution); + dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple); } else append(dst, placable); throw_on_cancel(); @@ -657,7 +659,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vectorgetCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), - m_min_resolution)); + m_min_resolution, polygons_strictly_simple)); throw_on_cancel(); } } @@ -744,7 +746,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector &ke ClipperLib::jtRound, m_min_resolution)); if (task.to_model) latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel)); - latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution); + latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple); data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance); throw_on_cancel(); } @@ -865,12 +867,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector Date: Tue, 2 May 2023 18:17:08 +0200 Subject: [PATCH 07/15] ClipperLib: emplace_back() instead of push_back(). --- src/clipper/clipper.cpp | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index 5da79d3a1..3691877ee 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -112,7 +112,7 @@ int PolyTree::Total() const void PolyNode::AddChild(PolyNode& child) { unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); + Childs.emplace_back(&child); child.Parent = this; child.Index = cnt; } @@ -674,7 +674,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) locMin.RightBound = E; E->WindDelta = 0; Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); } return Result; } @@ -896,7 +896,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b E->NextInLML = E->Next; E = E->Next; } - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); return true; } @@ -949,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b locMin.LeftBound = 0; else if (locMin.RightBound->OutIdx == Skip) locMin.RightBound = 0; - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); if (!leftBoundIsForward) E = E2; } return true; @@ -1177,7 +1177,7 @@ OutPt* Clipper::AllocateOutPt() pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back({}); + m_OutPts.emplace_back(); m_OutPtsChunkLast = 1; pt = &m_OutPts.back().front(); } @@ -1967,7 +1967,7 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutRec* Clipper::CreateOutRec() { - m_PolyOuts.push_back({}); + m_PolyOuts.emplace_back(); OutRec &result = m_PolyOuts.back(); result.IsHole = false; result.IsOpen = false; @@ -2576,7 +2576,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - if (m_StrictSimple) m_Maxima.push_back(e->Top.x()); + if (m_StrictSimple) m_Maxima.emplace_back(e->Top.x()); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -3349,7 +3349,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType break; } newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); + newNode->Contour.emplace_back(path[0]); int j = 0, k = 0; for (int i = 1; i <= highI; i++) { bool same = false; @@ -3362,7 +3362,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType if (same) continue; j++; - newNode->Contour.push_back(path[i]); + newNode->Contour.emplace_back(path[i]); if (path[i].y() > newNode->Contour[k].y() || (path[i].y() == newNode->Contour[k].y() && path[i].x() < newNode->Contour[k].x())) k = j; @@ -3490,7 +3490,7 @@ void ClipperOffset::DoOffset(double delta) { PolyNode& node = *m_polyNodes.Childs[i]; if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); + m_destPolys.emplace_back(node.Contour); } return; } @@ -3532,7 +3532,7 @@ void ClipperOffset::DoOffset(double delta) double X = 1.0, Y = 0.0; for (cInt j = 1; j <= steps; j++) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[0].x() + X * delta), Round(m_srcPoly[0].y() + Y * delta))); double X2 = X; @@ -3545,7 +3545,7 @@ void ClipperOffset::DoOffset(double delta) double X = -1.0, Y = -1.0; for (int j = 0; j < 4; ++j) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[0].x() + X * delta), Round(m_srcPoly[0].y() + Y * delta))); if (X < 0) X = 1; @@ -3553,32 +3553,32 @@ void ClipperOffset::DoOffset(double delta) else X = -1; } } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); continue; } //build m_normals ... m_normals.clear(); m_normals.reserve(len); for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); else - m_normals.push_back(DoublePoint(m_normals[len - 2])); + m_normals.emplace_back(DoublePoint(m_normals[len - 2])); if (node.m_endtype == etClosedPolygon) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else if (node.m_endtype == etClosedLine) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); m_destPoly.clear(); //re-build m_normals ... DoublePoint n = m_normals[len -1]; @@ -3588,7 +3588,7 @@ void ClipperOffset::DoOffset(double delta) k = 0; for (int j = len - 1; j >= 0; j--) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else { @@ -3601,9 +3601,9 @@ void ClipperOffset::DoOffset(double delta) { int j = len - 1; pt1 = IntPoint2d(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); + m_destPoly.emplace_back(pt1); pt1 = IntPoint2d(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); + m_destPoly.emplace_back(pt1); } else { @@ -3628,9 +3628,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { pt1 = IntPoint2d(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); + m_destPoly.emplace_back(pt1); pt1 = IntPoint2d(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); + m_destPoly.emplace_back(pt1); } else { @@ -3641,7 +3641,7 @@ void ClipperOffset::DoOffset(double delta) else DoRound(0, 1); } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } } } @@ -3657,7 +3657,7 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() ); if (cosA > 0) // angle => 0 degrees { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); return; } @@ -3668,10 +3668,10 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) if (m_sinA * m_delta < 0) { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + m_destPoly.emplace_back(m_srcPoly[j]); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } else @@ -3695,10 +3695,10 @@ void ClipperOffset::DoSquare(int j, int k) { double dx = std::tan(std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4); - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); } @@ -3707,7 +3707,7 @@ void ClipperOffset::DoSquare(int j, int k) void ClipperOffset::DoMiter(int j, int k, double r) { double q = m_delta / r; - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); } //------------------------------------------------------------------------------ @@ -3721,14 +3721,14 @@ void ClipperOffset::DoRound(int j, int k) double X = m_normals[k].x(), Y = m_normals[k].y(), X2; for (int i = 0; i < steps; ++i) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + X * m_delta), Round(m_srcPoly[j].y() + Y * m_delta))); X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } @@ -3996,8 +3996,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); + pp.emplace_back(p); } else for (size_t i = 0; i < pathCnt; ++i) @@ -4005,8 +4005,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); + pp.emplace_back(p); } solution.clear(); @@ -4016,12 +4016,12 @@ void Minkowski(const Path& poly, const Path& path, { Path quad; quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]); if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); + solution.emplace_back(quad); } } //------------------------------------------------------------------------------ @@ -4081,7 +4081,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path else if (nodetype == ntOpen) return; if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); + paths.emplace_back(polynode.Contour); for (int i = 0; i < polynode.ChildCount(); ++i) AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); } @@ -4093,7 +4093,7 @@ void AddPolyNodeToPaths(PolyNode&& polynode, NodeType nodetype, Paths& paths) else if (nodetype == ntOpen) return; if (!polynode.Contour.empty() && match) - paths.push_back(std::move(polynode.Contour)); + paths.emplace_back(std::move(polynode.Contour)); for (int i = 0; i < polynode.ChildCount(); ++i) AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths); } @@ -4131,7 +4131,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) //Open paths are top level only, so ... for (int i = 0; i < polytree.ChildCount(); ++i) if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); + paths.emplace_back(polytree.Childs[i]->Contour); } //------------------------------------------------------------------------------ From 63ca221394acca695508dd333633b43b1a5e9744 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:20:38 +0200 Subject: [PATCH 08/15] douglas_peucker(): Optimized for 32bit Point types. --- src/libslic3r/MultiPoint.cpp | 44 +++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index ce54a54c0..90928d3c1 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -106,7 +106,7 @@ bool MultiPoint::remove_duplicate_points() Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; - double tolerance_sq = tolerance * tolerance; + auto tolerance_sq = int64_t(sqr(tolerance)); if (! pts.empty()) { const Point *anchor = &pts.front(); size_t anchor_idx = 0; @@ -120,14 +120,42 @@ Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) dpStack.reserve(pts.size()); dpStack.emplace_back(floater_idx); for (;;) { - double max_dist_sq = 0.0; - size_t furthest_idx = anchor_idx; + int64_t max_dist_sq = 0; + size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater); - if (dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; + { + const Point a = *anchor; + const Point f = *floater; + const Vec2i64 v = (f - a).cast(); + const int64_t l2 = v.squaredNorm(); + // Make up for rounding when converting from int64_t to double. Double mantissa is just 52 bits. + if (l2 < (1 << 14)) { + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) + if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } else { + const double dl2 = double(l2); + const Vec2d dv = v.cast(); + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { + const Point p = pts[i]; + const Vec2i64 va = (p - a).template cast(); + const int64_t t = va.dot(v); + int64_t dist_sq; + if (t <= 0) { + dist_sq = va.squaredNorm(); + } else if (t >= l2) { + dist_sq = (p - f).cast().squaredNorm(); + } else { + const Vec2i64 w = ((double(t) / dl2) * dv).cast(); + dist_sq = (w - va).squaredNorm(); + } + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } } } // remove point if less than tolerance From 2600ba71ad52d3f43d3efd244493458567a7b457 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:36:59 +0200 Subject: [PATCH 09/15] Fix of undefined hardware_destructive_interference_size --- src/libslic3r/TriangleMeshSlicer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 2c6570219..747f36215 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -36,6 +36,13 @@ #include #include +#ifdef __cpp_lib_hardware_interference_size + using std::hardware_destructive_interference_size; +#else + // 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... + constexpr std::size_t hardware_destructive_interference_size = 64; +#endif + // #define SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -365,7 +372,7 @@ public: private: struct CacheLineAlignedMutex { - alignas(std::hardware_destructive_interference_size) std::mutex mutex; + alignas(hardware_destructive_interference_size) std::mutex mutex; }; std::array m_mutexes; }; From 15ccecf8858f2f5e11da62b5ccb1b25300b27538 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:37:24 +0200 Subject: [PATCH 10/15] TreeSupport optimization: better parallel scaling, simpler simplification --- src/libslic3r/TreeSupport.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 174e91a9c..3452bb71c 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -59,6 +59,8 @@ namespace Slic3r namespace FFFTreeSupport { +static constexpr const bool polygons_strictly_simple = false; + TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings, const SlicingParameters &slicing_params) : angle(mesh_group_settings.support_tree_angle), angle_slow(mesh_group_settings.support_tree_angle_slow), @@ -925,13 +927,13 @@ static std::optional> polyline_sample_next_point_at_dis ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled(0.01)), collision_trimmed()); // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound. if (i % 10 == 7) - ret = polygons_simplify(ret, scaled(0.015)); + ret = polygons_simplify(ret, scaled(0.015), polygons_strictly_simple); } // offset the remainder float last_offset = distance - steps * step_size; if (last_offset > SCALED_EPSILON) ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled(0.01)); - ret = polygons_simplify(ret, scaled(0.015)); + ret = polygons_simplify(ret, scaled(0.015), polygons_strictly_simple); if (do_final_difference) ret = diff(ret, collision_trimmed()); @@ -1797,7 +1799,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di } if (settings.no_error && settings.move) // as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance. - polygons_simplify(increased, scaled(0.025)); + polygons_simplify(increased, scaled(0.025), polygons_strictly_simple); } else // if no movement is done the areas keep parent area as no move == offset(0) increased = parent.influence_area; @@ -1821,6 +1823,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; } else + // Cannot route to gracious areas. Push the tree away from object and route it down anyways. to_model_data = safe_union(diff_clipped(increased, volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance))); } } @@ -1966,7 +1969,7 @@ static void increase_areas_one_layer( { using AvoidanceType = TreeModelVolumes::AvoidanceType; - tbb::parallel_for(tbb::blocked_range(0, merging_areas.size()), + tbb::parallel_for(tbb::blocked_range(0, merging_areas.size(), 1), [&](const tbb::blocked_range &range) { for (size_t merging_area_idx = range.begin(); merging_area_idx < range.end(); ++ merging_area_idx) { SupportElementMerging &merging_area = merging_areas[merging_area_idx]; @@ -2209,9 +2212,10 @@ static void increase_areas_one_layer( // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); } + throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); } [[nodiscard]] static SupportElementState merge_support_element_states( @@ -3325,7 +3329,7 @@ static void finalize_interface_and_support_areas( base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50); //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution. - base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution))); + base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution)), polygons_strictly_simple); } if (! support_roof_polygons.empty() && ! base_layer_polygons.empty()) { @@ -4270,7 +4274,7 @@ static std::vector draw_branches( const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; - tbb::parallel_for(tbb::blocked_range(0, trees.size()), + tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), [&trees, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; @@ -4322,9 +4326,9 @@ static std::vector draw_branches( tree.first_layer_id = new_begin; } } - }); + }, tbb::simple_partitioner()); - tbb::parallel_for(tbb::blocked_range(0, trees.size()), + tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), [&trees, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) { Tree &tree = trees[tree_id]; @@ -4335,7 +4339,7 @@ static std::vector draw_branches( } throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); size_t num_layers = 0; for (Tree &tree : trees) @@ -4356,14 +4360,14 @@ static std::vector draw_branches( } std::vector support_layer_storage(move_bounds.size()); - tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size())), + tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size()), 1), [&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) { Slice &slice = slices[slice_id]; support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons); throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); //FIXME simplify! return support_layer_storage; From 0c6f2261a3518ef2ae5337e34179632c503d4f48 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:45:07 +0200 Subject: [PATCH 11/15] Tree Supports: Extend bottoms of trees downwards to the object surface if those supports are not sitting on a flat surface. --- src/libslic3r/TreeSupport.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 3452bb71c..820b60d49 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -4275,7 +4275,7 @@ static std::vector draw_branches( MeshSlicingParams mesh_slicing_params; mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), - [&trees, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { + [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) { @@ -4297,7 +4297,23 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); - size_t num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + size_t num_empty = 0; + + if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { + // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. + std::vector bottom_extra_slices; + Polygons rest_support; + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) { + rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); + if (area(rest_support) < tiny_area_threshold) + break; + bottom_extra_slices.emplace_back(rest_support); + } + layer_begin -= LayerIndex(bottom_extra_slices.size()); + slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend())); + } else + num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + layer_begin += LayerIndex(num_empty); for (; slices.back().empty(); -- layer_end); LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); From d0f38cd0b4c65e0b2b2e91d9df3669fc3f65bdd8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 19:26:33 +0200 Subject: [PATCH 12/15] macOS clang is buggy, it does not implement __cpp_lib_hardware_interference_size correctly. --- src/libslic3r/TriangleMeshSlicer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 747f36215..da696e1ec 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -36,7 +36,7 @@ #include #include -#ifdef __cpp_lib_hardware_interference_size +#if defined(__cpp_lib_hardware_interference_size) && ! defined(__APPLE__) using std::hardware_destructive_interference_size; #else // 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... From af6c27b8613959319bbb66d1f066e55c2e231b1e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 19:50:12 +0200 Subject: [PATCH 13/15] douglas_peucker(): fix after 63ca221394acca695508dd333633b43b1a5e9744 --- src/libslic3r/MultiPoint.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 90928d3c1..fb4727abe 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -124,12 +124,10 @@ Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it { - const Point a = *anchor; - const Point f = *floater; + const Point a = *anchor; + const Point f = *floater; const Vec2i64 v = (f - a).cast(); - const int64_t l2 = v.squaredNorm(); - // Make up for rounding when converting from int64_t to double. Double mantissa is just 52 bits. - if (l2 < (1 << 14)) { + if (const int64_t l2 = v.squaredNorm(); l2 == 0) { for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { max_dist_sq = dist_sq; From 8593ad1f80011994b33a130c2d634cb83acda5f6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 15:26:41 +0200 Subject: [PATCH 14/15] Organic supports improvements: Removing collisions with trees, limiting how far tree bottoms at slanted surfaces could be extended down below their last full circle position. Placable areas are now calculated sitting on slightly inflated top surface to indicate support of tree bottoms at slanted surfaces. --- src/libslic3r/ClipperUtils.cpp | 2 + src/libslic3r/ClipperUtils.hpp | 3 + src/libslic3r/TreeModelVolumes.cpp | 17 ++-- src/libslic3r/TreeModelVolumes.hpp | 3 +- src/libslic3r/TreeSupport.cpp | 156 ++++++++++++++++++++++------- src/libslic3r/TreeSupport.hpp | 12 +++ 6 files changed, 147 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d10e14cc5..85ef53c88 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -685,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index ab967fd8f..774e9cb42 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -453,6 +453,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). +// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 3c82b5c6c..10840d1b5 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -426,7 +426,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L return (*result).get(); if (m_precalculated) { BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; - tree_supports_show_error("Not precalculated Placeable areas requested."sv, false); + tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false); } if (orig_radius == 0) // Placable areas for radius 0 are calculated in the general collision code. @@ -597,16 +597,19 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 3) Optionally calculate placables. if (calculate_placable) { // Now calculate the placable areas. - tbb::parallel_for(tbb::blocked_range(std::max(data.idx_begin, 1), data.idx_end), - [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh, - min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel] + tbb::parallel_for(tbb::blocked_range(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end), + [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh, + min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel] (const tbb::blocked_range& range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { - LayerIndex layer_idx_below = layer_idx - 1; + LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1; assert(layer_idx_below >= 0); const Polygons ¤t = collision_areas_offsetted[layer_idx]; - const Polygons &below = collision_areas_offsetted[layer_idx_below]; - Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); + const Polygons &below = outlines[layer_idx_below]; + Polygons placable = diff( + // Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface. + offset(below, xy_distance), + layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); auto &dst = data_placeable[layer_idx]; if (processing_last_mesh) { if (! dst.empty()) diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp index 139b12328..659baf1ff 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -215,6 +215,7 @@ public: void clear() { this->clear_all_but_object_collision(); m_collision_cache.clear(); + m_placeable_areas_cache.clear(); } void clear_all_but_object_collision() { //m_collision_cache.clear_all_but_radius0(); @@ -223,7 +224,7 @@ public: m_avoidance_cache_slow.clear(); m_avoidance_cache_to_model.clear(); m_avoidance_cache_to_model_slow.clear(); - m_placeable_areas_cache.clear(); + m_placeable_areas_cache.clear_all_but_radius0(); m_avoidance_cache_holefree.clear(); m_avoidance_cache_holefree_to_model.clear(); m_wall_restrictions_cache.clear(); diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 820b60d49..9a7b203c6 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -299,7 +299,7 @@ static bool inline g_showed_critical_error = false; static bool inline g_showed_performance_warning = false; void tree_supports_show_error(std::string_view message, bool critical) { // todo Remove! ONLY FOR PUBLIC BETA!! - +// printf("Error: %s, critical: %d\n", message.data(), int(critical)); #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 static bool showed_critical = false; static bool showed_performance = false; @@ -2158,6 +2158,10 @@ static void increase_areas_one_layer( " Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist << " to buildplate " << parent.state.to_buildplate << " gracious " << parent.state.to_model_gracious << " safe " << parent.state.can_use_safe_radius << " until move " << parent.state.dont_move_until; tree_supports_show_error("Potentially lost branch!"sv, true); +#ifdef TREE_SUPPORTS_TRACK_LOST + if (result) + result->lost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } else result = increase_single_area(volumes, config, settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer); @@ -2211,6 +2215,9 @@ static void increase_areas_one_layer( // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); +#ifdef TREE_SUPPORTS_TRACK_LOST + parent.state.verylost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } throw_on_cancel(); @@ -4234,26 +4241,35 @@ static std::vector draw_branches( branch.path.emplace_back(&start_element); // Traverse each branch until it branches again. SupportElement &first_parent = layer_above[start_element.parents[parent_idx]]; + assert(! first_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx); branch.path.emplace_back(&first_parent); if (first_parent.parents.size() < 2) first_parent.state.marked = true; SupportElement *next_branch = nullptr; - if (first_parent.parents.size() == 1) + if (first_parent.parents.size() == 1) { for (SupportElement *parent = &first_parent;;) { + assert(parent->state.marked); SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()]; + assert(! next_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx); branch.path.emplace_back(&next_parent); if (next_parent.parents.size() > 1) { + // Branching point was reached. next_branch = &next_parent; break; } next_parent.state.marked = true; if (next_parent.parents.size() == 0) + // Tip is reached. break; parent = &next_parent; } + } else if (first_parent.parents.size() > 1) + // Branching point was reached. + next_branch = &first_parent; assert(branch.path.size() >= 2); + assert(next_branch == nullptr || ! next_branch->state.marked); branch.has_root = root; branch.has_tip = ! next_branch; out.branches.emplace_back(std::move(branch)); @@ -4263,13 +4279,37 @@ static std::vector draw_branches( } }; - for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) - for (SupportElement &start_element : move_bounds[layer_idx]) - if (! start_element.state.marked && ! start_element.parents.empty()) { + for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) { +// int ielement; + for (SupportElement& start_element : move_bounds[layer_idx]) { + if (!start_element.state.marked && !start_element.parents.empty()) { +#if 0 + int found = 0; + if (layer_idx > 0) { + for (auto& el : move_bounds[layer_idx - 1]) { + for (auto iparent : el.parents) + if (iparent == ielement) + ++found; + } + if (found != 0) + printf("Found: %d\n", found); + } +#endif trees.push_back({}); TreeVisitor::visit_recursive(move_bounds, start_element, trees.back()); - assert(! trees.back().branches.empty()); + assert(!trees.back().branches.empty()); + //FIXME debugging +#if 0 + if (start_element.state.lost) { + } + else if (start_element.state.verylost) { + } else + trees.pop_back(); +#endif } +// ++ ielement; + } + } const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; @@ -4297,49 +4337,89 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); - size_t num_empty = 0; + //FIXME parallelize? + for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i) + slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); + size_t num_empty = 0; if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. - std::vector bottom_extra_slices; - Polygons rest_support; - for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) { + struct BottomExtraSlice { + Polygons polygons; + Polygons supported; + double area; + double supported_area; + }; + std::vector bottom_extra_slices; + Polygons rest_support; + coord_t bottom_radius = config.getRadius(branch.path.front()->state); + // Don't propagate further than 1.5 * bottom radius. + //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; + LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; + LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); + // Only propagate until the rest area is smaller than this threshold. + double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); + // Only propagate until the rest area is smaller than this threshold. + double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); - if (area(rest_support) < tiny_area_threshold) + double rest_support_area = area(rest_support); + if (rest_support_area < support_area_stop) + // Don't propagate a fraction of the tree contact surface. break; - bottom_extra_slices.emplace_back(rest_support); + // Measure how much the rest_support is actually supported. + /* + Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{})); + double supported_area = area(supported); + printf("Supported area: %d, %lf\n", layer_idx, supported_area); + */ + Polygons supported; + double supported_area; + bottom_extra_slices.push_back({ rest_support, std::move(supported), rest_support_area, supported_area }); } + // Now remove those bottom slices that are not supported at all. + while (! bottom_extra_slices.empty() && + area(intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}))) < support_area_min) + bottom_extra_slices.pop_back(); layer_begin -= LayerIndex(bottom_extra_slices.size()); - slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend())); + slices.insert(slices.begin(), bottom_extra_slices.size(), {}); + size_t i = 0; + for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i) + slices[i] = std::move(it->polygons); } else num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); layer_begin += LayerIndex(num_empty); - for (; slices.back().empty(); -- layer_end); - LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); - LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); - size_t new_size = size_t(new_end - new_begin); - if (tree.first_layer_id == -1) { - } else if (tree.slices.capacity() < new_size) { - std::vector new_slices; - new_slices.reserve(new_size); - if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - new_slices.insert(new_slices.end(), dif, {}); - append(new_slices, std::move(tree.slices)); - tree.slices.swap(new_slices); - } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); - tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); - layer_begin -= LayerIndex(num_empty); - for (LayerIndex i = layer_begin; i != layer_end; ++ i) - if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { - Slice &dst = tree.slices[i - new_begin]; - if (++ dst.num_branches > 1) - append(dst.polygons, std::move(src)); - else - dst.polygons = std::move(std::move(src)); - } - tree.first_layer_id = new_begin; + while (! slices.empty() && slices.back().empty()) { + slices.pop_back(); + -- layer_end; + } + if (layer_begin < layer_end) { + LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); + LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); + size_t new_size = size_t(new_end - new_begin); + if (tree.first_layer_id == -1) { + } else if (tree.slices.capacity() < new_size) { + std::vector new_slices; + new_slices.reserve(new_size); + if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + new_slices.insert(new_slices.end(), dif, {}); + append(new_slices, std::move(tree.slices)); + tree.slices.swap(new_slices); + } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); + tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); + layer_begin -= LayerIndex(num_empty); + for (LayerIndex i = layer_begin; i != layer_end; ++ i) + if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { + Slice &dst = tree.slices[i - new_begin]; + if (++ dst.num_branches > 1) + append(dst.polygons, std::move(src)); + else + dst.polygons = std::move(std::move(src)); + } + tree.first_layer_id = new_begin; + } } } }, tbb::simple_partitioner()); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 070903096..2c87a132d 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -93,6 +93,8 @@ struct AreaIncreaseSettings struct TreeSupportSettings; +// #define TREE_SUPPORTS_TRACK_LOST + // C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided. struct SupportElementStateBits { SupportElementStateBits() : @@ -102,6 +104,10 @@ struct SupportElementStateBits { supports_roof(false), can_use_safe_radius(false), skip_ovalisation(false), +#ifdef TREE_SUPPORTS_TRACK_LOST + lost(false), + verylost(false), +#endif // TREE_SUPPORTS_TRACK_LOST deleted(false), marked(false) {} @@ -136,6 +142,12 @@ struct SupportElementStateBits { */ bool skip_ovalisation : 1; +#ifdef TREE_SUPPORTS_TRACK_LOST + // Likely a lost branch, debugging information. + bool lost : 1; + bool verylost : 1; +#endif // TREE_SUPPORTS_TRACK_LOST + // Not valid anymore, to be deleted. bool deleted : 1; From 3b9037b44248ca19a49545ed31933c1b4c31bfe4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 15:27:09 +0200 Subject: [PATCH 15/15] nlopt in RelWithDebInfo now compiles as release. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b9a777b..49fe3437f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES}) endforeach() find_package(NLopt 1.4 REQUIRED) +slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release) if(SLIC3R_STATIC) set(OPENVDB_USE_STATIC_LIBS ON)