Merge branch 'master' into fs_dir_per_glyph

This commit is contained in:
Filip Sykala - NTB T15p 2023-05-09 13:01:08 +02:00
commit 5a517e71de
111 changed files with 4771 additions and 3948 deletions

View File

@ -406,6 +406,7 @@ endif()
set(TBB_DEBUG 1) set(TBB_DEBUG 1)
find_package(TBB REQUIRED) find_package(TBB REQUIRED)
slic3r_remap_configs(TBB::tbb RelWithDebInfo Release) slic3r_remap_configs(TBB::tbb RelWithDebInfo Release)
slic3r_remap_configs(TBB::tbbmalloc RelWithDebInfo Release)
# include_directories(${TBB_INCLUDE_DIRS}) # include_directories(${TBB_INCLUDE_DIRS})
# add_definitions(${TBB_DEFINITIONS}) # add_definitions(${TBB_DEFINITIONS})
# if(MSVC) # if(MSVC)
@ -545,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES})
endforeach() endforeach()
find_package(NLopt 1.4 REQUIRED) find_package(NLopt 1.4 REQUIRED)
slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release)
if(SLIC3R_STATIC) if(SLIC3R_STATIC)
set(OPENVDB_USE_STATIC_LIBS ON) set(OPENVDB_USE_STATIC_LIBS ON)

View File

@ -8,3 +8,5 @@ add_library(clipper STATIC
clipper_z.cpp clipper_z.cpp
clipper_z.hpp clipper_z.hpp
) )
target_link_libraries(clipper TBB::tbb TBB::tbbmalloc)

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)
@ -131,7 +112,7 @@ int PolyTree::Total() const
void PolyNode::AddChild(PolyNode& child) void PolyNode::AddChild(PolyNode& child)
{ {
unsigned cnt = (unsigned)Childs.size(); unsigned cnt = (unsigned)Childs.size();
Childs.push_back(&child); Childs.emplace_back(&child);
child.Parent = this; child.Parent = this;
child.Index = cnt; child.Index = cnt;
} }
@ -693,7 +674,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
locMin.RightBound = E; locMin.RightBound = E;
E->WindDelta = 0; E->WindDelta = 0;
Result = ProcessBound(E, NextIsForward); Result = ProcessBound(E, NextIsForward);
m_MinimaList.push_back(locMin); m_MinimaList.emplace_back(locMin);
} }
return Result; return Result;
} }
@ -784,7 +765,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
return false; return false;
// Allocate a new edge array. // Allocate a new edge array.
std::vector<TEdge> edges(highI + 1); Edges edges(highI + 1);
// Fill in the edge array. // Fill in the edge array.
bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data());
if (result) if (result)
@ -915,7 +896,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
E->NextInLML = E->Next; E->NextInLML = E->Next;
E = E->Next; E = E->Next;
} }
m_MinimaList.push_back(locMin); m_MinimaList.emplace_back(locMin);
return true; return true;
} }
@ -968,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
locMin.LeftBound = 0; locMin.LeftBound = 0;
else if (locMin.RightBound->OutIdx == Skip) else if (locMin.RightBound->OutIdx == Skip)
locMin.RightBound = 0; locMin.RightBound = 0;
m_MinimaList.push_back(locMin); m_MinimaList.emplace_back(locMin);
if (!leftBoundIsForward) E = E2; if (!leftBoundIsForward) E = E2;
} }
return true; return true;
@ -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)
{ {
@ -1079,7 +1059,7 @@ Clipper::Clipper(int initOptions) :
void Clipper::Reset() void Clipper::Reset()
{ {
ClipperBase::Reset(); ClipperBase::Reset();
m_Scanbeam = std::priority_queue<cInt>(); m_Scanbeam = std::priority_queue<cInt, cInts>{};
m_Maxima.clear(); m_Maxima.clear();
m_ActiveEdges = 0; m_ActiveEdges = 0;
m_SortedEdges = 0; m_SortedEdges = 0;
@ -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.emplace_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.emplace_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);
@ -2226,8 +2202,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if (!eLastHorz->NextInLML) if (!eLastHorz->NextInLML)
eMaxPair = GetMaximaPair(eLastHorz); eMaxPair = GetMaximaPair(eLastHorz);
std::vector<cInt>::const_iterator maxIt; cInts::const_iterator maxIt;
std::vector<cInt>::const_reverse_iterator maxRit; cInts::const_reverse_iterator maxRit;
if (!m_Maxima.empty()) if (!m_Maxima.empty())
{ {
//get the first maxima in range (X) ... //get the first maxima in range (X) ...
@ -2600,7 +2576,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
if(IsMaximaEdge) 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; TEdge* ePrev = e->PrevInAEL;
DoMaxima(e); DoMaxima(e);
if( !ePrev ) e = m_ActiveEdges; if( !ePrev ) e = m_ActiveEdges;
@ -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))
@ -3373,7 +3349,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
break; break;
} }
newNode->Contour.reserve(highI + 1); newNode->Contour.reserve(highI + 1);
newNode->Contour.push_back(path[0]); newNode->Contour.emplace_back(path[0]);
int j = 0, k = 0; int j = 0, k = 0;
for (int i = 1; i <= highI; i++) { for (int i = 1; i <= highI; i++) {
bool same = false; bool same = false;
@ -3386,7 +3362,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
if (same) if (same)
continue; continue;
j++; j++;
newNode->Contour.push_back(path[i]); newNode->Contour.emplace_back(path[i]);
if (path[i].y() > newNode->Contour[k].y() || if (path[i].y() > newNode->Contour[k].y() ||
(path[i].y() == newNode->Contour[k].y() && (path[i].y() == newNode->Contour[k].y() &&
path[i].x() < newNode->Contour[k].x())) k = j; path[i].x() < newNode->Contour[k].x())) k = j;
@ -3514,7 +3490,7 @@ void ClipperOffset::DoOffset(double delta)
{ {
PolyNode& node = *m_polyNodes.Childs[i]; PolyNode& node = *m_polyNodes.Childs[i];
if (node.m_endtype == etClosedPolygon) if (node.m_endtype == etClosedPolygon)
m_destPolys.push_back(node.Contour); m_destPolys.emplace_back(node.Contour);
} }
return; return;
} }
@ -3556,7 +3532,7 @@ void ClipperOffset::DoOffset(double delta)
double X = 1.0, Y = 0.0; double X = 1.0, Y = 0.0;
for (cInt j = 1; j <= steps; j++) 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].x() + X * delta),
Round(m_srcPoly[0].y() + Y * delta))); Round(m_srcPoly[0].y() + Y * delta)));
double X2 = X; double X2 = X;
@ -3569,7 +3545,7 @@ void ClipperOffset::DoOffset(double delta)
double X = -1.0, Y = -1.0; double X = -1.0, Y = -1.0;
for (int j = 0; j < 4; ++j) 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].x() + X * delta),
Round(m_srcPoly[0].y() + Y * delta))); Round(m_srcPoly[0].y() + Y * delta)));
if (X < 0) X = 1; if (X < 0) X = 1;
@ -3577,32 +3553,32 @@ void ClipperOffset::DoOffset(double delta)
else X = -1; else X = -1;
} }
} }
m_destPolys.push_back(m_destPoly); m_destPolys.emplace_back(m_destPoly);
continue; continue;
} }
//build m_normals ... //build m_normals ...
m_normals.clear(); m_normals.clear();
m_normals.reserve(len); m_normals.reserve(len);
for (int j = 0; j < len - 1; ++j) 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) 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 else
m_normals.push_back(DoublePoint(m_normals[len - 2])); m_normals.emplace_back(DoublePoint(m_normals[len - 2]));
if (node.m_endtype == etClosedPolygon) if (node.m_endtype == etClosedPolygon)
{ {
int k = len - 1; int k = len - 1;
for (int j = 0; j < len; ++j) for (int j = 0; j < len; ++j)
OffsetPoint(j, k, node.m_jointype); OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly); m_destPolys.emplace_back(m_destPoly);
} }
else if (node.m_endtype == etClosedLine) else if (node.m_endtype == etClosedLine)
{ {
int k = len - 1; int k = len - 1;
for (int j = 0; j < len; ++j) for (int j = 0; j < len; ++j)
OffsetPoint(j, k, node.m_jointype); OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly); m_destPolys.emplace_back(m_destPoly);
m_destPoly.clear(); m_destPoly.clear();
//re-build m_normals ... //re-build m_normals ...
DoublePoint n = m_normals[len -1]; DoublePoint n = m_normals[len -1];
@ -3612,7 +3588,7 @@ void ClipperOffset::DoOffset(double delta)
k = 0; k = 0;
for (int j = len - 1; j >= 0; j--) for (int j = len - 1; j >= 0; j--)
OffsetPoint(j, k, node.m_jointype); OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly); m_destPolys.emplace_back(m_destPoly);
} }
else else
{ {
@ -3625,9 +3601,9 @@ void ClipperOffset::DoOffset(double delta)
{ {
int j = len - 1; 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)); 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)); 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 else
{ {
@ -3652,9 +3628,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt) 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)); 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)); 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 else
{ {
@ -3665,7 +3641,7 @@ void ClipperOffset::DoOffset(double delta)
else else
DoRound(0, 1); DoRound(0, 1);
} }
m_destPolys.push_back(m_destPoly); m_destPolys.emplace_back(m_destPoly);
} }
} }
} }
@ -3681,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() ); double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() );
if (cosA > 0) // angle => 0 degrees 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))); Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
return; return;
} }
@ -3692,10 +3668,10 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
if (m_sinA * m_delta < 0) 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))); Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
m_destPoly.push_back(m_srcPoly[j]); m_destPoly.emplace_back(m_srcPoly[j]);
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), 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))); Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
} }
else else
@ -3719,10 +3695,10 @@ void ClipperOffset::DoSquare(int j, int k)
{ {
double dx = std::tan(std::atan2(m_sinA, 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_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].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)))); 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].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)))); Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx))));
} }
@ -3731,7 +3707,7 @@ void ClipperOffset::DoSquare(int j, int k)
void ClipperOffset::DoMiter(int j, int k, double r) void ClipperOffset::DoMiter(int j, int k, double r)
{ {
double q = m_delta / 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))); Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q)));
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3745,14 +3721,14 @@ void ClipperOffset::DoRound(int j, int k)
double X = m_normals[k].x(), Y = m_normals[k].y(), X2; double X = m_normals[k].x(), Y = m_normals[k].y(), X2;
for (int i = 0; i < steps; ++i) 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].x() + X * m_delta),
Round(m_srcPoly[j].y() + Y * m_delta))); Round(m_srcPoly[j].y() + Y * m_delta)));
X2 = X; X2 = X;
X = X * m_cos - m_sin * Y; X = X * m_cos - m_sin * Y;
Y = X2 * m_sin + Y * m_cos; 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].x() + m_normals[j].x() * m_delta),
Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
} }
@ -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);
@ -3941,7 +3917,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
return; return;
} }
std::vector<OutPt> outPts(size); OutPts outPts(size);
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
{ {
outPts[i].Pt = in_poly[i]; outPts[i].Pt = in_poly[i];
@ -4020,8 +3996,8 @@ void Minkowski(const Path& poly, const Path& path,
Path p; Path p;
p.reserve(polyCnt); p.reserve(polyCnt);
for (size_t j = 0; j < poly.size(); ++j) 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())); p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y()));
pp.push_back(p); pp.emplace_back(p);
} }
else else
for (size_t i = 0; i < pathCnt; ++i) for (size_t i = 0; i < pathCnt; ++i)
@ -4029,8 +4005,8 @@ void Minkowski(const Path& poly, const Path& path,
Path p; Path p;
p.reserve(polyCnt); p.reserve(polyCnt);
for (size_t j = 0; j < poly.size(); ++j) 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())); p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y()));
pp.push_back(p); pp.emplace_back(p);
} }
solution.clear(); solution.clear();
@ -4040,12 +4016,12 @@ void Minkowski(const Path& poly, const Path& path,
{ {
Path quad; Path quad;
quad.reserve(4); quad.reserve(4);
quad.push_back(pp[i % pathCnt][j % polyCnt]); quad.emplace_back(pp[i % pathCnt][j % polyCnt]);
quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]);
quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]);
if (!Orientation(quad)) ReversePath(quad); if (!Orientation(quad)) ReversePath(quad);
solution.push_back(quad); solution.emplace_back(quad);
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -4105,7 +4081,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path
else if (nodetype == ntOpen) return; else if (nodetype == ntOpen) return;
if (!polynode.Contour.empty() && match) if (!polynode.Contour.empty() && match)
paths.push_back(polynode.Contour); paths.emplace_back(polynode.Contour);
for (int i = 0; i < polynode.ChildCount(); ++i) for (int i = 0; i < polynode.ChildCount(); ++i)
AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
} }
@ -4117,7 +4093,7 @@ void AddPolyNodeToPaths(PolyNode&& polynode, NodeType nodetype, Paths& paths)
else if (nodetype == ntOpen) return; else if (nodetype == ntOpen) return;
if (!polynode.Contour.empty() && match) 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) for (int i = 0; i < polynode.ChildCount(); ++i)
AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths); AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths);
} }
@ -4155,7 +4131,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
//Open paths are top level only, so ... //Open paths are top level only, so ...
for (int i = 0; i < polytree.ChildCount(); ++i) for (int i = 0; i < polytree.ChildCount(); ++i)
if (polytree.Childs[i]->IsOpen()) if (polytree.Childs[i]->IsOpen())
paths.push_back(polytree.Childs[i]->Contour); paths.emplace_back(polytree.Childs[i]->Contour);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -39,6 +39,8 @@
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <oneapi/tbb/scalable_allocator.h>
#define CLIPPER_VERSION "6.2.6" #define CLIPPER_VERSION "6.2.6"
//CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance. //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance.
@ -50,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>
@ -112,8 +115,11 @@ using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
typedef std::vector<IntPoint> Path; template<typename BaseType>
typedef std::vector<Path> Paths; using Allocator = tbb::scalable_allocator<BaseType>;
//using Allocator = std::allocator<BaseType>;
using Path = std::vector<IntPoint, Allocator<IntPoint>>;
using Paths = std::vector<Path, Allocator<Path>>;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
@ -133,7 +139,7 @@ enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
class PolyNode; class PolyNode;
typedef std::vector< PolyNode* > PolyNodes; typedef std::vector<PolyNode*, Allocator<PolyNode*>> PolyNodes;
class PolyNode class PolyNode
{ {
@ -186,7 +192,7 @@ public:
private: private:
PolyTree(const PolyTree &src) = delete; PolyTree(const PolyTree &src) = delete;
PolyTree& operator=(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete;
std::vector<PolyNode> AllNodes; std::vector<PolyNode, Allocator<PolyNode>> AllNodes;
friend class Clipper; //to access AllNodes friend class Clipper; //to access AllNodes
}; };
@ -194,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);
@ -277,7 +284,27 @@ enum EdgeSide { esLeft = 1, esRight = 2};
OutPt *Prev; OutPt *Prev;
}; };
struct OutRec; using OutPts = std::vector<OutPt, Allocator<OutPt>>;
// 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) {}
@ -312,7 +339,7 @@ public:
if (num_paths == 1) if (num_paths == 1)
return AddPath(*paths_provider.begin(), PolyTyp, Closed); return AddPath(*paths_provider.begin(), PolyTyp, Closed);
std::vector<int> num_edges(num_paths, 0); std::vector<int, Allocator<int>> num_edges(num_paths, 0);
int num_edges_total = 0; int num_edges_total = 0;
size_t i = 0; size_t i = 0;
for (const Path &pg : paths_provider) { for (const Path &pg : paths_provider) {
@ -333,7 +360,7 @@ public:
return false; return false;
// Allocate a new edge array. // Allocate a new edge array.
std::vector<TEdge> edges(num_edges_total); std::vector<TEdge, Allocator<TEdge>> edges(num_edges_total);
// Fill in the edge array. // Fill in the edge array.
bool result = false; bool result = false;
TEdge *p_edge = edges.data(); TEdge *p_edge = edges.data();
@ -369,7 +396,7 @@ protected:
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
// Local minima (Y, left edge, right edge) sorted by ascending Y. // Local minima (Y, left edge, right edge) sorted by ascending Y.
std::vector<LocalMinimum> m_MinimaList; std::vector<LocalMinimum, Allocator<LocalMinimum>> m_MinimaList;
#ifdef CLIPPERLIB_INT32 #ifdef CLIPPERLIB_INT32
static constexpr const bool m_UseFullRange = false; static constexpr const bool m_UseFullRange = false;
@ -380,7 +407,8 @@ protected:
#endif // CLIPPERLIB_INT32 #endif // CLIPPERLIB_INT32
// A vector of edges per each input path. // A vector of edges per each input path.
std::vector<std::vector<TEdge>> m_edges; using Edges = std::vector<TEdge, Allocator<TEdge>>;
std::vector<Edges, Allocator<Edges>> m_edges;
// Don't remove intermediate vertices of a collinear sequence of points. // Don't remove intermediate vertices of a collinear sequence of points.
bool m_PreserveCollinear; bool m_PreserveCollinear;
// Is any of the paths inserted by AddPath() or AddPaths() open? // Is any of the paths inserted by AddPath() or AddPaths() open?
@ -424,22 +452,23 @@ protected:
private: private:
// Output polygons. // Output polygons.
std::vector<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*> 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> m_Joins; std::vector<Join, Allocator<Join>> m_Joins;
std::vector<Join> m_GhostJoins; std::vector<Join, Allocator<Join>> m_GhostJoins;
std::vector<IntersectNode> m_IntersectList; std::vector<IntersectNode, Allocator<IntersectNode>> m_IntersectList;
ClipType m_ClipType; ClipType m_ClipType;
// A priority queue (a binary heap) of Y coordinates. // A priority queue (a binary heap) of Y coordinates.
std::priority_queue<cInt> m_Scanbeam; using cInts = std::vector<cInt, Allocator<cInt>>;
std::priority_queue<cInt, cInts> m_Scanbeam;
// Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal().
std::vector<cInt> m_Maxima; cInts m_Maxima;
TEdge *m_ActiveEdges; TEdge *m_ActiveEdges;
TEdge *m_SortedEdges; TEdge *m_SortedEdges;
PolyFillType m_ClipFillType; PolyFillType m_ClipFillType;
@ -473,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);
@ -489,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);
@ -499,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
@ -530,7 +559,7 @@ private:
Paths m_destPolys; Paths m_destPolys;
Path m_srcPoly; Path m_srcPoly;
Path m_destPoly; Path m_destPoly;
std::vector<DoublePoint> m_normals; std::vector<DoublePoint, Allocator<DoublePoint>> m_normals;
double m_delta, m_sinA, m_sin, m_cos; double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad; double m_miterLim, m_StepsPerRad;
// x: index of the lowest contour in m_polyNodes // x: index of the lowest contour in m_polyNodes
@ -558,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

@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r) target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r)
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)

View File

@ -339,15 +339,15 @@ public:
return {distance, nearest_line_index_out, nearest_point_out}; return {distance, nearest_line_index_out, nearest_point_out};
} }
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, Scalar> &point) const
{ {
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point); auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
return dist; return dist;
} }
std::vector<size_t> all_lines_in_radius(const Vec<2, typename LineType::Scalar> &point, Floating radius) std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius)
{ {
return all_lines_in_radius(this->lines, this->tree, point, radius * radius); return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
} }
template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const

View File

@ -5,7 +5,6 @@
#include <stack> #include <stack>
#include <functional> #include <functional>
#include <unordered_set>
#include <sstream> #include <sstream>
#include <queue> #include <queue>
#include <functional> #include <functional>
@ -181,7 +180,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
} }
else else
{ {
std::vector<Point> discretized = discretize(vd_edge, segments); Points discretized = discretize(vd_edge, segments);
assert(discretized.size() >= 2); assert(discretized.size() >= 2);
if(discretized.size() < 2) if(discretized.size() < 2)
{ {
@ -236,7 +235,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
} }
} }
std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments) Points SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments)
{ {
/*Terminology in this function assumes that the edge moves horizontally from /*Terminology in this function assumes that the edge moves horizontally from
left to right. This is not necessarily the case; the edge can go in any left to right. This is not necessarily the case; the edge can go in any
@ -257,7 +256,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
bool point_right = right_cell->contains_point(); bool point_right = right_cell->contains_point();
if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment
{ {
return std::vector<Point>({ start, end }); return Points({ start, end });
} }
else if (point_left != point_right) //This is a parabolic edge between a point and a line. else if (point_left != point_right) //This is a parabolic edge between a point and a line.
{ {
@ -311,7 +310,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
//Start generating points along the edge. //Start generating points along the edge.
Point a = start; Point a = start;
Point b = end; Point b = end;
std::vector<Point> ret; Points ret;
ret.emplace_back(a); ret.emplace_back(a);
//Introduce an extra edge at the borders of the markings? //Introduce an extra edge at the borders of the markings?
@ -522,9 +521,11 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
return false; return false;
} }
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, using PointMap = SkeletalTrapezoidation::PointMap;
const double fix_angle,
const std::unordered_map<Point, Point, PointHash> &vertex_mapping) inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
const double fix_angle,
const PointMap &vertex_mapping)
{ {
for (STHalfEdgeNode &node : graph.nodes) { for (STHalfEdgeNode &node : graph.nodes) {
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
@ -588,7 +589,7 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi
return VoronoiDiagramStatus::NO_ISSUE_DETECTED; return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
} }
inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try_to_fix_degenerated_voronoi_diagram_by_rotation( inline static std::pair<PointMap, double> try_to_fix_degenerated_voronoi_diagram_by_rotation(
Geometry::VoronoiDiagram &voronoi_diagram, Geometry::VoronoiDiagram &voronoi_diagram,
const Polygons &polys, const Polygons &polys,
Polygons &polys_rotated, Polygons &polys_rotated,
@ -597,7 +598,7 @@ inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try
{ {
const Polygons polys_rotated_original = polys_rotated; const Polygons polys_rotated_original = polys_rotated;
double fixed_by_angle = fix_angles.front(); double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping; PointMap vertex_mapping;
for (const double &fix_angle : fix_angles) { for (const double &fix_angle : fix_angles) {
vertex_mapping.clear(); vertex_mapping.clear();
@ -685,7 +686,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11}; const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11};
double fixed_by_angle = fix_angles.front(); double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping; PointMap vertex_mapping;
// polys_copy is referenced through items stored in the std::vector segments. // polys_copy is referenced through items stored in the std::vector segments.
Polygons polys_copy = polys; Polygons polys_copy = polys;
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) { if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
@ -813,9 +814,11 @@ process_voronoi_diagram:
edge.from->incident_edge = &edge; edge.from->incident_edge = &edge;
} }
using NodeSet = SkeletalTrapezoidation::NodeSet;
void SkeletalTrapezoidation::separatePointyQuadEndNodes() void SkeletalTrapezoidation::separatePointyQuadEndNodes()
{ {
std::unordered_set<node_t*> visited_nodes; NodeSet visited_nodes;
for (edge_t& edge : graph.edges) for (edge_t& edge : graph.edges)
{ {
if (edge.prev) if (edge.prev)
@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c
void SkeletalTrapezoidation::connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions) void SkeletalTrapezoidation::connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions)
{ {
std::unordered_set<edge_t*> unprocessed_quad_starts(graph.edges.size() * 5 / 2); using EdgeSet = ankerl::unordered_dense::set<edge_t*>;
EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2);
for (edge_t& edge : graph.edges) for (edge_t& edge : graph.edges)
{ {
if (!edge.prev) if (!edge.prev)
{ {
unprocessed_quad_starts.insert(&edge); unprocessed_quad_starts.emplace(&edge);
} }
} }
std::unordered_set<edge_t*> passed_odd_edges; EdgeSet passed_odd_edges;
while (!unprocessed_quad_starts.empty()) while (!unprocessed_quad_starts.empty())
{ {

View File

@ -7,8 +7,10 @@
#include <boost/polygon/voronoi.hpp> #include <boost/polygon/voronoi.hpp>
#include <memory> // smart pointers #include <memory> // smart pointers
#include <unordered_map>
#include <utility> // pair #include <utility> // pair
#include <ankerl/unordered_dense.h>
#include <Arachne/utils/VoronoiUtils.hpp> #include <Arachne/utils/VoronoiUtils.hpp>
#include "utils/HalfEdgeGraph.hpp" #include "utils/HalfEdgeGraph.hpp"
@ -80,7 +82,9 @@ class SkeletalTrapezoidation
const BeadingStrategy& beading_strategy; const BeadingStrategy& beading_strategy;
public: public:
using Segment = PolygonsSegmentIndex; using Segment = PolygonsSegmentIndex;
using PointMap = ankerl::unordered_dense::map<Point, Point, PointHash>;
using NodeSet = ankerl::unordered_dense::set<node_t*>;
/*! /*!
* Construct a new trapezoidation problem to solve. * Construct a new trapezoidation problem to solve.
@ -164,8 +168,8 @@ protected:
* mapping each voronoi VD edge to the corresponding halfedge HE edge * mapping each voronoi VD edge to the corresponding halfedge HE edge
* In case the result segment is discretized, we map the VD edge to the *last* HE edge * In case the result segment is discretized, we map the VD edge to the *last* HE edge
*/ */
std::unordered_map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge; ankerl::unordered_dense::map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge;
std::unordered_map<vd_t::vertex_type*, node_t*> vd_node_to_he_node; ankerl::unordered_dense::map<vd_t::vertex_type*, node_t*> vd_node_to_he_node;
node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet. node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
/*! /*!
@ -204,7 +208,7 @@ protected:
* \return A number of coordinates along the edge where the edge is broken * \return A number of coordinates along the edge where the edge is broken
* up into discrete pieces. * up into discrete pieces.
*/ */
std::vector<Point> discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments); Points discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments);
/*! /*!
* Compute the range of line segments that surround a cell of the skeletal * Compute the range of line segments that surround a cell of the skeletal

View File

@ -2,7 +2,8 @@
//CuraEngine is released under the terms of the AGPLv3 or higher. //CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkeletalTrapezoidationGraph.hpp" #include "SkeletalTrapezoidationGraph.hpp"
#include <unordered_map>
#include <ankerl/unordered_dense.h>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
{ {
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator; ankerl::unordered_dense::map<edge_t*, Edges::iterator> edge_locator;
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator; ankerl::unordered_dense::map<node_t*, Nodes::iterator> node_locator;
for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it) for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it)
{ {
@ -193,7 +194,7 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
node_locator.emplace(&*node_it, node_it); node_locator.emplace(&*node_it, node_it);
} }
auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, std::list<edge_t>::iterator& current_edge_it, bool& edge_it_is_updated) auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, Edges::iterator& current_edge_it, bool& edge_it_is_updated)
{ {
if (current_edge_it != edges.end() if (current_edge_it != edges.end()
&& to_be_removed == &*current_edge_it) && to_be_removed == &*current_edge_it)

View File

@ -2,7 +2,6 @@
// CuraEngine is released under the terms of the AGPLv3 or higher. // CuraEngine is released under the terms of the AGPLv3 or higher.
#include <algorithm> //For std::partition_copy and std::min_element. #include <algorithm> //For std::partition_copy and std::min_element.
#include <unordered_set>
#include "WallToolPaths.hpp" #include "WallToolPaths.hpp"
@ -233,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;
} }
@ -274,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
} }
} }
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
} }
/*! /*!
@ -341,7 +340,7 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
} }
} else { } else {
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
std::vector<Polygon> small_holes; Polygons small_holes;
for (auto it = thiss.begin(); it < new_end;) { for (auto it = thiss.begin(); it < new_end;) {
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) { if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
if (area >= 0) { if (area >= 0) {
@ -767,9 +766,9 @@ bool WallToolPaths::removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpa
* *
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/ */
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner) WallToolPaths::ExtrusionLineSet WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner)
{ {
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> order_requirements; ExtrusionLineSet order_requirements;
// We build a grid where we map toolpath vertex locations to toolpaths, // We build a grid where we map toolpath vertex locations to toolpaths,
// so that we can easily find which two toolpaths are next to each other, // so that we can easily find which two toolpaths are next to each other,

View File

@ -5,7 +5,8 @@
#define CURAENGINE_WALLTOOLPATHS_H #define CURAENGINE_WALLTOOLPATHS_H
#include <memory> #include <memory>
#include <unordered_set>
#include <ankerl/unordered_dense.h>
#include "BeadingStrategy/BeadingStrategyFactory.hpp" #include "BeadingStrategy/BeadingStrategyFactory.hpp"
#include "utils/ExtrusionLine.hpp" #include "utils/ExtrusionLine.hpp"
@ -73,6 +74,7 @@ public:
*/ */
static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths); static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths);
using ExtrusionLineSet = ankerl::unordered_dense::set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>>;
/*! /*!
* Get the order constraints of the insets when printing walls per region / hole. * Get the order constraints of the insets when printing walls per region / hole.
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right. * Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
@ -81,7 +83,7 @@ public:
* *
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/ */
static std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner); static ExtrusionLineSet getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
protected: protected:
/*! /*!

View File

@ -21,8 +21,10 @@ class HalfEdgeGraph
public: public:
using edge_t = derived_edge_t; using edge_t = derived_edge_t;
using node_t = derived_node_t; using node_t = derived_node_t;
std::list<edge_t> edges; using Edges = std::list<edge_t>;
std::list<node_t> nodes; using Nodes = std::list<node_t>;
Edges edges;
Nodes nodes;
}; };
} // namespace Slic3r::Arachne } // namespace Slic3r::Arachne

View File

@ -7,7 +7,6 @@
#include "SparsePointGrid.hpp" #include "SparsePointGrid.hpp"
#include "PolygonsPointIndex.hpp" #include "PolygonsPointIndex.hpp"
#include "../../Polygon.hpp" #include "../../Polygon.hpp"
#include <unordered_set>
#include <cassert> #include <cassert>
namespace Slic3r::Arachne namespace Slic3r::Arachne

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_GRID_H #define UTILS_SPARSE_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_LINE_GRID_H #define UTILS_SPARSE_LINE_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_POINT_GRID_H #define UTILS_SPARSE_POINT_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include "SparseGrid.hpp" #include "SparseGrid.hpp"

View File

@ -7,7 +7,6 @@
#include "../../Point.hpp" #include "../../Point.hpp"
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -138,9 +138,9 @@ public:
return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3])); return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3]));
} }
}; };
std::vector<Point> VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle) Points VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
{ {
std::vector<Point> discretized; Points discretized;
// x is distance of point projected on the segment ab // x is distance of point projected on the segment ab
// xx is point projected on the segment ab // xx is point projected on the segment ab
const Point a = segment.from(); const Point a = segment.from();

View File

@ -34,7 +34,7 @@ public:
* Discretize a parabola based on (approximate) step size. * Discretize a parabola based on (approximate) step size.
* The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola.
*/ */
static std::vector<Point> discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); static Points discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle);
static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex)
{ {

View File

@ -6,23 +6,19 @@
namespace Slic3r { namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points); template BoundingBoxBase<Point, Points>::BoundingBoxBase(const Points &points);
template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points); template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points);
template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points); template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
void BoundingBox::polygon(Polygon* polygon) const void BoundingBox::polygon(Polygon* polygon) const
{ {
polygon->points.clear(); polygon->points = {
polygon->points.resize(4); this->min,
polygon->points[0](0) = this->min(0); { this->max.x(), this->min.y() },
polygon->points[0](1) = this->min(1); this->max,
polygon->points[1](0) = this->max(0); { this->min.x(), this->max.y() }
polygon->points[1](1) = this->min(1); };
polygon->points[2](0) = this->max(0);
polygon->points[2](1) = this->max(1);
polygon->points[3](0) = this->min(0);
polygon->points[3](1) = this->max(1);
} }
Polygon BoundingBox::polygon() const Polygon BoundingBox::polygon() const
@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const
BoundingBox out; BoundingBox out;
out.merge(this->min.rotated(angle)); out.merge(this->min.rotated(angle));
out.merge(this->max.rotated(angle)); out.merge(this->max.rotated(angle));
out.merge(Point(this->min(0), this->max(1)).rotated(angle)); out.merge(Point(this->min.x(), this->max.y()).rotated(angle));
out.merge(Point(this->max(0), this->min(1)).rotated(angle)); out.merge(Point(this->max.x(), this->min.y()).rotated(angle));
return out; return out;
} }
@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point &center) const
BoundingBox out; BoundingBox out;
out.merge(this->min.rotated(angle, center)); out.merge(this->min.rotated(angle, center));
out.merge(this->max.rotated(angle, center)); out.merge(this->max.rotated(angle, center));
out.merge(Point(this->min(0), this->max(1)).rotated(angle, center)); out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center));
out.merge(Point(this->max(0), this->min(1)).rotated(angle, center)); out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center));
return out; return out;
} }
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::scale(double factor) BoundingBoxBase<PointType, APointsType>::scale(double factor)
{ {
this->min *= factor; this->min *= factor;
this->max *= factor; this->max *= factor;
} }
template void BoundingBoxBase<Point>::scale(double factor); template void BoundingBoxBase<Point, Points>::scale(double factor);
template void BoundingBoxBase<Vec2d>::scale(double factor); template void BoundingBoxBase<Vec2d>::scale(double factor);
template void BoundingBoxBase<Vec3d>::scale(double factor); template void BoundingBoxBase<Vec3d>::scale(double factor);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const PointClass &point) BoundingBoxBase<PointType, APointsType>::merge(const PointType &point)
{ {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(point); this->min = this->min.cwiseMin(point);
@ -74,22 +70,22 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
this->defined = true; this->defined = true;
} }
} }
template void BoundingBoxBase<Point>::merge(const Point &point); template void BoundingBoxBase<Point, Points>::merge(const Point &point);
template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point); template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point); template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points) BoundingBoxBase<PointType, APointsType>::merge(const PointsType &points)
{ {
this->merge(BoundingBoxBase(points)); this->merge(BoundingBoxBase(points));
} }
template void BoundingBoxBase<Point>::merge(const Points &points); template void BoundingBoxBase<Point, Points>::merge(const Points &points);
template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points); template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb) BoundingBoxBase<PointType, APointsType>::merge(const BoundingBoxBase<PointType, PointsType> &bb)
{ {
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y());
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(bb.min); this->min = this->min.cwiseMin(bb.min);
@ -101,12 +97,12 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
} }
} }
} }
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb); template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb); template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb); template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const PointClass &point) BoundingBox3Base<PointType>::merge(const PointType &point)
{ {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(point); this->min = this->min.cwiseMin(point);
@ -120,17 +116,17 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point); template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point); template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points) BoundingBox3Base<PointType>::merge(const PointsType &points)
{ {
this->merge(BoundingBox3Base(points)); this->merge(BoundingBox3Base(points));
} }
template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points); template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb) BoundingBox3Base<PointType>::merge(const BoundingBox3Base<PointType> &bb)
{ {
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z());
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(bb.min); this->min = this->min.cwiseMin(bb.min);
@ -144,83 +140,78 @@ BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
} }
template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb); template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
template <class PointClass> PointClass template <class PointType, typename APointsType> PointType
BoundingBoxBase<PointClass>::size() const BoundingBoxBase<PointType, APointsType>::size() const
{ {
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); return this->max - this->min;
} }
template Point BoundingBoxBase<Point>::size() const; template Point BoundingBoxBase<Point, Points>::size() const;
template Vec2f BoundingBoxBase<Vec2f>::size() const; template Vec2f BoundingBoxBase<Vec2f>::size() const;
template Vec2d BoundingBoxBase<Vec2d>::size() const; template Vec2d BoundingBoxBase<Vec2d>::size() const;
template <class PointClass> PointClass template <class PointType> PointType
BoundingBox3Base<PointClass>::size() const BoundingBox3Base<PointType>::size() const
{ {
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); return this->max - this->min;
} }
template Vec3f BoundingBox3Base<Vec3f>::size() const; template Vec3f BoundingBox3Base<Vec3f>::size() const;
template Vec3d BoundingBox3Base<Vec3d>::size() const; template Vec3d BoundingBox3Base<Vec3d>::size() const;
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const template <class PointType, typename APointsType> double BoundingBoxBase<PointType, APointsType>::radius() const
{ {
assert(this->defined); assert(this->defined);
double x = this->max(0) - this->min(0); return 0.5 * (this->max - this->min).template cast<double>().norm();
double y = this->max(1) - this->min(1);
return 0.5 * sqrt(x*x+y*y);
} }
template double BoundingBoxBase<Point>::radius() const; template double BoundingBoxBase<Point, Points>::radius() const;
template double BoundingBoxBase<Vec2d>::radius() const; template double BoundingBoxBase<Vec2d>::radius() const;
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const template <class PointType> double BoundingBox3Base<PointType>::radius() const
{ {
double x = this->max(0) - this->min(0); return 0.5 * (this->max - this->min).template cast<double>().norm();
double y = this->max(1) - this->min(1);
double z = this->max(2) - this->min(2);
return 0.5 * sqrt(x*x+y*y+z*z);
} }
template double BoundingBox3Base<Vec3d>::radius() const; template double BoundingBox3Base<Vec3d>::radius() const;
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::offset(coordf_t delta) BoundingBoxBase<PointType, APointsType>::offset(coordf_t delta)
{ {
PointClass v(delta, delta); PointType v(delta, delta);
this->min -= v; this->min -= v;
this->max += v; this->max += v;
} }
template void BoundingBoxBase<Point>::offset(coordf_t delta); template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
template void BoundingBoxBase<Vec2d>::offset(coordf_t delta); template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::offset(coordf_t delta) BoundingBox3Base<PointType>::offset(coordf_t delta)
{ {
PointClass v(delta, delta, delta); PointType v(delta, delta, delta);
this->min -= v; this->min -= v;
this->max += v; this->max += v;
} }
template void BoundingBox3Base<Vec3d>::offset(coordf_t delta); template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
template <class PointClass> PointClass template <class PointType, typename APointsType> PointType
BoundingBoxBase<PointClass>::center() const BoundingBoxBase<PointType, APointsType>::center() const
{ {
return (this->min + this->max) / 2; return (this->min + this->max) / 2;
} }
template Point BoundingBoxBase<Point>::center() const; template Point BoundingBoxBase<Point, Points>::center() const;
template Vec2f BoundingBoxBase<Vec2f>::center() const; template Vec2f BoundingBoxBase<Vec2f>::center() const;
template Vec2d BoundingBoxBase<Vec2d>::center() const; template Vec2d BoundingBoxBase<Vec2d>::center() const;
template <class PointClass> PointClass template <class PointType> PointType
BoundingBox3Base<PointClass>::center() const BoundingBox3Base<PointType>::center() const
{ {
return (this->min + this->max) / 2; return (this->min + this->max) / 2;
} }
template Vec3f BoundingBox3Base<Vec3f>::center() const; template Vec3f BoundingBox3Base<Vec3f>::center() const;
template Vec3d BoundingBox3Base<Vec3d>::center() const; template Vec3d BoundingBox3Base<Vec3d>::center() const;
template <class PointClass> coordf_t template <class PointType> coordf_t
BoundingBox3Base<PointClass>::max_size() const BoundingBox3Base<PointType>::max_size() const
{ {
PointClass s = size(); PointType s = size();
return std::max(s(0), std::max(s(1), s(2))); return std::max(s.x(), std::max(s.y(), s.z()));
} }
template coordf_t BoundingBox3Base<Vec3f>::max_size() const; template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
template coordf_t BoundingBox3Base<Vec3d>::max_size() const; template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
@ -228,8 +219,8 @@ template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
void BoundingBox::align_to_grid(const coord_t cell_size) void BoundingBox::align_to_grid(const coord_t cell_size)
{ {
if (this->defined) { if (this->defined) {
min(0) = Slic3r::align_to_grid(min(0), cell_size); min.x() = Slic3r::align_to_grid(min.x(), cell_size);
min(1) = Slic3r::align_to_grid(min(1), cell_size); min.y() = Slic3r::align_to_grid(min.y(), cell_size);
} }
} }
@ -238,14 +229,14 @@ BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const
typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices; typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices;
Vertices src_vertices; Vertices src_vertices;
src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2); src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z();
src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2); src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z();
src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2); src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z();
src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2); src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z();
src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2); src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z();
src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2); src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z();
src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2); src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z();
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2); src_vertices(0, 7) = min.x(); src_vertices(1, 7) = max.y(); src_vertices(2, 7) = max.z();
Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous(); Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous();

View File

@ -8,53 +8,54 @@
namespace Slic3r { namespace Slic3r {
template <class PointClass> template <typename PointType, typename APointsType = std::vector<PointType>>
class BoundingBoxBase class BoundingBoxBase
{ {
public: public:
PointClass min; using PointsType = APointsType;
PointClass max; PointType min;
PointType max;
bool defined; bool defined;
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {} BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {}
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(const PointType &pmin, const PointType &pmax) :
min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {} min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {}
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase(const PointType &p1, const PointType &p2, const PointType &p3) :
min(p1), max(p1), defined(false) { merge(p2); merge(p3); } min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It>> template<class It, class = IteratorOnly<It>>
BoundingBoxBase(It from, It to) BoundingBoxBase(It from, It to)
{ construct(*this, from, to); } { construct(*this, from, to); }
BoundingBoxBase(const std::vector<PointClass> &points) BoundingBoxBase(const PointsType &points)
: BoundingBoxBase(points.begin(), points.end()) : BoundingBoxBase(points.begin(), points.end())
{} {}
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); } void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); }
void merge(const PointClass &point); void merge(const PointType &point);
void merge(const std::vector<PointClass> &points); void merge(const PointsType &points);
void merge(const BoundingBoxBase<PointClass> &bb); void merge(const BoundingBoxBase<PointType, PointsType> &bb);
void scale(double factor); void scale(double factor);
PointClass size() const; PointType size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y) { assert(this->defined); PointType v(x, y); this->min += v; this->max += v; }
void translate(const PointClass &v) { this->min += v; this->max += v; } void translate(const PointType &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; } BoundingBoxBase<PointType, PointsType> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointType, PointsType> out(*this); out.offset(delta); return out; }
PointClass center() const; PointType center() const;
bool contains(const PointClass &point) const { bool contains(const PointType &point) const {
return point.x() >= this->min.x() && point.x() <= this->max.x() return point.x() >= this->min.x() && point.x() <= this->max.x()
&& point.y() >= this->min.y() && point.y() <= this->max.y(); && point.y() >= this->min.y() && point.y() <= this->max.y();
} }
bool contains(const BoundingBoxBase<PointClass> &other) const { bool contains(const BoundingBoxBase<PointType, PointsType> &other) const {
return contains(other.min) && contains(other.max); return contains(other.min) && contains(other.max);
} }
bool overlap(const BoundingBoxBase<PointClass> &other) const { bool overlap(const BoundingBoxBase<PointType, PointsType> &other) const {
return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() ||
this->max.y() < other.min.y() || this->min.y() > other.max.y()); this->max.y() < other.min.y() || this->min.y() > other.max.y());
} }
bool operator==(const BoundingBoxBase<PointClass> &rhs) { return this->min == rhs.min && this->max == rhs.max; } bool operator==(const BoundingBoxBase<PointType, PointsType> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
bool operator!=(const BoundingBoxBase<PointClass> &rhs) { return ! (*this == rhs); } bool operator!=(const BoundingBoxBase<PointType, PointsType> &rhs) { return ! (*this == rhs); }
private: private:
// to access construct() // to access construct()
@ -69,10 +70,10 @@ private:
{ {
if (from != to) { if (from != to) {
auto it = from; auto it = from;
out.min = it->template cast<typename PointClass::Scalar>(); out.min = it->template cast<typename PointType::Scalar>();
out.max = out.min; out.max = out.min;
for (++ it; it != to; ++ it) { for (++ it; it != to; ++ it) {
auto vec = it->template cast<typename PointClass::Scalar>(); auto vec = it->template cast<typename PointType::Scalar>();
out.min = out.min.cwiseMin(vec); out.min = out.min.cwiseMin(vec);
out.max = out.max.cwiseMax(vec); out.max = out.max.cwiseMax(vec);
} }
@ -81,16 +82,18 @@ private:
} }
}; };
template <class PointClass> template <class PointType>
class BoundingBox3Base : public BoundingBoxBase<PointClass> class BoundingBox3Base : public BoundingBoxBase<PointType, std::vector<PointType>>
{ {
public: public:
BoundingBox3Base() : BoundingBoxBase<PointClass>() {} using PointsType = std::vector<PointType>;
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
BoundingBoxBase<PointClass>(pmin, pmax) BoundingBox3Base() : BoundingBoxBase<PointType>() {}
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointClass>::defined = false; } BoundingBox3Base(const PointType &pmin, const PointType &pmax) :
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase<PointType>(pmin, pmax)
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); } { if (pmin.z() >= pmax.z()) BoundingBoxBase<PointType>::defined = false; }
BoundingBox3Base(const PointType &p1, const PointType &p2, const PointType &p3) :
BoundingBoxBase<PointType>(p1, p1) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to) template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to)
{ {
@ -98,67 +101,67 @@ public:
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
auto it = from; auto it = from;
this->min = it->template cast<typename PointClass::Scalar>(); this->min = it->template cast<typename PointType::Scalar>();
this->max = this->min; this->max = this->min;
for (++ it; it != to; ++ it) { for (++ it; it != to; ++ it) {
auto vec = it->template cast<typename PointClass::Scalar>(); auto vec = it->template cast<typename PointType::Scalar>();
this->min = this->min.cwiseMin(vec); this->min = this->min.cwiseMin(vec);
this->max = this->max.cwiseMax(vec); this->max = this->max.cwiseMax(vec);
} }
this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z()); this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z());
} }
BoundingBox3Base(const std::vector<PointClass> &points) BoundingBox3Base(const PointsType &points)
: BoundingBox3Base(points.begin(), points.end()) : BoundingBox3Base(points.begin(), points.end())
{} {}
void merge(const PointClass &point); void merge(const PointType &point);
void merge(const std::vector<PointClass> &points); void merge(const PointsType &points);
void merge(const BoundingBox3Base<PointClass> &bb); void merge(const BoundingBox3Base<PointType> &bb);
PointClass size() const; PointType size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointType v(x, y, z); this->min += v; this->max += v; }
void translate(const Vec3d &v) { this->min += v; this->max += v; } void translate(const Vec3d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; } BoundingBox3Base<PointType> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointType> out(*this); out.offset(delta); return out; }
PointClass center() const; PointType center() const;
coordf_t max_size() const; coordf_t max_size() const;
bool contains(const PointClass &point) const { bool contains(const PointType &point) const {
return BoundingBoxBase<PointClass>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); return BoundingBoxBase<PointType>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
} }
bool contains(const BoundingBox3Base<PointClass>& other) const { bool contains(const BoundingBox3Base<PointType>& other) const {
return contains(other.min) && contains(other.max); return contains(other.min) && contains(other.max);
} }
// Intersects without boundaries. // Intersects without boundaries.
bool intersects(const BoundingBox3Base<PointClass>& other) const { bool intersects(const BoundingBox3Base<PointType>& other) const {
return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() && return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() &&
this->min.z() < other.max.z() && this->max.z() > other.min.z(); this->min.z() < other.max.z() && this->max.z() > other.min.z();
} }
}; };
// Will prevent warnings caused by non existing definition of template in hpp // Will prevent warnings caused by non existing definition of template in hpp
extern template void BoundingBoxBase<Point>::scale(double factor); extern template void BoundingBoxBase<Point, Points>::scale(double factor);
extern template void BoundingBoxBase<Vec2d>::scale(double factor); extern template void BoundingBoxBase<Vec2d>::scale(double factor);
extern template void BoundingBoxBase<Vec3d>::scale(double factor); extern template void BoundingBoxBase<Vec3d>::scale(double factor);
extern template void BoundingBoxBase<Point>::offset(coordf_t delta); extern template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta); extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
extern template void BoundingBoxBase<Point>::merge(const Point &point); extern template void BoundingBoxBase<Point, Points>::merge(const Point &point);
extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point); extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point); extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
extern template void BoundingBoxBase<Point>::merge(const Points &points); extern template void BoundingBoxBase<Point, Points>::merge(const Points &points);
extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points); extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
extern template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb); extern template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb); extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb); extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
extern template Point BoundingBoxBase<Point>::size() const; extern template Point BoundingBoxBase<Point, Points>::size() const;
extern template Vec2f BoundingBoxBase<Vec2f>::size() const; extern template Vec2f BoundingBoxBase<Vec2f>::size() const;
extern template Vec2d BoundingBoxBase<Vec2d>::size() const; extern template Vec2d BoundingBoxBase<Vec2d>::size() const;
extern template double BoundingBoxBase<Point>::radius() const; extern template double BoundingBoxBase<Point, Points>::radius() const;
extern template double BoundingBoxBase<Vec2d>::radius() const; extern template double BoundingBoxBase<Vec2d>::radius() const;
extern template Point BoundingBoxBase<Point>::center() const; extern template Point BoundingBoxBase<Point, Points>::center() const;
extern template Vec2f BoundingBoxBase<Vec2f>::center() const; extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
extern template Vec2d BoundingBoxBase<Vec2d>::center() const; extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point); extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
@ -174,7 +177,7 @@ extern template Vec3d BoundingBox3Base<Vec3d>::center() const;
extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const; extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const; extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
class BoundingBox : public BoundingBoxBase<Point> class BoundingBox : public BoundingBoxBase<Point, Points>
{ {
public: public:
void polygon(Polygon* polygon) const; void polygon(Polygon* polygon) const;
@ -187,9 +190,9 @@ public:
// to encompass the original bounding box. // to encompass the original bounding box.
void align_to_grid(const coord_t cell_size); void align_to_grid(const coord_t cell_size);
BoundingBox() : BoundingBoxBase<Point>() {} BoundingBox() : BoundingBoxBase<Point, Points>() {}
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {} BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {} BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; } BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
@ -222,14 +225,14 @@ public:
BoundingBoxf3 transformed(const Transform3d& matrix) const; BoundingBoxf3 transformed(const Transform3d& matrix) const;
}; };
template<typename VT> template<typename PointType, typename PointsType>
inline bool empty(const BoundingBoxBase<VT> &bb) inline bool empty(const BoundingBoxBase<PointType, PointsType> &bb)
{ {
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y(); return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y();
} }
template<typename VT> template<typename PointType>
inline bool empty(const BoundingBox3Base<VT> &bb) inline bool empty(const BoundingBox3Base<PointType> &bb)
{ {
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z(); return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z();
} }

View File

@ -276,10 +276,21 @@ set(SLIC3R_SOURCES
SlicingAdaptive.hpp SlicingAdaptive.hpp
Subdivide.cpp Subdivide.cpp
Subdivide.hpp Subdivide.hpp
Support/SupportCommon.cpp
Support/SupportCommon.hpp
Support/SupportDebug.cpp
Support/SupportDebug.hpp
Support/SupportLayer.hpp
Support/SupportMaterial.cpp
Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportParameters.hpp
Support/TreeSupport.cpp
Support/TreeSupport.hpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp
SupportSpotsGenerator.cpp SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp SupportSpotsGenerator.hpp
SupportMaterial.cpp
SupportMaterial.hpp
Surface.cpp Surface.cpp
Surface.hpp Surface.hpp
SurfaceCollection.cpp SurfaceCollection.cpp
@ -291,10 +302,6 @@ set(SLIC3R_SOURCES
Tesselate.cpp Tesselate.cpp
Tesselate.hpp Tesselate.hpp
TextConfiguration.hpp TextConfiguration.hpp
TreeSupport.cpp
TreeSupport.hpp
TreeModelVolumes.cpp
TreeModelVolumes.hpp
TriangleMesh.cpp TriangleMesh.cpp
TriangleMesh.hpp TriangleMesh.hpp
TriangleMeshSlicer.cpp TriangleMeshSlicer.cpp
@ -485,6 +492,7 @@ target_link_libraries(libslic3r
qhull qhull
semver semver
TBB::tbb TBB::tbb
TBB::tbbmalloc
libslic3r_cgal libslic3r_cgal
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
PNG::PNG PNG::PNG

View File

@ -3,6 +3,20 @@
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Utils.hpp" #include "Utils.hpp"
// #define CLIPPER_UTILS_TIMING
#ifdef CLIPPER_UTILS_TIMING
// time limit for one ClipperLib operation (union / diff / offset), in ms
#define CLIPPER_UTILS_TIME_LIMIT_DEFAULT 50
#include <boost/current_function.hpp>
#include "Timer.hpp"
#define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION)
#define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION)
#else
#define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) do {} while(false)
#define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) do {} while(false)
#endif // CLIPPER_UTILS_TIMING
// #define CLIPPER_UTILS_DEBUG // #define CLIPPER_UTILS_DEBUG
#ifdef CLIPPER_UTILS_DEBUG #ifdef CLIPPER_UTILS_DEBUG
@ -51,9 +65,11 @@ namespace ClipperUtils {
// Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon.
// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one
// with a set of polygons covering the whole layer below. // with a set of polygons covering the whole layer below.
template<typename PointType> template<typename PointsType>
inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox, std::vector<PointType> &out) inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out)
{ {
using PointType = typename PointsType::value_type;
out.clear(); out.clear();
const size_t cnt = src.size(); const size_t cnt = src.size();
if (cnt < 3) if (cnt < 3)
@ -108,10 +124,10 @@ namespace ClipperUtils {
void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out)
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
template<typename PointType> template<typename PointsType>
[[nodiscard]] std::vector<PointType> clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox) [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox)
{ {
std::vector<PointType> out; PointsType out;
clip_clipper_polygon_with_subject_bbox(src, bbox, out); clip_clipper_polygon_with_subject_bbox(src, bbox, out);
return out; return out;
} }
@ -258,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon> template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon>
static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
ClipperLib::Paths out; ClipperLib::Paths out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -299,6 +317,8 @@ TResult clipper_do(
TClip && clip, TClip && clip,
const ClipperLib::PolyFillType fillType) const ClipperLib::PolyFillType fillType)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
clipper.AddPaths(std::forward<TClip>(clip), ClipperLib::ptClip, true); clipper.AddPaths(std::forward<TClip>(clip), ClipperLib::ptClip, true);
@ -328,6 +348,8 @@ TResult clipper_union(
// fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons // fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons
const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero) const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
TResult retval; TResult retval;
@ -366,6 +388,8 @@ template<> void remove_outermost_polygon<ClipperLib::PolyTree>(ClipperLib::PolyT
template<class TResult, typename PathsProvider> template<class TResult, typename PathsProvider>
static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
assert(offset > 0); assert(offset > 0);
TResult out; TResult out;
if (auto raw = raw_offset(std::forward<PathsProvider>(paths), - offset, joinType, miterLimit); ! raw.empty()) { if (auto raw = raw_offset(std::forward<PathsProvider>(paths), - offset, joinType, miterLimit); ! raw.empty()) {
@ -405,6 +429,8 @@ Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, C
// returns number of expolygons collected (0 or 1). // returns number of expolygons collected (0 or 1).
static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out) static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// 1) Offset the outer contour. // 1) Offset the outer contour.
ClipperLib::Paths contours; ClipperLib::Paths contours;
{ {
@ -613,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree(
PathProvider2 &&clip, PathProvider2 &&clip,
const ClipperLib::PolyFillType fillType) const ClipperLib::PolyFillType fillType)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// Perform the operation with the output to input_subject. // Perform the operation with the output to input_subject.
// This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library // This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library
// if there are overapping edges. // if there are overapping edges.
@ -657,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); } { 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) 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); } { 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) 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); } { 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) Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
@ -751,6 +781,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject)
template<typename PathsProvider1, typename PathsProvider2> template<typename PathsProvider1, typename PathsProvider2>
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip) Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false); clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false);
clipper.AddPaths(std::forward<PathsProvider2>(clip), ClipperLib::ptClip, true); clipper.AddPaths(std::forward<PathsProvider2>(clip), ClipperLib::ptClip, true);
@ -934,18 +966,17 @@ 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);
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));
@ -953,12 +984,12 @@ Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
{ {
if (! preserve_collinear) CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
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);
@ -969,6 +1000,8 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
Polygons top_level_islands(const Slic3r::Polygons &polygons) Polygons top_level_islands(const Slic3r::Polygons &polygons)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// init Clipper // init Clipper
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.Clear(); clipper.Clear();
@ -992,6 +1025,8 @@ ClipperLib::Paths fix_after_outer_offset(
ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive
bool reverse_result) // = false bool reverse_result) // = false
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths solution; ClipperLib::Paths solution;
if (! input.empty()) { if (! input.empty()) {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
@ -1010,6 +1045,8 @@ ClipperLib::Paths fix_after_inner_offset(
ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative
bool reverse_result) // = true bool reverse_result) // = true
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths solution; ClipperLib::Paths solution;
if (! input.empty()) { if (! input.empty()) {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
@ -1030,6 +1067,8 @@ ClipperLib::Paths fix_after_inner_offset(
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit) ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
assert(contour.size() == deltas.size()); assert(contour.size() == deltas.size());
#ifndef NDEBUG #ifndef NDEBUG
@ -1170,6 +1209,8 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
#ifndef NDEBUG #ifndef NDEBUG
// Verify that the deltas are all non positive. // Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas) for (const std::vector<float> &ds : deltas)
@ -1203,6 +1244,8 @@ static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths contours, holes; ClipperLib::Paths contours, holes;
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
@ -1225,6 +1268,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths contours, holes; ClipperLib::Paths contours, holes;
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
@ -1251,6 +1296,8 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
#ifndef NDEBUG #ifndef NDEBUG
// Verify that the deltas are all non positive. // Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas) for (const std::vector<float> &ds : deltas)
@ -1286,6 +1333,8 @@ static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths contours, holes; ClipperLib::Paths contours, holes;
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
@ -1307,6 +1356,8 @@ Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::v
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths contours, holes; ClipperLib::Paths contours, holes;
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);

View File

@ -133,21 +133,21 @@ namespace ClipperUtils {
const std::vector<PathType> &m_paths; const std::vector<PathType> &m_paths;
}; };
template<typename MultiPointType> template<typename MultiPointsType>
class MultiPointsProvider { class MultiPointsProvider {
public: public:
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {} MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {}
struct iterator : public PathsProviderIteratorBase { struct iterator : public PathsProviderIteratorBase {
public: public:
explicit iterator(typename std::vector<MultiPointType>::const_iterator it) : m_it(it) {} explicit iterator(typename MultiPointsType::const_iterator it) : m_it(it) {}
const Points& operator*() const { return m_it->points; } const Points& operator*() const { return m_it->points; }
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; } bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); } bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
const Points& operator++(int) { return (m_it ++)->points; } const Points& operator++(int) { return (m_it ++)->points; }
iterator& operator++() { ++ m_it; return *this; } iterator& operator++() { ++ m_it; return *this; }
private: private:
typename std::vector<MultiPointType>::const_iterator m_it; typename MultiPointsType::const_iterator m_it;
}; };
iterator cbegin() const { return iterator(m_multipoints.begin()); } iterator cbegin() const { return iterator(m_multipoints.begin()); }
@ -157,11 +157,11 @@ namespace ClipperUtils {
size_t size() const { return m_multipoints.size(); } size_t size() const { return m_multipoints.size(); }
private: private:
const std::vector<MultiPointType> &m_multipoints; const MultiPointsType &m_multipoints;
}; };
using PolygonsProvider = MultiPointsProvider<Polygon>; using PolygonsProvider = MultiPointsProvider<Polygons>;
using PolylinesProvider = MultiPointsProvider<Polyline>; using PolylinesProvider = MultiPointsProvider<Polylines>;
struct ExPolygonProvider { struct ExPolygonProvider {
ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {} ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {}
@ -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::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::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); 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::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::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); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@ -596,8 +599,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);

View File

@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
// Convert multiple paths to paths with a given Z coordinate. // Convert multiple paths to paths with a given Z coordinate.
// If Open, then duplicate the first point of each path at its end. // If Open, then duplicate the first point of each path at its end.
template<bool Open = false> template<bool Open = false>
inline ZPaths to_zpaths(const std::vector<Points> &paths, coord_t z) inline ZPaths to_zpaths(const VecOfPoints &paths, coord_t z)
{ {
ZPaths out; ZPaths out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -86,16 +86,16 @@ inline Points from_zpath(const ZPoints &path)
// Convert multiple paths to paths with a given Z coordinate. // Convert multiple paths to paths with a given Z coordinate.
// If Open, then duplicate the first point of each path at its end. // If Open, then duplicate the first point of each path at its end.
template<bool Open = false> template<bool Open = false>
inline void from_zpaths(const ZPaths &paths, std::vector<Points> &out) inline void from_zpaths(const ZPaths &paths, VecOfPoints &out)
{ {
out.reserve(out.size() + paths.size()); out.reserve(out.size() + paths.size());
for (const ZPoints &path : paths) for (const ZPoints &path : paths)
out.emplace_back(from_zpath<Open>(path)); out.emplace_back(from_zpath<Open>(path));
} }
template<bool Open = false> template<bool Open = false>
inline std::vector<Points> from_zpaths(const ZPaths &paths) inline VecOfPoints from_zpaths(const ZPaths &paths)
{ {
std::vector<Points> out; VecOfPoints out;
from_zpaths(paths, out); from_zpaths(paths, out);
return out; return out;
} }

View File

@ -1991,7 +1991,7 @@ public:
void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) { void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) {
this->enum_def_new(); this->enum_def_new();
assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open); assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_close);
this->gui_type = gui_type; this->gui_type = gui_type;
enum_def->set_labels(il); enum_def->set_labels(il);
} }

View File

@ -17,7 +17,7 @@ public:
Contour() = default; Contour() = default;
Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {} Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {}
Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {} Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {}
Contour(const std::vector<Slic3r::Point> &pts, bool open) : Contour(pts.data(), pts.size(), open) {} Contour(const Points &pts, bool open) : Contour(pts.data(), pts.size(), open) {}
const Slic3r::Point *begin() const { return m_begin; } const Slic3r::Point *begin() const { return m_begin; }
const Slic3r::Point *end() const { return m_end; } const Slic3r::Point *end() const { return m_end; }

View File

@ -184,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const
{ {
Polygon p = this->contour; Polygon p = this->contour;
p.points.push_back(p.points.front()); 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(); p.points.pop_back();
pp.emplace_back(std::move(p)); pp.emplace_back(std::move(p));
} }
// holes // holes
for (Polygon p : this->holes) { for (Polygon p : this->holes) {
p.points.push_back(p.points.front()); 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(); p.points.pop_back();
pp.emplace_back(std::move(p)); pp.emplace_back(std::move(p));
} }
@ -397,7 +397,7 @@ bool has_duplicate_points(const ExPolygon &expoly)
size_t cnt = expoly.contour.points.size(); size_t cnt = expoly.contour.points.size();
for (const Polygon &hole : expoly.holes) for (const Polygon &hole : expoly.holes)
cnt += hole.points.size(); cnt += hole.points.size();
std::vector<Point> allpts; Points allpts;
allpts.reserve(cnt); allpts.reserve(cnt);
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
for (const Polygon &hole : expoly.holes) for (const Polygon &hole : expoly.holes)
@ -420,7 +420,7 @@ bool has_duplicate_points(const ExPolygons &expolys)
// Check globally. // Check globally.
#if 0 #if 0
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
std::vector<Point> allpts; Points allpts;
allpts.reserve(count_points(expolys)); allpts.reserve(count_points(expolys));
for (const ExPolygon &expoly : expolys) { for (const ExPolygon &expoly : expolys) {
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());

View File

@ -176,7 +176,7 @@ Flow Flow::with_cross_section(float area_new) const
return this->with_width(width_new); return this->with_width(width_new);
} else { } else {
// Create a rounded extrusion. // Create a rounded extrusion.
auto dmr = float(sqrt(area_new / M_PI)); auto dmr = 2.0 * float(sqrt(area_new / M_PI));
return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false); return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false);
} }
} else } else

View File

@ -3036,7 +3036,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm);
} }
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
m_writer.extruder()->id(), external_perim_reference_speed, m_writer.extruder()->id(), external_perim_reference_speed,
speed); speed);
variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(),

View File

@ -13,6 +13,7 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Flow.hpp" #include "../Flow.hpp"
#include "../Config.hpp" #include "../Config.hpp"
#include "../Line.hpp"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -20,107 +21,32 @@
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <numeric> #include <numeric>
#include <ostream>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace Slic3r { namespace Slic3r {
class SlidingWindowCurvatureAccumulator
{
float window_size;
float total_distance = 0; // accumulated distance
float total_curvature = 0; // accumulated signed ccw angles
deque<float> distances;
deque<float> angles;
public:
SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {}
void add_point(float distance, float angle)
{
total_distance += distance;
total_curvature += angle;
distances.push_back(distance);
angles.push_back(angle);
while (distances.size() > 1 && total_distance > window_size) {
total_distance -= distances.front();
total_curvature -= angles.front();
distances.pop_front();
angles.pop_front();
}
}
float get_curvature() const
{
return total_curvature / window_size;
}
void reset()
{
total_curvature = 0;
total_distance = 0;
distances.clear();
angles.clear();
}
};
class CurvatureEstimator
{
static const size_t sliders_count = 3;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.0},{4.0}, {10.0}};
public:
void add_point(float distance, float angle)
{
if (distance < EPSILON)
return;
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.add_point(distance, angle);
}
}
float get_curvature()
{
float max_curvature = 0.0f;
for (const SlidingWindowCurvatureAccumulator &slider : sliders) {
if (abs(slider.get_curvature()) > abs(max_curvature)) {
max_curvature = slider.get_curvature();
}
}
return max_curvature;
}
void reset()
{
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.reset();
}
}
};
struct ExtendedPoint struct ExtendedPoint
{ {
ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0)
: position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature)
{}
Vec2d position; Vec2d position;
float distance; float distance;
size_t nearest_prev_layer_line;
float curvature; float curvature;
}; };
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L> template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename POINTS, typename L>
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points, std::vector<ExtendedPoint> estimate_points_properties(const POINTS &input_points,
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer, const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
float flow_width, float flow_width,
float max_line_length = -1.0f) float max_line_length = -1.0f)
{ {
using P = typename POINTS::value_type;
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar; using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
if (input_points.empty()) if (input_points.empty())
return {}; return {};
float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
CurvatureEstimator cestim;
auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); }; auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
std::vector<ExtendedPoint> points; std::vector<ExtendedPoint> points;
@ -130,21 +56,22 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
ExtendedPoint start_point{maybe_unscale(input_points.front())}; ExtendedPoint start_point{maybe_unscale(input_points.front())};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>()); auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>());
start_point.distance = distance + boundary_offset; start_point.distance = distance + boundary_offset;
start_point.nearest_prev_layer_line = nearest_line;
points.push_back(start_point); points.push_back(start_point);
} }
for (size_t i = 1; i < input_points.size(); i++) { for (size_t i = 1; i < input_points.size(); i++) {
ExtendedPoint next_point{maybe_unscale(input_points[i])}; ExtendedPoint next_point{maybe_unscale(input_points[i])};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>()); auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
next_point.distance = distance + boundary_offset; next_point.distance = distance + boundary_offset;
next_point.nearest_prev_layer_line = nearest_line;
if (ADD_INTERSECTIONS && if (ADD_INTERSECTIONS &&
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
const ExtendedPoint &prev_point = points.back(); const ExtendedPoint &prev_point = points.back();
auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()}); auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()});
for (const auto &intersection : intersections) { for (const auto &intersection : intersections) {
points.emplace_back(intersection.first.template cast<double>(), boundary_offset, intersection.second); ExtendedPoint p{};
p.position = intersection.first.template cast<double>();
p.distance = boundary_offset;
points.push_back(p);
} }
} }
points.push_back(next_point); points.push_back(next_point);
@ -170,12 +97,18 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
if (t0 < 1.0) { if (t0 < 1.0) {
auto p0 = curr.position + t0 * (next.position - curr.position); auto p0 = curr.position + t0 * (next.position - curr.position);
auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>()); auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l}); ExtendedPoint new_p{};
new_p.position = p0;
new_p.distance = float(p0_dist + boundary_offset);
new_points.push_back(new_p);
} }
if (t1 > 0.0) { if (t1 > 0.0) {
auto p1 = curr.position + t1 * (next.position - curr.position); auto p1 = curr.position + t1 * (next.position - curr.position);
auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>()); auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l}); ExtendedPoint new_p{};
new_p.position = p1;
new_p.distance = float(p1_dist + boundary_offset);
new_points.push_back(new_p);
} }
} }
} }
@ -199,7 +132,10 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t);
auto [p_dist, p_near_l, auto [p_dist, p_near_l,
p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.cast<AABBScalar>()); p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); ExtendedPoint new_p{};
new_p.position = pos;
new_p.distance = float(p_dist + boundary_offset);
new_points.push_back(new_p);
} }
} }
new_points.push_back(points.back()); new_points.push_back(points.back());
@ -207,6 +143,9 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
points = new_points; points = new_points;
} }
std::vector<float> angles_for_curvature(points.size());
std::vector<float> distances_for_curvature(points.size());
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
ExtendedPoint &a = points[point_idx]; ExtendedPoint &a = points[point_idx];
ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx]; ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
@ -214,22 +153,59 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
int prev_point_idx = point_idx; int prev_point_idx = point_idx;
while (prev_point_idx > 0) { while (prev_point_idx > 0) {
prev_point_idx--; prev_point_idx--;
if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; } if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) {
break;
}
} }
int next_point_index = point_idx; int next_point_index = point_idx;
while (next_point_index < int(points.size()) - 1) { while (next_point_index < int(points.size()) - 1) {
next_point_index++; next_point_index++;
if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; } if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) {
break;
}
} }
distances_for_curvature[point_idx] = (prev.position - a.position).norm();
if (prev_point_idx != point_idx && next_point_index != point_idx) { if (prev_point_idx != point_idx && next_point_index != point_idx) {
float distance = (prev.position - a.position).norm(); float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); angles_for_curvature[point_idx] = alfa;
cestim.add_point(distance, alfa); } // else keep zero
} }
a.curvature = cestim.get_curvature(); for (float window_size : {3.0f, 9.0f, 16.0f}) {
size_t tail_point = 0;
float tail_window_acc = 0;
float tail_angle_acc = 0;
size_t head_point = 0;
float head_window_acc = 0;
float head_angle_acc = 0;
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
if (point_idx > 0) {
tail_window_acc += distances_for_curvature[point_idx - 1];
tail_angle_acc += angles_for_curvature[point_idx - 1];
head_window_acc -= distances_for_curvature[point_idx - 1];
head_angle_acc -= angles_for_curvature[point_idx - 1];
}
while (tail_window_acc > window_size * 0.5 && tail_point < point_idx) {
tail_window_acc -= distances_for_curvature[tail_point];
tail_angle_acc -= angles_for_curvature[tail_point];
tail_point++;
}
while (head_window_acc < window_size * 0.5 && head_point < int(points.size()) - 1) {
head_window_acc += distances_for_curvature[head_point];
head_angle_acc += angles_for_curvature[head_point];
head_point++;
}
float curvature = (tail_angle_acc + head_angle_acc) / (tail_window_acc + head_window_acc);
if (std::abs(curvature) > std::abs(points[point_idx].curvature)) {
points[point_idx].curvature = curvature;
}
}
} }
return points; return points;
@ -246,6 +222,8 @@ class ExtrusionQualityEstimator
{ {
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries; std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries; std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> prev_curled_extrusions;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> next_curled_extrusions;
const PrintObject *current_object; const PrintObject *current_object;
public: public:
@ -253,18 +231,22 @@ public:
void prepare_for_new_layer(const Layer *layer) void prepare_for_new_layer(const Layer *layer)
{ {
if (layer == nullptr) return; if (layer == nullptr)
const PrintObject *object = layer->object(); return;
prev_layer_boundaries[object] = next_layer_boundaries[object]; const PrintObject *object = layer->object();
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)}; prev_layer_boundaries[object] = next_layer_boundaries[object];
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
prev_curled_extrusions[object] = next_curled_extrusions[object];
next_curled_extrusions[object] = AABBTreeLines::LinesDistancer<CurledLine>{layer->curled_lines};
} }
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path, std::vector<ProcessedPoint> estimate_speed_from_extrusion_quality(
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds, const ExtrusionPath &path,
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds, const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
size_t extruder_id, const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
float ext_perimeter_speed, size_t extruder_id,
float original_speed) float ext_perimeter_speed,
float original_speed)
{ {
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
std::map<float, float> speed_sections; std::map<float, float> speed_sections;
@ -292,6 +274,22 @@ public:
const ExtendedPoint &curr = extended_points[i]; const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
float artificial_distance_to_curled_lines = 0.0;
{
Vec2d middle = 0.5 * (curr.position + next.position);
auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle),
scale_(10.0 * path.width));
for (size_t idx : line_indices) {
const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx);
float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
float dist = path.width * (1.0 - (distance_from_curled / (10.0 * path.width))) *
(1.0 - (distance_from_curled / (10.0 * path.width))) *
(line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist);
}
}
auto interpolate_speed = [](const std::map<float, float> &values, float distance) { auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
auto upper_dist = values.lower_bound(distance); auto upper_dist = values.lower_bound(distance);
if (upper_dist == values.end()) { if (upper_dist == values.end()) {
@ -306,12 +304,14 @@ public:
return (1.0f - t) * lower_dist->second + t * upper_dist->second; return (1.0f - t) * lower_dist->second + t * upper_dist->second;
}; };
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
interpolate_speed(speed_sections, next.distance)); interpolate_speed(speed_sections, next.distance));
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines);
interpolate_speed(fan_speed_sections, next.distance)); float final_speed = std::min(curled_base_speed, extrusion_speed);
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
interpolate_speed(fan_speed_sections, next.distance));
processed_points.push_back({scaled(curr.position), extrusion_speed, int(fan_speed)}); processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)});
} }
return processed_points; return processed_points;
} }

View File

@ -3,6 +3,7 @@
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/format.hpp" #include "libslic3r/format.hpp"
#include "libslic3r/I18N.hpp"
#include "libslic3r/GCodeWriter.hpp" #include "libslic3r/GCodeWriter.hpp"
#include "GCodeProcessor.hpp" #include "GCodeProcessor.hpp"
@ -441,9 +442,7 @@ void GCodeProcessorResult::reset() {
max_print_height = 0.0f; max_print_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
backtrace_enabled = false; backtrace_enabled = false;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@ -461,9 +460,7 @@ void GCodeProcessorResult::reset() {
max_print_height = 0.0f; max_print_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
backtrace_enabled = false; backtrace_enabled = false;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@ -557,9 +554,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_producer = EProducer::PrusaSlicer; m_producer = EProducer::PrusaSlicer;
m_flavor = config.gcode_flavor; m_flavor = config.gcode_flavor;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
m_result.backtrace_enabled = is_XL_printer(config); m_result.backtrace_enabled = is_XL_printer(config);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
size_t extruders_count = config.nozzle_diameter.values.size(); size_t extruders_count = config.nozzle_diameter.values.size();
m_result.extruders_count = extruders_count; m_result.extruders_count = extruders_count;
@ -3476,7 +3471,6 @@ void GCodeProcessor::post_process()
last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time); last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// Helper class to modify and export gcode to file // Helper class to modify and export gcode to file
class ExportLines class ExportLines
{ {
@ -3711,26 +3705,15 @@ void GCodeProcessor::post_process()
}; };
ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]);
#else
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// replace placeholder lines with the proper final value // replace placeholder lines with the proper final value
// gcode_line is in/out parameter, to reduce expensive memory allocation // gcode_line is in/out parameter, to reduce expensive memory allocation
auto process_placeholders = [&](std::string& gcode_line) { auto process_placeholders = [&](std::string& gcode_line) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool processed = false; bool processed = false;
#else
unsigned int extra_lines_count = 0;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// remove trailing '\n' // remove trailing '\n'
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
std::string ret;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (line.length() > 1) { if (line.length() > 1) {
line = line.substr(1); line = line.substr(1);
if (m_time_processor.export_remaining_time_enabled && if (m_time_processor.export_remaining_time_enabled &&
@ -3739,29 +3722,16 @@ void GCodeProcessor::post_process()
const TimeMachine& machine = m_time_processor.machines[i]; const TimeMachine& machine = m_time_processor.machines[i];
if (machine.enabled) { if (machine.enabled) {
// export pair <percent, remaining time> // export pair <percent, remaining time>
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0)); (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
processed = true; processed = true;
#else
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
++extra_lines_count;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// export remaining time to next printer stop // export remaining time to next printer stop
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) { if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time); const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#else
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
last_exported_stop[i] = to_export_stop;
++extra_lines_count;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
@ -3775,12 +3745,8 @@ void GCodeProcessor::post_process()
sprintf(buf, "; estimated printing time (%s mode) = %s\n", sprintf(buf, "; estimated printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.time).c_str()); get_time_dhms(machine.time).c_str());
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(buf); export_lines.append_line(buf);
processed = true; processed = true;
#else
ret += buf;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
@ -3791,25 +3757,14 @@ void GCodeProcessor::post_process()
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n", sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str()); get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(buf); export_lines.append_line(buf);
processed = true; processed = true;
#else
ret += buf;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
return processed; return processed;
#else
if (!ret.empty())
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
gcode_line = ret;
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
}; };
std::vector<double> filament_mm(m_result.extruders_count, 0.0); std::vector<double> filament_mm(m_result.extruders_count, 0.0);
@ -3883,16 +3838,8 @@ void GCodeProcessor::post_process()
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute, time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
// Caches, to be modified // Caches, to be modified
&g1_times_cache_it, &last_exported_main, &last_exported_stop, &g1_times_cache_it, &last_exported_main, &last_exported_stop,
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
&export_lines] &export_lines]
#else
// String output
&export_line]
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
(const size_t g1_lines_counter) { (const size_t g1_lines_counter) {
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
unsigned int exported_lines_count = 0;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (m_time_processor.export_remaining_time_enabled) { if (m_time_processor.export_remaining_time_enabled) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = m_time_processor.machines[i]; const TimeMachine& machine = m_time_processor.machines[i];
@ -3906,17 +3853,9 @@ void GCodeProcessor::post_process()
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time), std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
time_in_minutes(machine.time - it->elapsed_time) }; time_in_minutes(machine.time - it->elapsed_time) };
if (last_exported_main[i] != to_export_main) { if (last_exported_main[i] != to_export_main) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
to_export_main.first, to_export_main.second)); to_export_main.first, to_export_main.second));
#else
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
to_export_main.first, to_export_main.second);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_main[i] = to_export_main; last_exported_main[i] = to_export_main;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
// export remaining time to next printer stop // export remaining time to next printer stop
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time, auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
@ -3926,15 +3865,8 @@ void GCodeProcessor::post_process()
if (last_exported_stop[i] != to_export_stop) { if (last_exported_stop[i] != to_export_stop) {
if (to_export_stop > 0) { if (to_export_stop > 0) {
if (last_exported_stop[i] != to_export_stop) { if (last_exported_stop[i] != to_export_stop) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
#else
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
else { else {
@ -3953,22 +3885,12 @@ void GCodeProcessor::post_process()
} }
if (is_last) { if (is_last) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1)) if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
else else
export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time))); export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)));
#else
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
else
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
@ -3977,12 +3899,8 @@ void GCodeProcessor::post_process()
} }
} }
} }
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
return exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
}; };
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// add lines XXX to exported gcode // add lines XXX to exported gcode
auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) { auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) {
const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line); const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line);
@ -4027,37 +3945,15 @@ void GCodeProcessor::post_process()
}); });
} }
}; };
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
m_result.lines_ends.clear(); m_result.lines_ends.clear();
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
// helper function to write to disk
size_t out_file_pos = 0;
auto write_string = [this, &export_line, &out, &out_path, &out_file_pos](const std::string& str) {
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
if (ferror(out.f)) {
out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n"));
}
for (size_t i = 0; i < export_line.size(); ++i)
if (export_line[i] == '\n')
m_result.lines_ends.emplace_back(out_file_pos + i + 1);
out_file_pos += export_line.size();
export_line.clear();
};
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
unsigned int line_id = 0; unsigned int line_id = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// Backtrace data for Tx gcode lines // Backtrace data for Tx gcode lines
static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 }; static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 };
// In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed // In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed
// to flush the backtrace cache accordingly // to flush the backtrace cache accordingly
float max_backtrace_time = 120.0f; float max_backtrace_time = 120.0f;
#else
std::vector<std::pair<unsigned int, unsigned int>> offsets;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
{ {
// Read the input stream 64kB at a time, extract lines and process them. // Read the input stream 64kB at a time, extract lines and process them.
@ -4081,24 +3977,15 @@ void GCodeProcessor::post_process()
gcode_line.insert(gcode_line.end(), it, it_end); gcode_line.insert(gcode_line.end(), it, it_end);
if (eol) { if (eol) {
++line_id; ++line_id;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.update(line_id, g1_lines_counter); export_lines.update(line_id, g1_lines_counter);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
gcode_line += "\n"; gcode_line += "\n";
// replace placeholder lines // replace placeholder lines
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool processed = process_placeholders(gcode_line); bool processed = process_placeholders(gcode_line);
if (processed) if (processed)
gcode_line.clear(); gcode_line.clear();
#else
auto [processed, lines_added_count] = process_placeholders(gcode_line);
if (processed && lines_added_count > 0)
offsets.push_back({ line_id, lines_added_count });
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (!processed) if (!processed)
processed = process_used_filament(gcode_line); processed = process_used_filament(gcode_line);
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (!processed && !is_temporary_decoration(gcode_line)) { if (!processed && !is_temporary_decoration(gcode_line)) {
if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
// add lines M73 where needed // add lines M73 where needed
@ -4113,18 +4000,6 @@ void GCodeProcessor::post_process()
if (!gcode_line.empty()) if (!gcode_line.empty())
export_lines.append_line(gcode_line); export_lines.append_line(gcode_line);
export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path); export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path);
#else
if (!processed && !is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
// remove temporary lines, add lines M73 where needed
unsigned int extra_lines_count = process_line_G1(g1_lines_counter++);
if (extra_lines_count > 0)
offsets.push_back({ line_id, extra_lines_count });
}
export_line += gcode_line;
if (export_line.length() > 65535)
write_string(export_line);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
gcode_line.clear(); gcode_line.clear();
} }
// Skip EOL. // Skip EOL.
@ -4139,30 +4014,12 @@ void GCodeProcessor::post_process()
} }
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.flush(out, m_result, out_path); export_lines.flush(out, m_result, out_path);
#else
if (!export_line.empty())
write_string(export_line);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
out.close(); out.close();
in.close(); in.close();
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.synchronize_moves(m_result); export_lines.synchronize_moves(m_result);
#else
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
unsigned int curr_offset_id = 0;
unsigned int total_offset = 0;
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
total_offset += offsets[curr_offset_id].second;
++curr_offset_id;
}
move.gcode_id += total_offset;
}
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (rename_file(out_path, m_result.filename)) if (rename_file(out_path, m_result.filename))
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' + throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +

View File

@ -127,9 +127,7 @@ namespace Slic3r {
float max_print_height; float max_print_height;
SettingsIds settings_ids; SettingsIds settings_ids;
size_t extruders_count; size_t extruders_count;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool backtrace_enabled; bool backtrace_enabled;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
std::vector<std::string> extruder_colors; std::vector<std::string> extruder_colors;
std::vector<float> filament_diameters; std::vector<float> filament_diameters;
std::vector<float> filament_densities; std::vector<float> filament_densities;

View File

@ -437,7 +437,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vector<const LayerR
if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
// it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway
polygons.emplace_back(std::vector { Point { 0, 0 } }); polygons.emplace_back(Points{ { 0, 0 } });
corresponding_regions_out.push_back(nullptr); corresponding_regions_out.push_back(nullptr);
} }

View File

@ -71,7 +71,6 @@ public:
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) { static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) {
return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0; return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0;
} }
@ -82,7 +81,6 @@ public:
const std::string_view cmd = temp.cmd(); const std::string_view cmd = temp.cmd();
return { cmd.begin(), cmd.end() }; return { cmd.begin(), cmd.end() };
} }
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
private: private:
std::string m_raw; std::string m_raw;

View File

@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point);
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{ {
Polygons pp; Polygons simplified_raw;
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { for (const Polygon &source_polygon : polygons) {
Polygon p = *it; Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance);
p.points.push_back(p.points.front()); if (simplified.size() > 3) {
p.points = MultiPoint::_douglas_peucker(p.points, tolerance); simplified.pop_back();
p.points.pop_back(); simplified_raw.push_back(Polygon{ std::move(simplified) });
pp.push_back(p); }
} }
*retval = Slic3r::simplify_polygons(pp); *retval = Slic3r::simplify_polygons(simplified_raw);
} }
double linint(double value, double oldmin, double oldmax, double newmin, double newmax) double linint(double value, double oldmin, double oldmax, double newmin, double newmax)

View File

@ -12,11 +12,6 @@
namespace Slic3r { namespace Slic3r {
namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Geometry { namespace Geometry {
// Generic result of an orientation predicate. // Generic result of an orientation predicate.

View File

@ -19,6 +19,8 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <oneapi/tbb/scalable_allocator.h>
//#define DEBUG_FILES //#define DEBUG_FILES
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
@ -211,8 +213,8 @@ void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin
this->print_z = layer->print_z; this->print_z = layer->print_z;
Lines obstacles; Lines obstacles;
obstacles.reserve(layer->malformed_lines.size()); obstacles.reserve(layer->curled_lines.size());
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); } for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
add_obstacles(obstacles); add_obstacles(obstacles);
} }
@ -267,7 +269,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>; using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>;
std::unordered_map<size_t, QNode> astar_cache{}; std::unordered_map<size_t, QNode> astar_cache{};
std::vector<Pixel> out_path; std::vector<Pixel, PointsAllocator<Pixel>> out_path;
std::vector<decltype(tracer)::Node> out_nodes; std::vector<decltype(tracer)::Node> out_nodes;
if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) { if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) {
@ -306,7 +308,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
svg.draw(scaled_point(start), "green", scale_(0.4)); svg.draw(scaled_point(start), "green", scale_(0.4));
#endif #endif
std::vector<Pixel> tmp_path; std::vector<Pixel, PointsAllocator<Pixel>> tmp_path;
tmp_path.reserve(out_path.size()); tmp_path.reserve(out_path.size());
// Some path found, reverse and remove points that do not change direction // Some path found, reverse and remove points that do not change direction
std::reverse(out_path.begin(), out_path.end()); std::reverse(out_path.begin(), out_path.end());

View File

@ -1,6 +1,7 @@
#ifndef slic3r_Layer_hpp_ #ifndef slic3r_Layer_hpp_
#define slic3r_Layer_hpp_ #define slic3r_Layer_hpp_
#include "Line.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "Flow.hpp" #include "Flow.hpp"
@ -325,7 +326,7 @@ public:
coordf_t bottom_z() const { return this->print_z - this->height; } coordf_t bottom_z() const { return this->print_z - this->height; }
//Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels. //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels.
Lines malformed_lines; CurledLines curled_lines;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged. // (with possibly differing extruder ID and slicing parameters) and merged.

View File

@ -230,10 +230,13 @@ Surfaces expand_bridges_detect_orientations(
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) && bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) &&
// One may ignore holes, they are irrelevant for intersection test. // One may ignore holes, they are irrelevant for intersection test.
! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) { ! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) {
// The two bridge regions intersect. Give them the same group id. // The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id(it->src_id); uint32_t id = group_id(it->src_id);
uint32_t id2 = group_id(it2->src_id); uint32_t id2 = group_id(it2->src_id);
bridges[it->src_id].group_id = bridges[it2->src_id].group_id = std::min(id, id2); if (id < id2)
bridges[id2].group_id = id;
else
bridges[id].group_id = id2;
} }
} }
} }

View File

@ -40,11 +40,12 @@ template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l)
// Distance to the closest point of line. // Distance to the closest point of line.
template<class L> template<class L>
double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *nearest_point) inline double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *nearest_point)
{ {
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>(); using VecType = Vec<Dim<L>, double>;
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>(); const VecType v = (get_b(line) - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt const VecType va = (point - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm();
if (l2 == 0.0) { if (l2 == 0.0) {
// a == b case // a == b case
*nearest_point = get_a(line); *nearest_point = get_a(line);
@ -53,19 +54,20 @@ double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, V
// Consider the line extending the segment, parameterized as a + t (b - a). // Consider the line extending the segment, parameterized as a + t (b - a).
// We find projection of this point onto the line. // We find projection of this point onto the line.
// It falls where t = [(this-a) . (b-a)] / |b-a|^2 // 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) { if (t <= 0.0) {
// beyond the 'a' end of the segment // beyond the 'a' end of the segment
*nearest_point = get_a(line); *nearest_point = get_a(line);
return va.squaredNorm(); return va.squaredNorm();
} else if (t >= 1.0) { } else if (t >= l2) {
// beyond the 'b' end of the segment // beyond the 'b' end of the segment
*nearest_point = get_b(line); *nearest_point = get_b(line);
return (point - get_b(line)).template cast<double>().squaredNorm(); return (point - get_b(line)).template cast<double>().squaredNorm();
} }
*nearest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>(); const VecType w = ((t / l2) * v).eval();
return (t * v - va).squaredNorm(); *nearest_point = (get_a(line).template cast<double>() + w).template cast<Scalar<L>>();
return (w - va).squaredNorm();
} }
// Distance to the closest point of line. // Distance to the closest point of line.
@ -209,6 +211,18 @@ public:
double a_width, b_width; double a_width, b_width;
}; };
class CurledLine : public Line
{
public:
CurledLine() : curled_height(0.0f) {}
CurledLine(const Point& a, const Point& b) : Line(a, b), curled_height(0.0f) {}
CurledLine(const Point& a, const Point& b, float curled_height) : Line(a, b), curled_height(curled_height) {}
float curled_height;
};
using CurledLines = std::vector<CurledLine>;
class Line3 class Line3
{ {
public: public:

View File

@ -2406,11 +2406,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
{ {
// static const double SIMPLIFY_TOLERANCE_MM = 0.1; // static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation(); Polygon p = get_object()->convex_hull_2d(this->get_matrix());
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
Polygon p = get_object()->convex_hull_2d(trafo_instance);
// if (!p.points.empty()) { // if (!p.points.empty()) {
// Polygons pp{p}; // Polygons pp{p};
@ -2420,12 +2416,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
arrangement::ArrangePolygon ret; arrangement::ArrangePolygon ret;
ret.poly.contour = std::move(p); ret.poly.contour = std::move(p);
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; ret.translation = Vec2crd::Zero();
ret.rotation = get_rotation(Z); ret.rotation = 0.;
return ret; return ret;
} }
void ModelInstance::apply_arrange_result(const Vec2d &offs, double rotation)
{
// write the transformation data into the model instance
auto trafo = get_transformation().get_matrix();
auto tr = Transform3d::Identity();
tr.translate(to_3d(unscaled(offs), 0.));
trafo = tr * Eigen::AngleAxisd(rotation, Vec3d::UnitZ()) * trafo;
m_transformation.set_matrix(trafo);
this->object->invalidate_bounding_box();
}
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
{ {
TriangleSelector selector(mv.mesh()); TriangleSelector selector(mv.mesh());

View File

@ -1167,14 +1167,7 @@ public:
arrangement::ArrangePolygon get_arrange_polygon() const; arrangement::ArrangePolygon get_arrange_polygon() const;
// Apply the arrange result on the ModelInstance // Apply the arrange result on the ModelInstance
void apply_arrange_result(const Vec2d& offs, double rotation) void apply_arrange_result(const Vec2d& offs, double rotation);
{
// write the transformation data into the model instance
set_rotation(Z, rotation);
set_offset(X, unscale<double>(offs(X)));
set_offset(Y, unscale<double>(offs(Y)));
this->object->invalidate_bounding_box();
}
protected: protected:
friend class Print; friend class Print;

View File

@ -103,10 +103,10 @@ bool MultiPoint::remove_duplicate_points()
return false; return false;
} }
std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance) Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance)
{ {
std::vector<Point> result_pts; Points result_pts;
double tolerance_sq = tolerance * tolerance; auto tolerance_sq = int64_t(sqr(tolerance));
if (! pts.empty()) { if (! pts.empty()) {
const Point *anchor = &pts.front(); const Point *anchor = &pts.front();
size_t anchor_idx = 0; size_t anchor_idx = 0;
@ -120,14 +120,40 @@ std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, c
dpStack.reserve(pts.size()); dpStack.reserve(pts.size());
dpStack.emplace_back(floater_idx); dpStack.emplace_back(floater_idx);
for (;;) { for (;;) {
double max_dist_sq = 0.0; int64_t max_dist_sq = 0;
size_t furthest_idx = anchor_idx; size_t furthest_idx = anchor_idx;
// find point furthest from line seg created by (anchor, floater) and note it // 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); const Point a = *anchor;
if (dist_sq > max_dist_sq) { const Point f = *floater;
max_dist_sq = dist_sq; const Vec2i64 v = (f - a).cast<int64_t>();
furthest_idx = i; 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<int64_t>().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<double>();
for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
const Point p = pts[i];
const Vec2i64 va = (p - a).template cast<int64_t>();
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<int64_t>().squaredNorm();
} else {
const Vec2i64 w = ((double(t) / dl2) * dv).cast<int64_t>();
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 // remove point if less than tolerance

View File

@ -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); static Points visivalingam(const Points& pts, const double& tolerance);
inline auto begin() { return points.begin(); } inline auto begin() { return points.begin(); }
@ -110,7 +110,7 @@ public:
}; };
extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents(const MultiPoint &mp);
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle); extern BoundingBox get_extents_rotated(const Points &points, double angle);
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
inline double length(const Points &pts) { inline double length(const Points &pts) {

View File

@ -40,10 +40,11 @@
#include <stack> #include <stack>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <ankerl/unordered_dense.h>
// #define ARACHNE_DEBUG // #define ARACHNE_DEBUG
#ifdef ARACHNE_DEBUG #ifdef ARACHNE_DEBUG
@ -569,7 +570,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
size_t occurrence = 0; size_t occurrence = 0;
bool is_overhang = false; bool is_overhang = false;
}; };
std::unordered_map<Point, PointInfo, PointHash> point_occurrence; ankerl::unordered_dense::map<Point, PointInfo, PointHash> point_occurrence;
for (const ExtrusionPath &path : paths) { for (const ExtrusionPath &path : paths) {
++point_occurrence[path.polyline.first_point()].occurrence; ++point_occurrence[path.polyline.first_point()].occurrence;
++point_occurrence[path.polyline.last_point()].occurrence; ++point_occurrence[path.polyline.last_point()].occurrence;
@ -1153,11 +1154,11 @@ void PerimeterGenerator::process_arachne(
// Find topological order with constraints from extrusions_constrains. // Find topological order with constraints from extrusions_constrains.
std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
std::unordered_map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx; ankerl::unordered_dense::map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx;
for (size_t idx = 0; idx < all_extrusions.size(); idx++) for (size_t idx = 0; idx < all_extrusions.size(); idx++)
map_extrusion_to_idx.emplace(all_extrusions[idx], idx); map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first); Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
for (auto [before, after] : extrusions_constrains) { for (auto [before, after] : extrusions_constrains) {
auto after_it = map_extrusion_to_idx.find(after); auto after_it = map_extrusion_to_idx.find(after);
++blocked[after_it->second]; ++blocked[after_it->second];

View File

@ -1644,9 +1644,9 @@ namespace client
} }
if (! evaluated) { if (! evaluated) {
// Clamp x into the table range with EPSILON. // Clamp x into the table range with EPSILON.
if (x > table.table.front().x - EPSILON) if (double x0 = table.table.front().x; x > x0 - EPSILON && x < x0)
out.set_d(table.table.front().y); out.set_d(table.table.front().y);
else if (x < table.table.back().x + EPSILON) else if (double x1 = table.table.back().x; x > x1 && x < x1 + EPSILON)
out.set_d(table.table.back().y); out.set_d(table.table.back().y);
else else
// The value is really outside the table range. // The value is really outside the table range.

View File

@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point &center)
(*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx );
} }
bool has_duplicate_points(std::vector<Point> &&pts) bool has_duplicate_points(Points &&pts)
{ {
std::sort(pts.begin(), pts.end()); std::sort(pts.begin(), pts.end());
for (size_t i = 1; i < pts.size(); ++ i) for (size_t i = 1; i < pts.size(); ++ i)
@ -97,15 +97,15 @@ template BoundingBox get_extents<true>(const Points &pts);
// if IncludeBoundary, then a bounding box is defined even for a single point. // if IncludeBoundary, then a bounding box is defined even for a single point.
// otherwise a bounding box is only defined if it has a positive area. // otherwise a bounding box is only defined if it has a positive area.
template<bool IncludeBoundary> template<bool IncludeBoundary>
BoundingBox get_extents(const std::vector<Points> &pts) BoundingBox get_extents(const VecOfPoints &pts)
{ {
BoundingBox bbox; BoundingBox bbox;
for (const Points &p : pts) for (const Points &p : pts)
bbox.merge(get_extents<IncludeBoundary>(p)); bbox.merge(get_extents<IncludeBoundary>(p));
return bbox; return bbox;
} }
template BoundingBox get_extents<false>(const std::vector<Points> &pts); template BoundingBox get_extents<false>(const VecOfPoints &pts);
template BoundingBox get_extents<true>(const std::vector<Points> &pts); template BoundingBox get_extents<true>(const VecOfPoints &pts);
BoundingBoxf get_extents(const std::vector<Vec2d> &pts) BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
{ {

View File

@ -9,6 +9,9 @@
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#include <oneapi/tbb/scalable_allocator.h>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include "LocalesUtils.hpp" #include "LocalesUtils.hpp"
@ -49,7 +52,10 @@ using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>; using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>; using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
using Points = std::vector<Point>; template<typename BaseType>
using PointsAllocator = tbb::scalable_allocator<BaseType>;
//using PointsAllocator = std::allocator<BaseType>;
using Points = std::vector<Point, PointsAllocator<Point>>;
using PointPtrs = std::vector<Point*>; using PointPtrs = std::vector<Point*>;
using PointConstPtrs = std::vector<const Point*>; using PointConstPtrs = std::vector<const Point*>;
using Points3 = std::vector<Vec3crd>; using Points3 = std::vector<Vec3crd>;
@ -57,6 +63,8 @@ using Pointfs = std::vector<Vec2d>;
using Vec2ds = std::vector<Vec2d>; using Vec2ds = std::vector<Vec2d>;
using Pointf3s = std::vector<Vec3d>; using Pointf3s = std::vector<Vec3d>;
using VecOfPoints = std::vector<Points, PointsAllocator<Points>>;
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>; using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>; using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>; using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
@ -247,9 +255,9 @@ extern template BoundingBox get_extents<true>(const Points &pts);
// if IncludeBoundary, then a bounding box is defined even for a single point. // if IncludeBoundary, then a bounding box is defined even for a single point.
// otherwise a bounding box is only defined if it has a positive area. // otherwise a bounding box is only defined if it has a positive area.
template<bool IncludeBoundary = false> template<bool IncludeBoundary = false>
BoundingBox get_extents(const std::vector<Points> &pts); BoundingBox get_extents(const VecOfPoints &pts);
extern template BoundingBox get_extents<false>(const std::vector<Points> &pts); extern template BoundingBox get_extents<false>(const VecOfPoints &pts);
extern template BoundingBox get_extents<true>(const std::vector<Points> &pts); extern template BoundingBox get_extents<true>(const VecOfPoints &pts);
BoundingBoxf get_extents(const std::vector<Vec2d> &pts); BoundingBoxf get_extents(const std::vector<Vec2d> &pts);
@ -263,16 +271,16 @@ inline std::pair<Point, bool> nearest_point(const Points &points, const Point &p
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// The points are copied, sorted and checked for duplicates globally. // The points are copied, sorted and checked for duplicates globally.
bool has_duplicate_points(std::vector<Point> &&pts); bool has_duplicate_points(Points &&pts);
inline bool has_duplicate_points(const std::vector<Point> &pts) inline bool has_duplicate_points(const Points &pts)
{ {
std::vector<Point> cpy = pts; Points cpy = pts;
return has_duplicate_points(std::move(cpy)); return has_duplicate_points(std::move(cpy));
} }
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// Only successive points are checked for equality. // Only successive points are checked for equality.
inline bool has_duplicate_successive_points(const std::vector<Point> &pts) inline bool has_duplicate_successive_points(const Points &pts)
{ {
for (size_t i = 1; i < pts.size(); ++ i) for (size_t i = 1; i < pts.size(); ++ i)
if (pts[i - 1] == pts[i]) if (pts[i - 1] == pts[i])
@ -282,7 +290,7 @@ inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// Only successive points are checked for equality. Additionally, first and last points are compared for equality. // Only successive points are checked for equality. Additionally, first and last points are compared for equality.
inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts) inline bool has_duplicate_successive_points_closed(const Points &pts)
{ {
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back()); return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
} }

View File

@ -96,7 +96,7 @@ bool Polygon::make_clockwise()
void Polygon::douglas_peucker(double tolerance) void Polygon::douglas_peucker(double tolerance)
{ {
this->points.push_back(this->points.front()); 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(); p.pop_back();
this->points = std::move(p); this->points = std::move(p);
} }
@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const
// on the whole polygon // on the whole polygon
Points points = this->points; Points points = this->points;
points.push_back(points.front()); points.push_back(points.front());
Polygon p(MultiPoint::_douglas_peucker(points, tolerance)); Polygon p(MultiPoint::douglas_peucker(points, tolerance));
p.points.pop_back(); p.points.pop_back();
Polygons pp; Polygons pp;
@ -404,7 +404,7 @@ bool has_duplicate_points(const Polygons &polys)
// Check globally. // Check globally.
#if 0 #if 0
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
std::vector<Point> allpts; Points allpts;
allpts.reserve(count_points(polys)); allpts.reserve(count_points(polys));
for (const Polygon &poly : polys) for (const Polygon &poly : polys)
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end()); allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly); 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; Polygons out;
out.reserve(source_polygons.size()); out.reserve(source_polygons.size());
for (const Polygon &source_polygon : source_polygons) { 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), // 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); simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out);
// 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));
}
} }
return out; return out;
} }

View File

@ -5,15 +5,16 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "Line.hpp" #include "Line.hpp"
#include "Point.hpp"
#include "MultiPoint.hpp" #include "MultiPoint.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
namespace Slic3r { namespace Slic3r {
class Polygon; class Polygon;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
using PolygonPtrs = std::vector<Polygon*>; using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
using ConstPolygonPtrs = std::vector<const Polygon*>; using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
// Returns true if inside. Returns border_result if on boundary. // Returns true if inside. Returns border_result if on boundary.
bool contains(const Polygon& polygon, const Point& p, bool border_result = true); bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
@ -148,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) inline void polygons_rotate(Polygons &polys, double angle)
{ {
@ -241,7 +243,7 @@ inline Polylines to_polylines(Polygons &&polys)
return polylines; return polylines;
} }
inline Polygons to_polygons(const std::vector<Points> &paths) inline Polygons to_polygons(const VecOfPoints &paths)
{ {
Polygons out; Polygons out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -250,7 +252,7 @@ inline Polygons to_polygons(const std::vector<Points> &paths)
return out; return out;
} }
inline Polygons to_polygons(std::vector<Points> &&paths) inline Polygons to_polygons(VecOfPoints &&paths)
{ {
Polygons out; Polygons out;
out.reserve(paths.size()); out.reserve(paths.size());

View File

@ -17,7 +17,7 @@ namespace EdgeGrid {
struct TrimmedLoop struct TrimmedLoop
{ {
std::vector<Point> points; Points points;
// Number of points per segment. Empty if the loop is // Number of points per segment. Empty if the loop is
std::vector<unsigned int> segments; std::vector<unsigned int> segments;

View File

@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const
void Polyline::simplify(double tolerance) void Polyline::simplify(double tolerance)
{ {
this->points = MultiPoint::_douglas_peucker(this->points, tolerance); this->points = MultiPoint::douglas_peucker(this->points, tolerance);
} }
#if 0 #if 0

View File

@ -158,8 +158,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
// src_first: the merge point is at src.begin() or src.end()? // src_first: the merge point is at src.begin() or src.end()?
// The orientation of the resulting polyline is unknown, the output polyline may start // The orientation of the resulting polyline is unknown, the output polyline may start
// either with src piece or dst piece. // either with src piece or dst piece.
template<typename PointType> template<typename PointsType>
inline void polylines_merge(std::vector<PointType> &dst, bool dst_first, std::vector<PointType> &&src, bool src_first) inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first)
{ {
if (dst_first) { if (dst_first) {
if (src_first) if (src_first)

View File

@ -444,7 +444,8 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only", "support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",

View File

@ -8,7 +8,6 @@
#include "Geometry/ConvexHull.hpp" #include "Geometry/ConvexHull.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "SupportMaterial.hpp"
#include "Thread.hpp" #include "Thread.hpp"
#include "GCode.hpp" #include "GCode.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
@ -880,7 +879,6 @@ void Print::process()
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects) for (PrintObject *obj : m_objects)
obj->make_perimeters(); obj->make_perimeters();
this->set_status(70, _u8L("Infilling layers"));
for (PrintObject *obj : m_objects) for (PrintObject *obj : m_objects)
obj->infill(); obj->infill();
for (PrintObject *obj : m_objects) for (PrintObject *obj : m_objects)
@ -1132,9 +1130,9 @@ Polygons Print::first_layer_islands() const
return islands; return islands;
} }
std::vector<Point> Print::first_layer_wipe_tower_corners() const Points Print::first_layer_wipe_tower_corners() const
{ {
std::vector<Point> pts_scaled; Points pts_scaled;
if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) {
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;

View File

@ -626,7 +626,7 @@ private:
// Islands of objects and their supports extruded at the 1st layer. // Islands of objects and their supports extruded at the 1st layer.
Polygons first_layer_islands() const; Polygons first_layer_islands() const;
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners() const; Points first_layer_wipe_tower_corners() const;
// Returns true if any of the print_objects has print_object_step valid. // Returns true if any of the print_objects has print_object_step valid.
// That means data shared by all print objects of the print_objects span may still use the shared data. // That means data shared by all print objects of the print_objects span may still use the shared data.

View File

@ -2939,6 +2939,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(5)); def->set_default_value(new ConfigOptionFloat(5));
def = this->add("support_tree_branch_diameter_double_wall", coFloat);
def->label = L("Branch Diameter with double walls");
def->category = L("Support material");
// TRN PrintSettings: "Organic supports" > "Branch Diameter"
def->tooltip = L("Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. "
"Set this value to zero for no double walls.");
def->sidetext = L("mm");
def->min = 0;
def->max = 100.f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(3));
// Tree Support Branch Distance // Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause // How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove. // the tree support to touch the model at more points, causing better overhang but making support harder to remove.

View File

@ -554,6 +554,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, support_tree_angle_slow)) ((ConfigOptionFloat, support_tree_angle_slow))
((ConfigOptionFloat, support_tree_branch_diameter)) ((ConfigOptionFloat, support_tree_branch_diameter))
((ConfigOptionFloat, support_tree_branch_diameter_angle)) ((ConfigOptionFloat, support_tree_branch_diameter_angle))
((ConfigOptionFloat, support_tree_branch_diameter_double_wall))
((ConfigOptionPercent, support_tree_top_rate)) ((ConfigOptionPercent, support_tree_top_rate))
((ConfigOptionFloat, support_tree_branch_distance)) ((ConfigOptionFloat, support_tree_branch_distance))
((ConfigOptionFloat, support_tree_tip_diameter)) ((ConfigOptionFloat, support_tree_tip_diameter))

View File

@ -17,8 +17,8 @@
#include "MutablePolygon.hpp" #include "MutablePolygon.hpp"
#include "PrintBase.hpp" #include "PrintBase.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "SupportMaterial.hpp" #include "Support/SupportMaterial.hpp"
#include "TreeSupport.hpp" #include "Support/TreeSupport.hpp"
#include "Surface.hpp" #include "Surface.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
@ -27,7 +27,7 @@
#include "Fill/FillAdaptive.hpp" #include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp" #include "Fill/FillLightning.hpp"
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "SupportMaterial.hpp" #include "Support/SupportMaterial.hpp"
#include "SupportSpotsGenerator.hpp" #include "SupportSpotsGenerator.hpp"
#include "TriangleSelectorWrapper.hpp" #include "TriangleSelectorWrapper.hpp"
#include "format.hpp" #include "format.hpp"
@ -56,6 +56,20 @@
using namespace std::literals; using namespace std::literals;
// #define PRINT_OBJECT_TIMING
#ifdef PRINT_OBJECT_TIMING
// time limit for one ClipperLib operation (union / diff / offset), in ms
#define PRINT_OBJECT_TIME_LIMIT_DEFAULT 50
#include <boost/current_function.hpp>
#include "Timer.hpp"
#define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION)
#define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION)
#else
#define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) do {} while(false)
#define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) do {} while(false)
#endif // PRINT_OBJECT_TIMING
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#define SLIC3R_DEBUG #define SLIC3R_DEBUG
#endif #endif
@ -178,6 +192,7 @@ void PrintObject::make_perimeters()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1), tbb::blocked_range<size_t>(0, m_layers.size() - 1),
[this, &region, region_id](const tbb::blocked_range<size_t>& range) { [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
@ -237,6 +252,7 @@ void PrintObject::make_perimeters()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) { [this](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_perimeters(); m_layers[layer_idx]->make_perimeters();
@ -408,6 +424,7 @@ void PrintObject::infill()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get()); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
@ -431,6 +448,7 @@ void PrintObject::ironing()
// Ironing starting with layer 0 to support ironing all surfaces. // Ironing starting with layer 0 to support ironing all surfaces.
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) { [this](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_ironing(); m_layers[layer_idx]->make_ironing();
@ -491,7 +509,9 @@ void PrintObject::generate_support_material()
void PrintObject::estimate_curled_extrusions() void PrintObject::estimate_curled_extrusions()
{ {
if (this->set_started(posEstimateCurledExtrusions)) { if (this->set_started(posEstimateCurledExtrusions)) {
if (this->print()->config().avoid_crossing_curled_overhangs) { if (this->print()->config().avoid_crossing_curled_overhangs ||
std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(),
[](const PrintRegion *region) { return region->config().enable_dynamic_overhang_speeds.getBool(); })) {
BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start"; BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start";
m_print->set_status(88, _u8L("Estimating curled extrusions")); m_print->set_status(88, _u8L("Estimating curled extrusions"));
@ -528,16 +548,17 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1))); std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally // ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()), tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) { [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
std::vector<Vec3d> &out = overhangs[surface_idx]; for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
m_print->throw_if_canceled(); std::vector<Vec3d> &out = overhangs[surface_idx];
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, m_print->throw_if_canceled();
surfaces_w_bottom_z[surface_idx].second)); append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
for (Vec3d &p : out) surfaces_w_bottom_z[surface_idx].second));
p = (to_octree * p).eval(); for (Vec3d &p : out)
} p = (to_octree * p).eval();
}); }
});
// and gather them. // and gather them.
for (size_t i = 1; i < overhangs.size(); ++ i) for (size_t i = 1; i < overhangs.size(); ++ i)
append(overhangs.front(), std::move(overhangs[i])); append(overhangs.front(), std::move(overhangs[i]));
@ -691,6 +712,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_tree_angle_slow" || opt_key == "support_tree_angle_slow"
|| opt_key == "support_tree_branch_diameter" || opt_key == "support_tree_branch_diameter"
|| opt_key == "support_tree_branch_diameter_angle" || opt_key == "support_tree_branch_diameter_angle"
|| opt_key == "support_tree_branch_diameter_double_wall"
|| opt_key == "support_tree_top_rate" || opt_key == "support_tree_top_rate"
|| opt_key == "support_tree_branch_distance" || opt_key == "support_tree_branch_distance"
|| opt_key == "support_tree_tip_diameter" || opt_key == "support_tree_tip_diameter"
@ -909,6 +931,7 @@ void PrintObject::detect_surfaces_type()
// In non-spiral vase mode, go over all layers. // In non-spiral vase mode, go over all layers.
m_layers.size()), m_layers.size()),
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) { [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print. // the support from the print.
SurfaceType surface_type_bottom_other = SurfaceType surface_type_bottom_other =
@ -1057,6 +1080,7 @@ void PrintObject::detect_surfaces_type()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, region_id](const tbb::blocked_range<size_t>& range) { [this, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
@ -1115,6 +1139,7 @@ void PrintObject::process_external_surfaces()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1), tbb::blocked_range<size_t>(0, m_layers.size() - 1),
[this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) { [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
if (layer_expansions_and_voids[layer_idx + 1]) { if (layer_expansions_and_voids[layer_idx + 1]) {
// Layer above is partially filled with solid infill (top, bottom, bridging...), // Layer above is partially filled with solid infill (top, bottom, bridging...),
@ -1140,6 +1165,7 @@ void PrintObject::process_external_surfaces()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) { [this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
@ -1192,6 +1218,7 @@ void PrintObject::discover_vertical_shells()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge }; const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
const size_t num_regions = this->num_printing_regions(); const size_t num_regions = this->num_printing_regions();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1265,6 +1292,7 @@ void PrintObject::discover_vertical_shells()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge }; const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1290,10 +1318,12 @@ void PrintObject::discover_vertical_shells()
} }
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
grain_size = 1;
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, region_id, &cache_top_botom_regions] [this, region_id, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1624,6 +1654,7 @@ void PrintObject::bridge_over_infill()
tbb::concurrent_vector<CandidateSurface> candidate_surfaces; tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this),
&candidate_surfaces](tbb::blocked_range<size_t> r) { &candidate_surfaces](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx); const Layer *layer = po->get_layer(lidx);
if (layer->lower_layer == nullptr) { if (layer->lower_layer == nullptr) {
@ -1721,6 +1752,7 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_generate_infill.size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_generate_infill.size()), [po = static_cast<const PrintObject *>(this),
&layers_to_generate_infill, &layers_to_generate_infill,
&infill_lines](tbb::blocked_range<size_t> r) { &infill_lines](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_to_generate_infill[job_idx]; size_t lidx = layers_to_generate_infill[job_idx];
infill_lines.at( infill_lines.at(
@ -1752,6 +1784,7 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
&layer_area_covered_by_candidates]( &layer_area_covered_by_candidates](
tbb::blocked_range<size_t> r) { tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_with_candidates[job_idx]; size_t lidx = layers_with_candidates[job_idx];
for (const auto &candidate : surfaces_by_layer.at(lidx)) { for (const auto &candidate : surfaces_by_layer.at(lidx)) {
@ -1990,8 +2023,8 @@ void PrintObject::bridge_over_infill()
// reconstruct polygon from polygon sections // reconstruct polygon from polygon sections
struct TracedPoly struct TracedPoly
{ {
std::vector<Point> lows; Points lows;
std::vector<Point> highs; Points highs;
}; };
std::vector<TracedPoly> current_traced_polys; std::vector<TracedPoly> current_traced_polys;
@ -2070,6 +2103,7 @@ void PrintObject::bridge_over_infill()
determine_bridging_angle, determine_bridging_angle,
construct_anchored_polygon]( construct_anchored_polygon](
tbb::blocked_range<size_t> r) { tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) {
for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) {
size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx];
@ -2242,6 +2276,7 @@ void PrintObject::bridge_over_infill()
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info();
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) { tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end())
continue; continue;
@ -2758,6 +2793,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs<Layer> layers, const inde
[&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) { [&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
std::array<Vec3f, 3> facet; std::array<Vec3f, 3> facet;
// Transform the triangle into worlds coords. // Transform the triangle into worlds coords.

View File

@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp)
Points ConcaveHull::calculate_centroids() const Points ConcaveHull::calculate_centroids() const
{ {
// We get the centroids of all the islands in the 2D slice // We get the centroids of all the islands in the 2D slice
Points centroids = reserve_vector<Point>(m_polys.size()); Points centroids;
centroids.reserve(m_polys.size());
std::transform(m_polys.begin(), m_polys.end(), std::transform(m_polys.begin(), m_polys.end(),
std::back_inserter(centroids), std::back_inserter(centroids),
[](const Polygon &poly) { return centroid(poly); }); [](const Polygon &poly) { return centroid(poly); });

View File

@ -6,6 +6,8 @@
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <libslic3r/Point.hpp>
struct indexed_triangle_set; struct indexed_triangle_set;
namespace Slic3r { namespace Slic3r {
@ -13,7 +15,7 @@ namespace Slic3r {
class ExPolygon; class ExPolygon;
class Polygon; class Polygon;
using ExPolygons = std::vector<ExPolygon>; using ExPolygons = std::vector<ExPolygon>;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
namespace sla { namespace sla {

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>
#include <libslic3r/AABBMesh.hpp> #include <libslic3r/AABBMesh.hpp>
@ -14,9 +15,6 @@
namespace Slic3r { namespace Slic3r {
using Polygons = std::vector<Polygon>;
using ExPolygons = std::vector<ExPolygon>;
namespace sla { namespace sla {
struct SupportTreeConfig struct SupportTreeConfig

View File

@ -305,7 +305,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st
bench.stop(); bench.stop();
if (!m.empty()) if (!po.m_preview_meshes[step]->empty())
BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec(); BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec();
else else
BOOST_LOG_TRIVIAL(error) << "Preview failed!"; BOOST_LOG_TRIVIAL(error) << "Preview failed!";

View File

@ -6,6 +6,8 @@
#include <random> #include <random>
#include <algorithm> #include <algorithm>
#include <ankerl/unordered_dense.h>
namespace Slic3r { namespace Slic3r {
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) { void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) {
@ -155,7 +157,7 @@ void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_
} }
//Extract the result mesh //Extract the result mesh
std::unordered_map<size_t, size_t> final_vertices_mapping; ankerl::unordered_dense::map<size_t, size_t> final_vertices_mapping;
std::vector<Vec3f> final_vertices; std::vector<Vec3f> final_vertices;
std::vector<Vec3i> final_indices; std::vector<Vec3i> final_indices;
final_indices.reserve(face_indices.size()); final_indices.reserve(face_indices.size());

View File

@ -8,10 +8,13 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace ClipperLib { class PolyNode; }
namespace Slic3r { namespace Slic3r {
namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
}
class ExPolygon; class ExPolygon;
using ExPolygons = std::vector<ExPolygon>; using ExPolygons = std::vector<ExPolygon>;
@ -29,7 +32,7 @@ void chain_and_reorder_extrusion_paths(std::vect
Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr);
inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items); ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items);
// Chain instances of print objects by an approximate shortest path. // Chain instances of print objects by an approximate shortest path.
// Returns pairs of PrintObject idx and instance of that PrintObject. // Returns pairs of PrintObject idx and instance of that PrintObject.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
#include "../Polygon.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r {
class PrintObject;
class SupportLayer;
namespace FFFSupport {
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts(
const PrintConfig &print_config,
const Layer &lower_layer,
const LayerRegion &layerm,
float fw,
Polygons &contact_polygons);
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
// produced by this function.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
// Input / output, will be merged with output
SupportGeneratorLayersPtr &top_interface_layers,
SupportGeneratorLayersPtr &top_base_interface_layers,
// Input, will be trimmed with the newly created interface layers.
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage);
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
template<typename IteratorType, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = 0;
} else if (idx == IndexType(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_higher_equal(begin[idx_mid]))
idx_high = idx_mid;
else
idx_low = idx_mid;
}
idx = fn_higher_equal(begin[idx_low]) ? idx_low :
(fn_higher_equal(begin[idx_high]) ? idx_high : size);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (int(idx) < size && ! fn_higher_equal(begin[idx]))
++ idx;
}
return idx;
}
template<typename T, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(const std::vector<T>& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal);
}
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
// If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards.
template<typename IT, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = -1;
} else if (idx < -1) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_lower_equal(begin[idx_mid]))
idx_low = idx_mid;
else
idx_high = idx_mid;
}
idx = fn_lower_equal(begin[idx_high]) ? idx_high :
(fn_lower_equal(begin[idx_low ]) ? idx_low : -1);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (idx >= 0 && ! fn_lower_equal(begin[idx]))
-- idx;
}
return idx;
}
template<typename T, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
}
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

View File

@ -0,0 +1,108 @@
#if 1 //#ifdef SLIC3R_DEBUG
#include "../ClipperUtils.hpp"
#include "../SVG.hpp"
#include "../Layer.hpp"
#include "SupportLayer.hpp"
namespace Slic3r::FFFSupport {
const char* support_surface_type_to_color_name(const SupporLayerType surface_type)
{
switch (surface_type) {
case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red";
case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green";
case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue";
case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow
case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta
case SupporLayerType::RaftInterface: return "rgb(0,255,255)";
case SupporLayerType::RaftBase: return "rgb(128,128,128)";
case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon
default: return "rgb(64,64,64)";
};
}
Point export_support_surface_type_legend_to_svg_box_size()
{
return Point(scale_(1.+10.*8.), scale_(3.));
}
void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact));
// 2nd row
pos_x = pos_x0;
pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate));
}
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
void export_print_z_polygons_and_extrusions_to_svg(
const char *path,
SupportGeneratorLayer ** const layers,
int n_layers,
SupportLayer &support_layer)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
Polygons polygons_support, polygons_interface;
support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON));
// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
svg.draw(union_ex(polygons_support), "brown");
svg.draw(union_ex(polygons_interface), "black");
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
} // namespace Slic3r
#endif /* SLIC3R_DEBUG */

View File

@ -0,0 +1,18 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
namespace Slic3r {
class SupportGeneratorLayer;
class SupportLayer;
namespace FFFSupport {
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

View File

@ -0,0 +1,146 @@
#ifndef slic3r_SupportLayer_hpp_
#define slic3r_SupportLayer_hpp_
#include <oneapi/tbb/scalable_allocator.h>
#include <oneapi/tbb/spin_mutex.h>
// for Slic3r::deque
#include "../libslic3r.h"
#include "ClipperUtils.hpp"
#include "Polygon.hpp"
namespace Slic3r::FFFSupport {
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum class SupporLayerType {
Unknown = 0,
// Ratft base layer, to be printed with the support material.
RaftBase,
// Raft interface layer, to be printed with the support interface material.
RaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
BottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an BottomContact layer.
BottomInterface,
// Sparse base support layer, to be printed with a support material.
Base,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with TopContact layer.
TopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
TopContact,
// Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface.
Intermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
class SupportGeneratorLayer
{
public:
void reset() {
*this = SupportGeneratorLayer();
}
bool operator==(const SupportGeneratorLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const SupportGeneratorLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(SupportGeneratorLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { SupporLayerType::Unknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
class SupportGeneratorLayerStorage {
public:
SupportGeneratorLayer& allocate_unguarded(SupporLayerType layer_type) {
m_storage.emplace_back();
m_storage.back().layer_type = layer_type;
return m_storage.back();
}
SupportGeneratorLayer& allocate(SupporLayerType layer_type)
{
m_mutex.lock();
m_storage.emplace_back();
SupportGeneratorLayer *layer_new = &m_storage.back();
m_mutex.unlock();
layer_new->layer_type = layer_type;
return *layer_new;
}
private:
template<typename BaseType>
using Allocator = tbb::scalable_allocator<BaseType>;
Slic3r::deque<SupportGeneratorLayer, Allocator<SupportGeneratorLayer>> m_storage;
tbb::spin_mutex m_mutex;
};
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
} // namespace Slic3r
#endif /* slic3r_SupportLayer_hpp_ */

View File

@ -0,0 +1,101 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "../Flow.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r {
class PrintObject;
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value || m_object_config->support_material_enforce_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
using SupportParameters = FFFSupport::SupportParameters;
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
SupportGeneratorLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Following objects are not owned by SupportMaterial class.
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */

View File

@ -0,0 +1,144 @@
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportParameters.hpp"
namespace Slic3r::FFFSupport {
SupportParameters::SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_material_interface_extruder.value > 0 && print_config.filament_soluble.get_at(object_config.support_material_interface_extruder.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_material_extruder.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_material_extruder.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_material_interface_layers.value);
int num_bottom_interface_layers = object_config.support_material_bottom_interface_layers < 0 ?
num_top_interface_layers : object_config.support_material_bottom_interface_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow_ratio;
}
this->gap_xy = object_config.support_material_xy_spacing.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_material_extruder.value == object_config.support_material_interface_extruder.value;
if (!this->can_merge_support_regions && (object_config.support_material_extruder.value == 0 || object_config.support_material_interface_extruder.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_material_extruder.value, object_config.support_material_interface_extruder.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_material_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_material_pattern;
this->with_sheath = object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_material_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_material_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.support_tree_branch_diameter_double_wall.value)) * M_PI;
}
} // namespace Slic3r

View File

@ -0,0 +1,96 @@
#ifndef slic3r_SupportParameters_hpp_
#define slic3r_SupportParameters_hpp_
#include "../libslic3r.h"
#include "../Flow.hpp"
namespace Slic3r {
class PrintObject;
enum InfillPattern : int;
namespace FFFSupport {
struct SupportParameters {
SupportParameters(const PrintObject &object);
// Both top / bottom contacts and interfaces are soluble.
bool soluble_interface;
// Support contact & interface are soluble, but support base is non-soluble.
bool soluble_interface_non_soluble_base;
// Is there at least a top contact layer extruded above support base?
bool has_top_contacts;
// Is there at least a bottom contact layer extruded below support base?
bool has_bottom_contacts;
// Number of top interface layers without counting the contact layer.
size_t num_top_interface_layers;
// Number of bottom interface layers without counting the contact layer.
size_t num_bottom_interface_layers;
// Number of top base interface layers. Zero if not soluble_interface_non_soluble_base.
size_t num_top_base_interface_layers;
// Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base.
size_t num_bottom_base_interface_layers;
bool has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; }
bool has_interfaces() const { return this->num_top_interface_layers + this->num_bottom_interface_layers > 0; }
bool has_base_interfaces() const { return this->num_top_base_interface_layers + this->num_bottom_base_interface_layers > 0; }
size_t num_top_interface_layers_only() const { return this->num_top_interface_layers - this->num_top_base_interface_layers; }
size_t num_bottom_interface_layers_only() const { return this->num_bottom_interface_layers - this->num_bottom_base_interface_layers; }
// Flow at the 1st print layer.
Flow first_layer_flow;
// Flow at the support base (neither top, nor bottom interface).
// Also flow at the raft base with the exception of raft interface and contact layers.
Flow support_material_flow;
// Flow at the top interface and contact layers.
Flow support_material_interface_flow;
// Flow at the bottom interfaces and contacts.
Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers.
Flow raft_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
// Density of the top / bottom interface and contact layers.
coordf_t interface_density;
// Density of the raft interface and contact layers.
coordf_t raft_interface_density;
// Density of the base support layers.
coordf_t support_density;
// Pattern of the sparse infill including sparse raft layers.
InfillPattern base_fill_pattern;
// Pattern of the top / bottom interface and contact layers.
InfillPattern interface_fill_pattern;
// Pattern of the raft interface and contact layers.
InfillPattern raft_interface_fill_pattern;
// Pattern of the contact layers.
InfillPattern contact_fill_pattern;
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
bool with_sheath;
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
double tree_branch_diameter_double_wall_area_scaled;
float raft_angle_1st_layer;
float raft_angle_base;
float raft_angle_interface;
// Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
};
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportParameters_hpp_ */

View File

@ -9,14 +9,15 @@
#include "TreeModelVolumes.hpp" #include "TreeModelVolumes.hpp"
#include "TreeSupport.hpp" #include "TreeSupport.hpp"
#include "BuildVolume.hpp" #include "../BuildVolume.hpp"
#include "ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "Flow.hpp" #include "../Flow.hpp"
#include "Layer.hpp" #include "../Layer.hpp"
#include "Point.hpp" #include "../Point.hpp"
#include "Print.hpp" #include "../Print.hpp"
#include "PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "Utils.hpp" #include "../Utils.hpp"
#include "../format.hpp"
#include <string_view> #include <string_view>
@ -34,6 +35,8 @@ using namespace std::literals;
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() // had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache error #define error_level_not_in_cache error
static constexpr const bool polygons_strictly_simple = false;
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object) TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{ {
const PrintConfig &print_config = print_object.print()->config(); const PrintConfig &print_config = print_object.print()->config();
@ -76,7 +79,9 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
// this->support_interface_skip_height = // this->support_interface_skip_height =
// this->support_infill_angles = // this->support_infill_angles =
this->support_roof_enable = config.support_material_interface_layers.value > 0; this->support_roof_enable = config.support_material_interface_layers.value > 0;
this->support_roof_height = config.support_material_interface_layers.value * this->layer_height; this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0;
this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0;
// this->minimum_roof_area = // this->minimum_roof_area =
// this->support_roof_angles = // this->support_roof_angles =
this->support_roof_pattern = config.support_material_interface_pattern; this->support_roof_pattern = config.support_material_interface_pattern;
@ -175,7 +180,7 @@ TreeModelVolumes::TreeModelVolumes(
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))), tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
[&](const tbb::blocked_range<size_t> &range) { [&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) 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 #endif
@ -424,7 +429,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
return (*result).get(); return (*result).get();
if (m_precalculated) { 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!"; 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) if (orig_radius == 0)
// Placable areas for radius 0 are calculated in the general collision code. // Placable areas for radius 0 are calculated in the general collision code.
@ -585,7 +590,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
if (processing_last_mesh) { if (processing_last_mesh) {
if (! dst.empty()) if (! dst.empty())
collisions = union_(collisions, dst); collisions = union_(collisions, dst);
dst = polygons_simplify(collisions, min_resolution); dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
} else } else
append(dst, std::move(collisions)); append(dst, std::move(collisions));
throw_on_cancel(); throw_on_cancel();
@ -595,21 +600,24 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
// 3) Optionally calculate placables. // 3) Optionally calculate placables.
if (calculate_placable) { if (calculate_placable) {
// Now calculate the placable areas. // Now calculate the placable areas.
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(data.idx_begin, 1), data.idx_end), tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end),
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh, [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel] min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex>& range) { (const tbb::blocked_range<LayerIndex>& range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { 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); assert(layer_idx_below >= 0);
const Polygons &current = collision_areas_offsetted[layer_idx]; const Polygons &current = collision_areas_offsetted[layer_idx];
const Polygons &below = collision_areas_offsetted[layer_idx_below]; const Polygons &below = outlines[layer_idx_below];
Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); 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]; auto &dst = data_placeable[layer_idx];
if (processing_last_mesh) { if (processing_last_mesh) {
if (! dst.empty()) if (! dst.empty())
placable = union_(placable, dst); placable = union_(placable, dst);
dst = polygons_simplify(placable, min_resolution); dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
} else } else
append(dst, placable); append(dst, placable);
throw_on_cancel(); throw_on_cancel();
@ -657,7 +665,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerP
data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify( data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify(
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)), offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
m_min_resolution)); m_min_resolution, polygons_strictly_simple));
throw_on_cancel(); throw_on_cancel();
} }
} }
@ -744,7 +752,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
ClipperLib::jtRound, m_min_resolution)); ClipperLib::jtRound, m_min_resolution));
if (task.to_model) if (task.to_model)
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel)); 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); data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
throw_on_cancel(); throw_on_cancel();
} }
@ -865,12 +873,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector<RadiusLayerPa
data[layer_idx - min_layer_bottom] = polygons_simplify( data[layer_idx - min_layer_bottom] = polygons_simplify(
// radius contains m_current_min_xy_dist_delta already if required // radius contains m_current_min_xy_dist_delta already if required
intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)), intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)),
m_min_resolution); m_min_resolution, polygons_strictly_simple);
if (! data_min.empty()) if (! data_min.empty())
data_min[layer_idx - min_layer_bottom] = data_min[layer_idx - min_layer_bottom] =
polygons_simplify( polygons_simplify(
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)), intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
m_min_resolution); m_min_resolution, polygons_strictly_simple);
throw_on_cancel(); throw_on_cancel();
} }
}); });

View File

@ -14,9 +14,9 @@
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
#include "Point.hpp" #include "../Point.hpp"
#include "Polygon.hpp" #include "../Polygon.hpp"
#include "PrintConfig.hpp" #include "../PrintConfig.hpp"
namespace Slic3r namespace Slic3r
{ {
@ -94,7 +94,9 @@ struct TreeSupportMeshGroupSettings {
bool support_roof_enable { false }; bool support_roof_enable { false };
// Support Roof Thickness // Support Roof Thickness
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests. // The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
coord_t support_roof_height { scaled<coord_t>(1.) }; coord_t support_roof_layers { 2 };
bool support_floor_enable { false };
coord_t support_floor_layers { 2 };
// Minimum Support Roof Area // Minimum Support Roof Area
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support. // Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
double minimum_roof_area { scaled<double>(scaled<double>(1.)) }; double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
@ -215,6 +217,7 @@ public:
void clear() { void clear() {
this->clear_all_but_object_collision(); this->clear_all_but_object_collision();
m_collision_cache.clear(); m_collision_cache.clear();
m_placeable_areas_cache.clear();
} }
void clear_all_but_object_collision() { void clear_all_but_object_collision() {
//m_collision_cache.clear_all_but_radius0(); //m_collision_cache.clear_all_but_radius0();
@ -223,7 +226,7 @@ public:
m_avoidance_cache_slow.clear(); m_avoidance_cache_slow.clear();
m_avoidance_cache_to_model.clear(); m_avoidance_cache_to_model.clear();
m_avoidance_cache_to_model_slow.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.clear();
m_avoidance_cache_holefree_to_model.clear(); m_avoidance_cache_holefree_to_model.clear();
m_wall_restrictions_cache.clear(); m_wall_restrictions_cache.clear();

View File

@ -11,6 +11,7 @@
#include "TreeModelVolumes.hpp" #include "TreeModelVolumes.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "Support/SupportLayer.hpp"
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
@ -39,10 +40,7 @@ namespace Slic3r
// Forward declarations // Forward declarations
class Print; class Print;
class PrintObject; class PrintObject;
class SupportGeneratorLayer;
struct SlicingParameters; struct SlicingParameters;
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace FFFTreeSupport namespace FFFTreeSupport
{ {
@ -93,6 +91,8 @@ struct AreaIncreaseSettings
struct TreeSupportSettings; 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. // C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
struct SupportElementStateBits { struct SupportElementStateBits {
SupportElementStateBits() : SupportElementStateBits() :
@ -102,6 +102,10 @@ struct SupportElementStateBits {
supports_roof(false), supports_roof(false),
can_use_safe_radius(false), can_use_safe_radius(false),
skip_ovalisation(false), skip_ovalisation(false),
#ifdef TREE_SUPPORTS_TRACK_LOST
lost(false),
verylost(false),
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false), deleted(false),
marked(false) marked(false)
{} {}
@ -136,6 +140,12 @@ struct SupportElementStateBits {
*/ */
bool skip_ovalisation : 1; 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. // Not valid anymore, to be deleted.
bool deleted : 1; bool deleted : 1;
@ -354,10 +364,6 @@ public:
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure. * \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
*/ */
size_t z_distance_bottom_layers; size_t z_distance_bottom_layers;
/*!
* \brief used for performance optimization at the support floor. Should have no impact on the resulting tree.
*/
size_t performance_interface_skip_layers;
/*! /*!
* \brief User specified angles for the support infill. * \brief User specified angles for the support infill.
*/ */

View File

@ -1,314 +0,0 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum class SupporLayerType {
Unknown = 0,
// Ratft base layer, to be printed with the support material.
RaftBase,
// Raft interface layer, to be printed with the support interface material.
RaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
BottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an BottomContact layer.
BottomInterface,
// Sparse base support layer, to be printed with a support material.
Base,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with TopContact layer.
TopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
TopContact,
// Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface.
Intermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
class SupportGeneratorLayer
{
public:
void reset() {
*this = SupportGeneratorLayer();
}
bool operator==(const SupportGeneratorLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const SupportGeneratorLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(SupportGeneratorLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { SupporLayerType::Unknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
struct SupportParameters {
SupportParameters(const PrintObject &object);
// Flow at the 1st print layer.
Flow first_layer_flow;
// Flow at the support base (neither top, nor bottom interface).
// Also flow at the raft base with the exception of raft interface and contact layers.
Flow support_material_flow;
// Flow at the top interface and contact layers.
Flow support_material_interface_flow;
// Flow at the bottom interfaces and contacts.
Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers.
Flow raft_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
// Density of the top / bottom interface and contact layers.
coordf_t interface_density;
// Density of the raft interface and contact layers.
coordf_t raft_interface_density;
// Density of the base support layers.
coordf_t support_density;
// Pattern of the sparse infill including sparse raft layers.
InfillPattern base_fill_pattern;
// Pattern of the top / bottom interface and contact layers.
InfillPattern interface_fill_pattern;
// Pattern of the raft interface and contact layers.
InfillPattern raft_interface_fill_pattern;
// Pattern of the contact layers.
InfillPattern contact_fill_pattern;
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
bool with_sheath;
float raft_angle_1st_layer;
float raft_angle_base;
float raft_angle_interface;
// Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
};
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts(
const PrintConfig &print_config,
const Layer &lower_layer,
const LayerRegion &layerm,
float fw,
Polygons &contact_polygons);
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value || m_object_config->support_material_enforce_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
SupportGeneratorLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Following objects are not owned by SupportMaterial class.
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */

View File

@ -13,6 +13,7 @@
#include "PrintBase.hpp" #include "PrintBase.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
#include "Utils.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "tbb/parallel_for.h" #include "tbb/parallel_for.h"
#include "tbb/blocked_range.h" #include "tbb/blocked_range.h"
@ -24,6 +25,10 @@
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <functional> #include <functional>
#include <limits>
#include <math.h>
#include <oneapi/tbb/concurrent_vector.h>
#include <oneapi/tbb/parallel_for.h>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -169,6 +174,69 @@ struct SliceConnection
} }
}; };
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
using PrecomputedSliceConnections = std::vector<std::vector<SliceConnection>>;
PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po)
{
PrecomputedSliceConnections result{};
for (size_t lidx = 0; lidx < po->layer_count(); lidx++) {
result.emplace_back(std::vector<SliceConnection>{});
for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) {
result[lidx].push_back(SliceConnection{});
}
}
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()), [po, &result](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *l = po->get_layer(lidx);
tbb::parallel_for(tbb::blocked_range<size_t>(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range<size_t> r2) {
for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) {
result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l);
}
});
}
});
return result;
};
float get_flow_width(const LayerRegion *region, ExtrusionRole role) float get_flow_width(const LayerRegion *region, ExtrusionRole role)
{ {
if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width();
@ -206,18 +274,38 @@ std::vector<ExtrusionLine> to_short_lines(const ExtrusionEntity *e, float length
} }
float estimate_curled_up_height( float estimate_curled_up_height(
const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) float distance, float curvature, float layer_height, float flow_width, float prev_line_curled_height, Params params)
{ {
float curled_up_height = 0.0f; float curled_up_height = 0;
if (fabs(point.distance) < 1.5 * flow_width) { if (fabs(distance) < 3.0 * flow_width) {
curled_up_height = 0.85 * prev_line_curled_height; curled_up_height = std::max(prev_line_curled_height - layer_height * 0.75f, 0.0f);
} }
if (point.distance > params.malformation_distance_factors.first * flow_width &&
point.distance < params.malformation_distance_factors.second * flow_width && point.curvature > -0.1f) {
float dist_factor = std::max(point.distance - params.malformation_distance_factors.first * flow_width, 0.01f) /
((params.malformation_distance_factors.second - params.malformation_distance_factors.first) * flow_width);
curled_up_height = layer_height * sqrt(sqrt(dist_factor)) * std::clamp(3.0f * point.curvature, 1.0f, 3.0f); if (distance > params.malformation_distance_factors.first * flow_width &&
distance < params.malformation_distance_factors.second * flow_width) {
// imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section
// and the rest will be called curling section
// float anchored_section = flow_width - point.distance;
float curling_section = distance;
// after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle
// The anchored part not, because the melted material holds to the previous layer well.
// We can assume for simplicity perfect equalization of layer height and raising part width, from which:
float swelling_radius = (layer_height + curling_section) / 2.0f;
curled_up_height += std::max(0.f, (swelling_radius - layer_height) / 2.0f);
// On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section.
// The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and
// thus shrinking force is greater. This tension will cause the curling section to curle up
if (curvature > 0.01) {
float radius = (1.0 / curvature);
float curling_t = sqrt(radius / 100);
float b = curling_t * flow_width;
float a = curling_section;
float c = sqrt(std::max(0.0f, a * a - b * b));
curled_up_height += c;
}
curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height); curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height);
} }
@ -230,15 +318,8 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary, const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
const Params &params) const Params &params)
{ {
if (entity->is_collection()) { assert(!entity->is_collection());
std::vector<ExtrusionLine> checked_lines_out; if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3);
for (const auto *e : static_cast<const ExtrusionEntityCollection *>(entity)->entities) {
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params);
checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end());
}
return checked_lines_out;
} else if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
// pure bridges are handled separately, beacuse we need to align the forward and backward direction support points // pure bridges are handled separately, beacuse we need to align the forward and backward direction support points
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
return {}; return {};
@ -312,18 +393,19 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
float line_len = (prev_point.position - curr_point.position).norm(); float line_len = (prev_point.position - curr_point.position).norm();
ExtrusionLine line_out{prev_point.position.cast<float>(), curr_point.position.cast<float>(), line_len, entity}; ExtrusionLine line_out{prev_point.position.cast<float>(), curr_point.position.cast<float>(), line_len, entity};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : prev_layer_lines.get_line(bottom_line_idx);
// correctify the distance sign using slice polygons // correctify the distance sign using slice polygons
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
curr_point.distance *= sign; curr_point.distance *= sign;
SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion;
if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { // Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
potential_cause = SupportPointCause::FloatingExtrusion; // if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
} // potential_cause = SupportPointCause::FloatingExtrusion;
// }
float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f,
params.bridge_distance / params.bridge_distance /
@ -339,7 +421,7 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
} }
} else if (curr_point.distance > flow_width * 0.8f) { } else if (curr_point.distance > flow_width * 0.8f) {
bridged_distance += line_len; bridged_distance += line_len;
line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; line_out.form_quality = bottom_line.form_quality - 0.3f;
if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) {
line_out.support_point_generated = potential_cause; line_out.support_point_generated = potential_cause;
line_out.form_quality = 0.5f; line_out.form_quality = 0.5f;
@ -349,8 +431,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
bridged_distance = 0.0f; bridged_distance = 0.0f;
} }
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance, 0.5 * (prev_point.curvature + curr_point.curvature),
nearest_prev_layer_line.curled_up_height, params); layer_region->layer()->height, flow_width, bottom_line.curled_up_height,
params);
lines_out.push_back(line_out); lines_out.push_back(line_out);
} }
@ -359,44 +442,6 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
} }
} }
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
class ObjectPart class ObjectPart
{ {
public: public:
@ -737,7 +782,10 @@ public:
} }
}; };
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params &params) std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
const PrecomputedSliceConnections &precomputed_slices_connections,
const PrintTryCancel &cancel_func,
const Params &params)
{ {
SupportPoints supp_points{}; SupportPoints supp_points{};
SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points); SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points);
@ -766,8 +814,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx); const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx];
#ifdef DETAILED_DEBUG_LOGS #ifdef DETAILED_DEBUG_LOGS
std::cout << "SLICE IDX: " << slice_idx << std::endl; std::cout << "SLICE IDX: " << slice_idx << std::endl;
@ -834,25 +882,87 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection; prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection;
next_slice_idx_to_weakest_connection.clear(); next_slice_idx_to_weakest_connection.clear();
auto get_flat_entities = [](const ExtrusionEntity *e) {
std::vector<const ExtrusionEntity *> entities;
std::vector<const ExtrusionEntity *> queue{e};
while (!queue.empty()) {
const ExtrusionEntity *next = queue.back();
queue.pop_back();
if (next->is_collection()) {
for (const ExtrusionEntity *e : static_cast<const ExtrusionEntityCollection *>(next)->entities) {
queue.push_back(e);
}
} else {
entities.push_back(next);
}
}
return entities;
};
struct EnitityToCheck
{
const ExtrusionEntity *e;
const LayerRegion *region;
size_t slice_idx;
};
std::vector<EnitityToCheck> entities_to_check;
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
for (const auto &island : slice.islands) {
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) {
if (e->role() == ExtrusionRole::BridgeInfill) {
entities_to_check.push_back({e, fill_region, slice_idx});
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const size_t &perimeter_idx : island.perimeters) {
for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) {
entities_to_check.push_back({e, perimeter_region, slice_idx});
}
}
}
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary = layer->lower_layer != nullptr ?
AABBTreeLines::LinesDistancer<Linef>{
to_unscaled_linesf(layer->lower_layer->lslices)} :
AABBTreeLines::LinesDistancer<Linef>{};
std::vector<tbb::concurrent_vector<ExtrusionLine>> unstable_lines_per_slice(layer->lslices_ex.size());
std::vector<tbb::concurrent_vector<ExtrusionLine>> ext_perim_lines_per_slice(layer->lslices_ex.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, entities_to_check.size()),
[&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice,
&ext_perim_lines_per_slice, &params](tbb::blocked_range<size_t> r) {
for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) {
const auto &e_to_check = entities_to_check[entity_idx];
for (const auto &line :
check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines,
prev_layer_boundary, params)) {
if (line.support_point_generated.has_value()) {
unstable_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
if (line.is_external_perimeter()) {
ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
}
}
});
std::vector<ExtrusionLine> current_layer_ext_perims_lines{}; std::vector<ExtrusionLine> current_layer_ext_perims_lines{};
current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size()); current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size());
// All object parts updated, and for each slice we have coresponding weakest connection. // All object parts updated, and for each slice we have coresponding weakest connection.
// We can now check each slice and its corresponding weakest connection and object part for stability. // We can now check each slice and its corresponding weakest connection and object part for stability.
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]); ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]);
SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx];
std::vector<Linef> boundary_lines;
for (const auto &link : slice.overlaps_below) {
auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]});
boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end());
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_slice_ext_perims_lines{};
current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size());
#ifdef DETAILED_DEBUG_LOGS #ifdef DETAILED_DEBUG_LOGS
weakest_conn.print_info("weakest connection info: "); weakest_conn.print_info("weakest connection info: ");
#endif #endif
@ -887,73 +997,15 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
} }
}; };
// first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they for (const auto &l : unstable_lines_per_slice[slice_idx]) {
// account for most of the curling and possible crashes, so on them we will run also global stability check assert(l.support_point_generated.has_value());
for (const auto &island : slice.islands) { reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero());
// Support bridges where needed.
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx];
if (entity->role() == ExtrusionRole::BridgeInfill) {
for (const ExtrusionLine &bridge :
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary,
params)) {
if (bridge.support_point_generated.has_value()) {
reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b),
float(-EPSILON), Vec2f::Zero());
}
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const auto &perimeter_idx : island.perimeters) {
const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx];
std::vector<ExtrusionLine> perims = check_extrusion_entity_stability(entity, perimeter_region,
prev_layer_ext_perim_lines, prev_layer_boundary,
params);
for (const ExtrusionLine &perim : perims) {
if (perim.support_point_generated.has_value()) {
reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON),
Vec2f::Zero());
}
if (perim.is_external_perimeter()) {
current_slice_ext_perims_lines.push_back(perim);
}
}
}
// DEBUG EXPORT, NOT USED NOW
// if (BR_bridge) {
// Lines scaledl;
// for (const auto &l : prev_layer_boundary.get_lines()) {
// scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// Lines perimsl;
// for (const auto &l : current_slice_ext_perims_lines) {
// perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// BoundingBox bb = get_extents(scaledl);
// bb.merge(get_extents(perimsl));
// ::Slic3r::SVG svg(debug_out_path(
// ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()),
// get_extents(scaledl));
// svg.draw(scaledl, "red", scale_(0.4));
// svg.draw(perimsl, "blue", scale_(0.25));
// svg.Close();
// }
} }
LD current_slice_lines_distancer(current_slice_ext_perims_lines); LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()});
float unchecked_dist = params.min_distance_between_support_points + 1.0f; float unchecked_dist = params.min_distance_between_support_points + 1.0f;
for (const ExtrusionLine &line : current_slice_ext_perims_lines) { for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) || if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) ||
line.len < EPSILON) { line.len < EPSILON) {
unchecked_dist += line.len; unchecked_dist += line.len;
@ -969,8 +1021,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
} }
} }
} }
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(), current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(),
current_slice_ext_perims_lines.end()); current_slice_lines_distancer.get_lines().end());
} // slice iterations } // slice iterations
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines); prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
} // layer iterations } // layer iterations
@ -1024,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params) std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{ {
auto results = check_stability(po, cancel_func, params); auto precomputed_slices_connections = precompute_slices_connections(po);
auto results = check_stability(po, precomputed_slices_connections, cancel_func, params);
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
auto [supp_points, objects] = results; auto [supp_points, objects] = results;
debug_export(supp_points, objects, "issues"); debug_export(supp_points, objects, "issues");
@ -1043,7 +1096,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
AABBTreeLines::LinesDistancer<ExtrusionLine> prev_layer_lines{}; AABBTreeLines::LinesDistancer<ExtrusionLine> prev_layer_lines{};
for (SupportLayer *l : layers) { for (SupportLayer *l : layers) {
l->malformed_lines.clear(); l->curled_lines.clear();
std::vector<ExtrusionLine> current_layer_lines; std::vector<ExtrusionLine> current_layer_lines;
for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) { for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) {
@ -1054,24 +1107,23 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
auto annotated_points = estimate_points_properties<true, true, false, false>(pol.points, prev_layer_lines, flow_width); auto annotated_points = estimate_points_properties<true, true, false, false>(pol.points, prev_layer_lines, flow_width);
for (size_t i = 0; i < annotated_points.size(); ++i) { for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i]; const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; const ExtendedPoint &b = annotated_points[i];
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(), ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
curr_point.position.cast<float>(), line_len, extrusion}; extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} :
prev_layer_lines.get_line(bottom_line_idx);
Vec2f v1 = (nearest_prev_layer_line.b - nearest_prev_layer_line.a); Vec2f v1 = (bottom_line.b - bottom_line.a);
Vec2f v2 = (curr_point.position.cast<float>() - nearest_prev_layer_line.a); Vec2f v2 = (a.position.cast<float>() - bottom_line.a);
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x()); auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
if (d > 0) { float sign = (d > 0) ? -1.0f : 1.0f;
curr_point.distance *= -1.0f;
}
line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height,
nearest_prev_layer_line.curled_up_height, params); flow_width, bottom_line.curled_up_height, params);
current_layer_lines.push_back(line_out); current_layer_lines.push_back(line_out);
} }
@ -1079,7 +1131,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > params.curling_tolerance_limit) { if (line.curled_up_height > params.curling_tolerance_limit) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height});
} }
} }
@ -1109,42 +1161,42 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
{ {
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w"); FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w");
FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w"); FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w");
#endif #endif
LD prev_layer_lines{}; LD prev_layer_lines{};
for (Layer *l : layers) { for (Layer *l : layers) {
l->malformed_lines.clear(); l->curled_lines.clear();
std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>(); std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>();
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)}; AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_layer_lines; std::vector<ExtrusionLine> current_layer_lines;
for (const LayerRegion *layer_region : l->regions()) { for (const LayerRegion *layer_region : l->regions()) {
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
if (!extrusion->role().is_external_perimeter())
if (!extrusion->role().is_external_perimeter()) continue; continue;
Points extrusion_pts; Points extrusion_pts;
extrusion->collect_points(extrusion_pts); extrusion->collect_points(extrusion_pts);
float flow_width = get_flow_width(layer_region, extrusion->role()); float flow_width = get_flow_width(layer_region, extrusion->role());
auto annotated_points = estimate_points_properties<true, false, false, false>(extrusion_pts, prev_layer_lines, flow_width, auto annotated_points = estimate_points_properties<true, true, false, false>(extrusion_pts, prev_layer_lines, flow_width,
params.bridge_distance); params.bridge_distance);
for (size_t i = 0; i < annotated_points.size(); ++i) { for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i]; const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; const ExtendedPoint &b = annotated_points[i];
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(), ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
curr_point.position.cast<float>(), line_len, extrusion}; extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} :
prev_layer_lines.get_line(bottom_line_idx);
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : // correctify the distance sign using slice polygons
1.0f; float sign = (prev_layer_boundary.distance_from_lines<true>(middle.cast<double>()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
curr_point.distance *= sign;
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature),
nearest_prev_layer_line.curled_up_height, params); l->height, flow_width, bottom_line.curled_up_height, params);
current_layer_lines.push_back(line_out); current_layer_lines.push_back(line_out);
} }
@ -1153,7 +1205,7 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > params.curling_tolerance_limit) { if (line.curled_up_height > params.curling_tolerance_limit) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height});
} }
} }
@ -1164,9 +1216,9 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
} }
} }
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
} }
#endif #endif

View File

@ -42,7 +42,7 @@ struct Params
BrimType brim_type; BrimType brim_type;
const float brim_width; const float brim_width;
const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.5, 1.1 }; const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.2, 1.1 };
const float max_curled_height_factor = 10.0f; const float max_curled_height_factor = 10.0f;
const float curling_tolerance_limit = 0.1f; const float curling_tolerance_limit = 0.1f;

View File

@ -58,8 +58,6 @@
// Enable alternative version of file_wildcards() // Enable alternative version of file_wildcards()
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1) #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)
// Enable gcode postprocess modified to allow for backward insertion of new lines
#define ENABLE_GCODE_POSTPROCESS_BACKTRACE (1 && ENABLE_2_6_0_ALPHA1)
#endif // _prusaslicer_technologies_h_ #endif // _prusaslicer_technologies_h_

View File

@ -10,3 +10,12 @@ Slic3r::Timer::~Timer()
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " << BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms"; duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms";
} }
namespace Slic3r::Timing {
void TimeLimitAlarm::report_time_exceeded() const {
BOOST_LOG_TRIVIAL(error) << "Time limit exceeded for " << m_limit_exceeded_message << ": " << m_timer.elapsed_seconds() << "s";
}
}

View File

@ -27,5 +27,66 @@ public:
~Timer(); ~Timer();
}; };
namespace Timing {
// Timing code from Catch2 unit testing library
static inline uint64_t nanoseconds_since_epoch() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
// Timing code from Catch2 unit testing library
class Timer {
public:
void start() {
m_nanoseconds = nanoseconds_since_epoch();
}
uint64_t elapsed_nanoseconds() const {
return nanoseconds_since_epoch() - m_nanoseconds;
}
uint64_t elapsed_microseconds() const {
return elapsed_nanoseconds() / 1000;
}
unsigned int elapsed_milliseconds() const {
return static_cast<unsigned int>(elapsed_microseconds()/1000);
}
double elapsed_seconds() const {
return elapsed_microseconds() / 1000000.0;
}
private:
uint64_t m_nanoseconds = 0;
};
// Emits a Boost::log error if the life time of this timing object exceeds a limit.
class TimeLimitAlarm {
public:
TimeLimitAlarm(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) :
m_time_limit_nanoseconds(time_limit_nanoseconds), m_limit_exceeded_message(limit_exceeded_message) {
m_timer.start();
}
~TimeLimitAlarm() {
auto elapsed = m_timer.elapsed_nanoseconds();
if (elapsed > m_time_limit_nanoseconds)
this->report_time_exceeded();
}
static TimeLimitAlarm new_nanos(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(time_limit_nanoseconds, limit_exceeded_message);
}
static TimeLimitAlarm new_milis(uint64_t time_limit_milis, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_milis) * 1000000l, limit_exceeded_message);
}
static TimeLimitAlarm new_seconds(uint64_t time_limit_seconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_seconds) * 1000000000l, limit_exceeded_message);
}
private:
void report_time_exceeded() const;
Timer m_timer;
uint64_t m_time_limit_nanoseconds;
std::string_view m_limit_exceeded_message;
};
} // namespace Catch
} // namespace Slic3r } // namespace Slic3r
#endif // libslic3r_Timer_hpp_ #endif // libslic3r_Timer_hpp_

View File

@ -10,11 +10,15 @@
#include <deque> #include <deque>
#include <queue> #include <queue>
#include <mutex> #include <mutex>
#include <new>
#include <utility> #include <utility>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/scalable_allocator.h>
#include <ankerl/unordered_dense.h>
#ifndef NDEBUG #ifndef NDEBUG
// #define EXPENSIVE_DEBUG_CHECKS // #define EXPENSIVE_DEBUG_CHECKS
@ -32,6 +36,13 @@
#include <boost/thread/mutex.hpp> #include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp> #include <boost/thread/lock_guard.hpp>
#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 │ ...
constexpr std::size_t hardware_destructive_interference_size = 64;
#endif
// #define SLIC3R_DEBUG_SLICE_PROCESSING // #define SLIC3R_DEBUG_SLICE_PROCESSING
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -139,7 +150,7 @@ public:
#endif #endif
}; };
using IntersectionLines = std::vector<IntersectionLine>; using IntersectionLines = std::vector<IntersectionLine, tbb::scalable_allocator<IntersectionLine>>;
enum class FacetSliceType { enum class FacetSliceType {
NoSlice = 0, NoSlice = 0,
@ -351,6 +362,21 @@ inline FacetSliceType slice_facet(
return FacetSliceType::NoSlice; return FacetSliceType::NoSlice;
} }
class LinesMutexes {
public:
std::mutex& operator()(size_t slice_id) {
ankerl::unordered_dense::hash<size_t> hash;
return m_mutexes[hash(slice_id) % m_mutexes.size()].mutex;
}
private:
struct CacheLineAlignedMutex
{
alignas(hardware_destructive_interference_size) std::mutex mutex;
};
std::array<CacheLineAlignedMutex, 64> m_mutexes;
};
template<typename TransformVertex> template<typename TransformVertex>
void slice_facet_at_zs( void slice_facet_at_zs(
// Scaled or unscaled vertices. transform_vertex_fn may scale zs. // Scaled or unscaled vertices. transform_vertex_fn may scale zs.
@ -361,7 +387,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. // 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<float> &zs, const std::vector<float> &zs,
std::vector<IntersectionLines> &lines, std::vector<IntersectionLines> &lines,
std::array<std::mutex, 64> &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)]) }; 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 +406,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) { 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); assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
size_t slice_id = it - zs.begin(); size_t slice_id = it - zs.begin();
boost::lock_guard<std::mutex> l(lines_mutex[slice_id % lines_mutex.size()]); boost::lock_guard<std::mutex> l(lines_mutex(slice_id));
lines[slice_id].emplace_back(il); lines[slice_id].emplace_back(il);
} }
} }
@ -395,8 +421,8 @@ static inline std::vector<IntersectionLines> slice_make_lines(
const std::vector<float> &zs, const std::vector<float> &zs,
const ThrowOnCancel throw_on_cancel_fn) const ThrowOnCancel throw_on_cancel_fn)
{ {
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines()); std::vector<IntersectionLines> lines(zs.size(), IntersectionLines{});
std::array<std::mutex, 64> lines_mutex; LinesMutexes lines_mutex;
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<int>(0, int(indices.size())), tbb::blocked_range<int>(0, int(indices.size())),
[&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) { [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
@ -475,7 +501,7 @@ void slice_facet_with_slabs(
const int num_edges, const int num_edges,
const std::vector<float> &zs, const std::vector<float> &zs,
SlabLines &lines, SlabLines &lines,
std::array<std::mutex, 64> &lines_mutex) LinesMutexes &lines_mutex)
{ {
const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx]; 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)] }; stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] };
@ -494,7 +520,7 @@ void slice_facet_with_slabs(
auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) { auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) {
if (reverse) if (reverse)
il.reverse(); il.reverse();
boost::lock_guard<std::mutex> l(lines_mutex[(slab_id + lines_mutex.size() / 2) % lines_mutex.size()]); boost::lock_guard<std::mutex> l(lines_mutex(slab_id));
lines.between_slices[slab_id].emplace_back(il); lines.between_slices[slab_id].emplace_back(il);
}; };
@ -530,7 +556,7 @@ void slice_facet_with_slabs(
}; };
// Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining. // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
// if (! ProjectionFromTop) il.reverse(); // if (! ProjectionFromTop) il.reverse();
boost::lock_guard<std::mutex> l(lines_mutex[line_id % lines_mutex.size()]); boost::lock_guard<std::mutex> l(lines_mutex(line_id));
lines.at_slice[line_id].emplace_back(il); lines.at_slice[line_id].emplace_back(il);
} }
} else { } else {
@ -649,7 +675,7 @@ void slice_facet_with_slabs(
if (! ProjectionFromTop) if (! ProjectionFromTop)
il.reverse(); il.reverse();
size_t line_id = it - zs.begin(); size_t line_id = it - zs.begin();
boost::lock_guard<std::mutex> l(lines_mutex[line_id % lines_mutex.size()]); boost::lock_guard<std::mutex> l(lines_mutex(line_id));
lines.at_slice[line_id].emplace_back(il); lines.at_slice[line_id].emplace_back(il);
} }
} }
@ -804,8 +830,8 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
std::pair<SlabLines, SlabLines> out; std::pair<SlabLines, SlabLines> out;
SlabLines &lines_top = out.first; SlabLines &lines_top = out.first;
SlabLines &lines_bottom = out.second; SlabLines &lines_bottom = out.second;
std::array<std::mutex, 64> lines_mutex_top; LinesMutexes lines_mutex_top;
std::array<std::mutex, 64> lines_mutex_bottom; LinesMutexes lines_mutex_bottom;
if (top) { if (top) {
lines_top.at_slice.assign(zs.size(), IntersectionLines()); lines_top.at_slice.assign(zs.size(), IntersectionLines());
@ -1540,7 +1566,7 @@ static std::vector<Polygons> make_slab_loops(
} }
// Used to cut the mesh into two halves. // Used to cut the mesh into two halves.
static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines) static ExPolygons make_expolygons_simple(IntersectionLines &lines)
{ {
ExPolygons slices; ExPolygons slices;
Polygons holes; Polygons holes;

View File

@ -106,8 +106,8 @@ enum Axis {
NUM_AXES_WITH_UNKNOWN, NUM_AXES_WITH_UNKNOWN,
}; };
template <typename T> template <typename T, typename Alloc, typename Alloc2>
inline void append(std::vector<T>& dest, const std::vector<T>& src) inline void append(std::vector<T, Alloc> &dest, const std::vector<T, Alloc2> &src)
{ {
if (dest.empty()) if (dest.empty())
dest = src; // copy dest = src; // copy
@ -115,8 +115,8 @@ inline void append(std::vector<T>& dest, const std::vector<T>& src)
dest.insert(dest.end(), src.begin(), src.end()); dest.insert(dest.end(), src.begin(), src.end());
} }
template <typename T> template <typename T, typename Alloc>
inline void append(std::vector<T>& dest, std::vector<T>&& src) inline void append(std::vector<T, Alloc> &dest, std::vector<T, Alloc> &&src)
{ {
if (dest.empty()) if (dest.empty())
dest = std::move(src); dest = std::move(src);

View File

@ -107,6 +107,7 @@
#include <boost/version.hpp> #include <boost/version.hpp>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/scalable_allocator.h>
#include <tbb/spin_mutex.h> #include <tbb/spin_mutex.h>
#include <tbb/task_group.h> #include <tbb/task_group.h>

View File

@ -291,7 +291,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
(config->opt_bool("support_material") || (config->opt_bool("support_material") ||
config->opt_int("support_material_enforce_layers") > 0); config->opt_int("support_material_enforce_layers") > 0);
for (const std::string& key : { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", for (const std::string& key : { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter",
"support_tree_branch_diameter_angle", "support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" }) "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" })
toggle_field(key, has_organic_supports); toggle_field(key, has_organic_supports);
for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder", for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",

View File

@ -615,7 +615,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
if (m_layer_height_profile_modified) { if (m_layer_height_profile_modified) {
wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit")); wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit"));
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile); const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().obj_list()->update_info_items(last_object_id); wxGetApp().obj_list()->update_info_items(last_object_id);
} }
} }
@ -2944,8 +2944,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
const double delta = direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta(); const double delta = direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta();
if (wxGetKeyState(WXK_SHIFT)) { if (wxGetKeyState(WXK_SHIFT)) {
const auto cnv_size = get_canvas_size(); const auto cnv_size = get_canvas_size();
const auto screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 }); const Vec3d screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 });
const auto mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() }); const Vec3d mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() });
const Vec3d displacement = mouse_3d_pos - screen_center_3d_pos; const Vec3d displacement = mouse_3d_pos - screen_center_3d_pos;
wxGetApp().plater()->get_camera().translate_world(displacement); wxGetApp().plater()->get_camera().translate_world(displacement);
const double origin_zoom = wxGetApp().plater()->get_camera().get_zoom(); const double origin_zoom = wxGetApp().plater()->get_camera().get_zoom();
@ -3190,9 +3190,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_canvas->SetFocus(); m_canvas->SetFocus();
if (evt.Entering()) { if (evt.Entering()) {
if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown()) if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown()) {
// reset dragging state if the user released the mouse button outside the 3D scene // ensure to stop layers editing if enabled
m_mouse.dragging = false; if (m_layers_editing.state != LayersEditing::Unknown) {
m_layers_editing.state = LayersEditing::Unknown;
_stop_timer();
m_layers_editing.accept_changes(*this);
}
mouse_up_cleanup();
}
//#if defined(__WXMSW__) || defined(__linux__) //#if defined(__WXMSW__) || defined(__linux__)
// // On Windows and Linux needs focus in order to catch key events // // On Windows and Linux needs focus in order to catch key events
@ -3636,6 +3642,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
} }
// Fixes flying instances // Fixes flying instances
std::set<int> obj_idx_for_update_info_items;
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* m = m_model->objects[i.first];
const double shift_z = m->get_instance_min_z(i.second); const double shift_z = m->get_instance_min_z(i.second);
@ -3644,8 +3651,11 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); m->translate_instance(i.second, shift);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); obj_idx_for_update_info_items.emplace(i.first);
} }
//update sinking information in ObjectList
for (int id : obj_idx_for_update_info_items)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(id));
// if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running // if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running
// similar to void Plater::priv::selection_changed() // similar to void Plater::priv::selection_changed()
@ -3723,6 +3733,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
} }
// Fixes sinking/flying instances // Fixes sinking/flying instances
std::set<int> obj_idx_for_update_info_items;
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* m = m_model->objects[i.first];
const double shift_z = m->get_instance_min_z(i.second); const double shift_z = m->get_instance_min_z(i.second);
@ -3733,8 +3744,11 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
m->translate_instance(i.second, shift); m->translate_instance(i.second, shift);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); obj_idx_for_update_info_items.emplace(i.first);
} }
//update sinking information in ObjectList
for (int id : obj_idx_for_update_info_items)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(id));
if (!done.empty()) if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
@ -3792,6 +3806,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
} }
// Fixes sinking/flying instances // Fixes sinking/flying instances
std::set<int> obj_idx_for_update_info_items;
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* m = m_model->objects[i.first];
const double shift_z = m->get_instance_min_z(i.second); const double shift_z = m->get_instance_min_z(i.second);
@ -3801,8 +3816,11 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); m->translate_instance(i.second, shift);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); obj_idx_for_update_info_items.emplace(i.first);
} }
//update sinking information in ObjectList
for (int id : obj_idx_for_update_info_items)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(id));
if (!done.empty()) if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
@ -3855,6 +3873,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
} }
// Fixes sinking/flying instances // Fixes sinking/flying instances
std::set<int> obj_idx_for_update_info_items;
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* m = m_model->objects[i.first];
double shift_z = m->get_instance_min_z(i.second); double shift_z = m->get_instance_min_z(i.second);
@ -3864,8 +3883,11 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); m->translate_instance(i.second, shift);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); obj_idx_for_update_info_items.emplace(i.first);
} }
//update sinking information in ObjectList
for (int id : obj_idx_for_update_info_items)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(id));
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
@ -3917,6 +3939,7 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type)
} }
// Fixes sinking/flying instances // Fixes sinking/flying instances
std::set<int> obj_idx_for_update_info_items;
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* m = m_model->objects[i.first];
double shift_z = m->get_instance_min_z(i.second); double shift_z = m->get_instance_min_z(i.second);
@ -3926,8 +3949,11 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type)
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); m->translate_instance(i.second, shift);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); obj_idx_for_update_info_items.emplace(i.first);
} }
//update sinking information in ObjectList
for (int id : obj_idx_for_update_info_items)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(id));
post_event(SimpleEvent(EVT_GLCANVAS_RESET_SKEW)); post_event(SimpleEvent(EVT_GLCANVAS_RESET_SKEW));
@ -6239,10 +6265,10 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
m_layers_editing.last_action = m_layers_editing.last_action =
evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) : evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) :
(evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE); (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE);
}
m_layers_editing.adjust_layer_height_profile(); m_layers_editing.adjust_layer_height_profile();
_refresh_if_shown_on_screen(); _refresh_if_shown_on_screen();
}
// Automatic action on mouse down with the same coordinate. // Automatic action on mouse down with the same coordinate.
_start_timer(); _start_timer();
@ -6268,7 +6294,8 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z)
Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
{ {
return mouse_ray(mouse_pos).intersect_plane(0.0); const Linef3 ray = mouse_ray(mouse_pos);
return (std::abs(ray.unit_vector().z()) < EPSILON) ? ray.a : ray.intersect_plane(0.0);
} }
void GLCanvas3D::_start_timer() void GLCanvas3D::_start_timer()

View File

@ -14,7 +14,7 @@ namespace Slic3r {
class TriangleMesh; class TriangleMesh;
class Polygon; class Polygon;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
class BuildVolume; class BuildVolume;
namespace GUI { namespace GUI {

View File

@ -3381,6 +3381,9 @@ void GUI_App::on_version_read(wxCommandEvent& evt)
? _u8L("Check for application update has failed.") ? _u8L("Check for application update has failed.")
: Slic3r::format(_u8L("You are currently running the latest released version %1%."), evt.GetString()); : Slic3r::format(_u8L("You are currently running the latest released version %1%."), evt.GetString());
if (*Semver::parse(SLIC3R_VERSION) > *Semver::parse(into_u8(evt.GetString())))
text = Slic3r::format(_u8L("There are no new released versions online. The latest release version is %1%."), evt.GetString());
this->plater_->get_notification_manager()->push_version_notification(NotificationType::NoNewReleaseAvailable this->plater_->get_notification_manager()->push_version_notification(NotificationType::NoNewReleaseAvailable
, NotificationManager::NotificationLevel::RegularNotificationLevel , NotificationManager::NotificationLevel::RegularNotificationLevel
, text , text

View File

@ -743,6 +743,14 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
} }
evt.Check(check); evt.Check(check);
// disable the menu item if SLA supports or Hollow gizmos are active
if (printer_technology() == ptSLA) {
const auto gizmo_type = plater()->canvas3D()->get_gizmos_manager().get_current_type();
const bool enable = gizmo_type != GLGizmosManager::SlaSupports && gizmo_type != GLGizmosManager::Hollow;
evt.Enable(enable);
}
plater()->set_current_canvas_as_dirty(); plater()->set_current_canvas_as_dirty();
}, menu_item_printable->GetId()); }, menu_item_printable->GetId());

View File

@ -85,6 +85,17 @@ ObjectList::ObjectList(wxWindow* parent) :
// describe control behavior // describe control behavior
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) { Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
// do not allow to change selection while the sla support gizmo is in editing mode
const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true)) {
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.size() > 1 || event.GetItem() != m_last_selected_item) {
select_item(m_last_selected_item);
return;
}
}
// detect the current mouse position here, to pass it to list_manipulation() method // detect the current mouse position here, to pass it to list_manipulation() method
// if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation() // if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation()
// see: https://github.com/prusa3d/PrusaSlicer/issues/3802 // see: https://github.com/prusa3d/PrusaSlicer/issues/3802
@ -2983,21 +2994,19 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
wxGetApp().notification_manager()->push_updated_item_info_notification(type); wxGetApp().notification_manager()->push_updated_item_info_notification(type);
} }
else if (shows && ! should_show) { else if (shows && ! should_show) {
if (!selections) if (!selections && IsSelected(item)) {
Unselect(item); Unselect(item);
m_objects_model->Delete(item);
if (selections) {
if (selections->Index(item) != wxNOT_FOUND) {
// If info item was deleted from the list,
// it's need to be deleted from selection array, if it was there
selections->Remove(item);
// Select item_obj, if info_item doesn't exist for item anymore, but was selected
if (selections->Index(item_obj) == wxNOT_FOUND)
selections->Add(item_obj);
}
}
else
Select(item_obj); Select(item_obj);
}
m_objects_model->Delete(item);
if (selections && selections->Index(item) != wxNOT_FOUND) {
// If info item was deleted from the list,
// it's need to be deleted from selection array, if it was there
selections->Remove(item);
// Select item_obj, if info_item doesn't exist for item anymore, but was selected
if (selections->Index(item_obj) == wxNOT_FOUND)
selections->Add(item_obj);
}
} }
} }
} }
@ -4960,6 +4969,11 @@ void ObjectList::update_printable_state(int obj_idx, int instance_idx)
void ObjectList::toggle_printable_state() void ObjectList::toggle_printable_state()
{ {
// do not allow to toggle the printable state while the sla support gizmo is in editing mode
const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true))
return;
wxDataViewItemArray sels; wxDataViewItemArray sels;
GetSelections(sels); GetSelections(sels);
if (sels.IsEmpty()) if (sels.IsEmpty())

View File

@ -434,18 +434,15 @@ void GLGizmoFdmSupports::apply_data_from_backend()
mesh_id++; mesh_id++;
auto selector = selectors.find(mv->id().id); auto selector = selectors.find(mv->id().id);
if (selector != selectors.end()) { if (selector != selectors.end()) {
mv->supported_facets.set(selector->second.selector); m_triangle_selectors[mesh_id]->deserialize(selector->second.selector.serialize(), true);
m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true);
m_triangle_selectors[mesh_id]->request_update_render_data(); m_triangle_selectors[mesh_id]->request_update_render_data();
} }
} }
} }
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
m_parent.set_as_dirty();
} }
this->waiting_for_autogenerated_supports = false;
} }
this->waiting_for_autogenerated_supports = false;
update_model_object();
} }
void GLGizmoFdmSupports::update_model_object() const void GLGizmoFdmSupports::update_model_object() const

View File

@ -24,14 +24,8 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filen
bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
{ {
if (mouse_event.Moving()) {
// only for sure
m_mouse_left_down = false;
return false;
}
if (mouse_event.LeftDown()) { if (mouse_event.LeftDown()) {
if (m_hover_id != -1) { if (m_hover_id != -1) {
m_mouse_left_down = true;
Selection &selection = m_parent.get_selection(); Selection &selection = m_parent.get_selection();
if (selection.is_single_full_instance()) { if (selection.is_single_full_instance()) {
// Rotate the object so the normal points downward: // Rotate the object so the normal points downward:
@ -42,16 +36,8 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
return true; return true;
} }
} }
else if (mouse_event.LeftUp()) { else if (mouse_event.LeftUp())
if (m_mouse_left_down) { return m_hover_id != -1;
// responsible for mouse left up after selecting plane
m_mouse_left_down = false;
return true;
}
}
else if (mouse_event.Leaving())
m_mouse_left_down = false;
return false; return false;
} }

View File

@ -37,7 +37,6 @@ private:
std::vector<PlaneData> m_planes; std::vector<PlaneData> m_planes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_planes_casters; std::vector<std::shared_ptr<SceneRaycasterItem>> m_planes_casters;
bool m_mouse_left_down = false; // for detection left_up of this gizmo
const ModelObject* m_old_model_object = nullptr; const ModelObject* m_old_model_object = nullptr;
int m_old_instance_id{ -1 }; int m_old_instance_id{ -1 };

View File

@ -820,6 +820,12 @@ bool GLGizmoHollow::on_is_activable() const
if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0)
return false; return false;
// Check that none of the selected volumes is marked as non-pritable.
for (const auto& idx : list) {
if (!selection.get_volume(idx)->printable)
return false;
}
return true; return true;
} }

View File

@ -65,9 +65,13 @@ void GLGizmoSlaSupports::data_changed(bool is_serializing)
// If we triggered autogeneration before, check backend and fetch results if they are there // If we triggered autogeneration before, check backend and fetch results if they are there
if (mo) { if (mo) {
m_c->instances_hider()->set_hide_full_scene(true); m_c->instances_hider()->set_hide_full_scene(true);
const SLAPrintObject* po = m_c->selection_info()->print_object();
int last_comp_step = slaposCount;
const int required_step = get_min_sla_print_object_step(); const int required_step = get_min_sla_print_object_step();
auto last_comp_step = static_cast<int>(po->last_completed_step()); const SLAPrintObject* po = m_c->selection_info()->print_object();
if (po != nullptr)
last_comp_step = static_cast<int>(po->last_completed_step());
if (last_comp_step == slaposCount) if (last_comp_step == slaposCount)
last_comp_step = -1; last_comp_step = -1;
@ -123,6 +127,8 @@ void GLGizmoSlaSupports::on_render()
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
show_sla_supports(!m_editing_mode);
render_volumes(); render_volumes();
render_points(selection); render_points(selection);
@ -791,6 +797,12 @@ bool GLGizmoSlaSupports::on_is_activable() const
if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0)
return false; return false;
// Check that none of the selected volumes is marked as non-pritable.
for (const auto& idx : list) {
if (!selection.get_volume(idx)->printable)
return false;
}
return true; return true;
} }

View File

@ -261,11 +261,12 @@ void Raycaster::on_update()
// For sla printers we use the mesh generated by the backend // For sla printers we use the mesh generated by the backend
std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr; std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr;
const SLAPrintObject* po = get_pool()->selection_info()->print_object(); const SLAPrintObject* po = get_pool()->selection_info()->print_object();
if (po) if (po != nullptr)
preview_mesh_ptr = po->get_mesh_to_print(); preview_mesh_ptr = po->get_mesh_to_print();
else
preview_mesh_ptr.reset();
if (preview_mesh_ptr) m_sla_mesh_cache = (preview_mesh_ptr != nullptr) ? TriangleMesh{ *preview_mesh_ptr } : TriangleMesh();
m_sla_mesh_cache = TriangleMesh{*preview_mesh_ptr};
if (!m_sla_mesh_cache.empty()) { if (!m_sla_mesh_cache.empty()) {
m_sla_mesh_cache.transform(po->trafo().inverse()); m_sla_mesh_cache.transform(po->trafo().inverse());

Some files were not shown because too many files have changed in this diff Show More