mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-10-02 06:03:14 +08:00
Merge branch 'master' into fs_dir_per_glyph
This commit is contained in:
commit
5a517e71de
@ -406,6 +406,7 @@ endif()
|
||||
set(TBB_DEBUG 1)
|
||||
find_package(TBB REQUIRED)
|
||||
slic3r_remap_configs(TBB::tbb RelWithDebInfo Release)
|
||||
slic3r_remap_configs(TBB::tbbmalloc RelWithDebInfo Release)
|
||||
# include_directories(${TBB_INCLUDE_DIRS})
|
||||
# add_definitions(${TBB_DEFINITIONS})
|
||||
# if(MSVC)
|
||||
@ -545,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES})
|
||||
endforeach()
|
||||
|
||||
find_package(NLopt 1.4 REQUIRED)
|
||||
slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release)
|
||||
|
||||
if(SLIC3R_STATIC)
|
||||
set(OPENVDB_USE_STATIC_LIBS ON)
|
||||
|
@ -8,3 +8,5 @@ add_library(clipper STATIC
|
||||
clipper_z.cpp
|
||||
clipper_z.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(clipper TBB::tbb TBB::tbbmalloc)
|
||||
|
@ -73,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path
|
||||
#define TOLERANCE (1.0e-20)
|
||||
#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
|
||||
|
||||
// Output polygon.
|
||||
struct OutRec {
|
||||
int Idx;
|
||||
bool IsHole;
|
||||
bool IsOpen;
|
||||
//The 'FirstLeft' field points to another OutRec that contains or is the
|
||||
//'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
|
||||
//parsed left from the current edge (owning OutRec) until the owner OutRec
|
||||
//is found. This field simplifies sorting the polygons into a tree structure
|
||||
//which reflects the parent/child relationships of all polygons.
|
||||
//This field should be renamed Parent, and will be later.
|
||||
OutRec *FirstLeft;
|
||||
// Used only by void Clipper::BuildResult2(PolyTree& polytree)
|
||||
PolyNode *PolyNd;
|
||||
// Linked list of output points, dynamically allocated.
|
||||
OutPt *Pts;
|
||||
OutPt *BottomPt;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline IntPoint IntPoint2d(cInt x, cInt y)
|
||||
@ -131,7 +112,7 @@ int PolyTree::Total() const
|
||||
void PolyNode::AddChild(PolyNode& child)
|
||||
{
|
||||
unsigned cnt = (unsigned)Childs.size();
|
||||
Childs.push_back(&child);
|
||||
Childs.emplace_back(&child);
|
||||
child.Parent = this;
|
||||
child.Index = cnt;
|
||||
}
|
||||
@ -693,7 +674,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
|
||||
locMin.RightBound = E;
|
||||
E->WindDelta = 0;
|
||||
Result = ProcessBound(E, NextIsForward);
|
||||
m_MinimaList.push_back(locMin);
|
||||
m_MinimaList.emplace_back(locMin);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
@ -784,7 +765,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
|
||||
return false;
|
||||
|
||||
// Allocate a new edge array.
|
||||
std::vector<TEdge> edges(highI + 1);
|
||||
Edges edges(highI + 1);
|
||||
// Fill in the edge array.
|
||||
bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data());
|
||||
if (result)
|
||||
@ -915,7 +896,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
|
||||
E->NextInLML = E->Next;
|
||||
E = E->Next;
|
||||
}
|
||||
m_MinimaList.push_back(locMin);
|
||||
m_MinimaList.emplace_back(locMin);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -968,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
|
||||
locMin.LeftBound = 0;
|
||||
else if (locMin.RightBound->OutIdx == Skip)
|
||||
locMin.RightBound = 0;
|
||||
m_MinimaList.push_back(locMin);
|
||||
m_MinimaList.emplace_back(locMin);
|
||||
if (!leftBoundIsForward) E = E2;
|
||||
}
|
||||
return true;
|
||||
@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds()
|
||||
Clipper::Clipper(int initOptions) :
|
||||
ClipperBase(),
|
||||
m_OutPtsFree(nullptr),
|
||||
m_OutPtsChunkSize(32),
|
||||
m_OutPtsChunkLast(32),
|
||||
m_OutPtsChunkLast(m_OutPtsChunkSize),
|
||||
m_ActiveEdges(nullptr),
|
||||
m_SortedEdges(nullptr)
|
||||
{
|
||||
@ -1079,7 +1059,7 @@ Clipper::Clipper(int initOptions) :
|
||||
void Clipper::Reset()
|
||||
{
|
||||
ClipperBase::Reset();
|
||||
m_Scanbeam = std::priority_queue<cInt>();
|
||||
m_Scanbeam = std::priority_queue<cInt, cInts>{};
|
||||
m_Maxima.clear();
|
||||
m_ActiveEdges = 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: The area is calculated with floats, it may not be numerically stable!
|
||||
{
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
|
||||
ReversePolyPtLinks(outRec->Pts);
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0))
|
||||
ReversePolyPtLinks(outRec.Pts);
|
||||
}
|
||||
|
||||
JoinCommonEdges();
|
||||
|
||||
//unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
|
||||
{
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
if (outRec->Pts) {
|
||||
if (outRec->IsOpen)
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
if (outRec.Pts) {
|
||||
if (outRec.IsOpen)
|
||||
// Removes duplicate points.
|
||||
FixupOutPolyline(*outRec);
|
||||
FixupOutPolyline(outRec);
|
||||
else
|
||||
// Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex.
|
||||
FixupOutPolygon(*outRec);
|
||||
FixupOutPolygon(outRec);
|
||||
}
|
||||
}
|
||||
// For each polygon, search for exactly duplicate non-successive points.
|
||||
@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt()
|
||||
m_OutPtsFree = pt->Next;
|
||||
} else if (m_OutPtsChunkLast < m_OutPtsChunkSize) {
|
||||
// Get a point from the last chunk.
|
||||
pt = m_OutPts.back() + (m_OutPtsChunkLast ++);
|
||||
pt = &m_OutPts.back()[m_OutPtsChunkLast ++];
|
||||
} else {
|
||||
// The last chunk is full. Allocate a new one.
|
||||
m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]);
|
||||
m_OutPts.emplace_back();
|
||||
m_OutPtsChunkLast = 1;
|
||||
pt = m_OutPts.back();
|
||||
pt = &m_OutPts.back().front();
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
void Clipper::DisposeAllOutRecs()
|
||||
{
|
||||
for (OutPt *pts : m_OutPts)
|
||||
delete[] pts;
|
||||
for (OutRec *rec : m_PolyOuts)
|
||||
delete rec;
|
||||
m_OutPts.clear();
|
||||
m_OutPtsFree = nullptr;
|
||||
m_OutPtsChunkLast = m_OutPtsChunkSize;
|
||||
@ -1832,7 +1808,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const
|
||||
void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
|
||||
{
|
||||
bool IsHole = false;
|
||||
TEdge *e2 = e->PrevInAEL;
|
||||
@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const
|
||||
{
|
||||
IsHole = !IsHole;
|
||||
if (! outrec->FirstLeft)
|
||||
outrec->FirstLeft = m_PolyOuts[e2->OutIdx];
|
||||
outrec->FirstLeft = &m_PolyOuts[e2->OutIdx];
|
||||
}
|
||||
e2 = e2->PrevInAEL;
|
||||
}
|
||||
@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2)
|
||||
|
||||
OutRec* Clipper::GetOutRec(int Idx)
|
||||
{
|
||||
OutRec* outrec = m_PolyOuts[Idx];
|
||||
while (outrec != m_PolyOuts[outrec->Idx])
|
||||
outrec = m_PolyOuts[outrec->Idx];
|
||||
OutRec* outrec = &m_PolyOuts[Idx];
|
||||
while (outrec != &m_PolyOuts[outrec->Idx])
|
||||
outrec = &m_PolyOuts[outrec->Idx];
|
||||
return outrec;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const
|
||||
void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
|
||||
{
|
||||
//get the start and ends of both output polygons ...
|
||||
OutRec *outRec1 = m_PolyOuts[e1->OutIdx];
|
||||
OutRec *outRec2 = m_PolyOuts[e2->OutIdx];
|
||||
OutRec *outRec1 = &m_PolyOuts[e1->OutIdx];
|
||||
OutRec *outRec2 = &m_PolyOuts[e2->OutIdx];
|
||||
|
||||
OutRec *holeStateRec;
|
||||
if (Param1RightOfParam2(outRec1, outRec2))
|
||||
@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const
|
||||
|
||||
OutRec* Clipper::CreateOutRec()
|
||||
{
|
||||
OutRec* result = new OutRec;
|
||||
result->IsHole = false;
|
||||
result->IsOpen = false;
|
||||
result->FirstLeft = 0;
|
||||
result->Pts = 0;
|
||||
result->BottomPt = 0;
|
||||
result->PolyNd = 0;
|
||||
m_PolyOuts.push_back(result);
|
||||
result->Idx = (int)m_PolyOuts.size()-1;
|
||||
return result;
|
||||
m_PolyOuts.emplace_back();
|
||||
OutRec &result = m_PolyOuts.back();
|
||||
result.IsHole = false;
|
||||
result.IsOpen = false;
|
||||
result.FirstLeft = 0;
|
||||
result.Pts = 0;
|
||||
result.BottomPt = 0;
|
||||
result.PolyNd = 0;
|
||||
result.Idx = (int)m_PolyOuts.size()-1;
|
||||
return &result;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
||||
return newOp;
|
||||
} else
|
||||
{
|
||||
OutRec *outRec = m_PolyOuts[e->OutIdx];
|
||||
OutRec *outRec = &m_PolyOuts[e->OutIdx];
|
||||
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
|
||||
OutPt* op = outRec->Pts;
|
||||
|
||||
@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
||||
|
||||
OutPt* Clipper::GetLastOutPt(TEdge *e)
|
||||
{
|
||||
OutRec *outRec = m_PolyOuts[e->OutIdx];
|
||||
OutRec *outRec = &m_PolyOuts[e->OutIdx];
|
||||
if (e->Side == esLeft)
|
||||
return outRec->Pts;
|
||||
else
|
||||
@ -2216,7 +2192,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
|
||||
{
|
||||
Direction dir;
|
||||
cInt horzLeft, horzRight;
|
||||
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen);
|
||||
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx].IsOpen);
|
||||
|
||||
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
|
||||
|
||||
@ -2226,8 +2202,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
|
||||
if (!eLastHorz->NextInLML)
|
||||
eMaxPair = GetMaximaPair(eLastHorz);
|
||||
|
||||
std::vector<cInt>::const_iterator maxIt;
|
||||
std::vector<cInt>::const_reverse_iterator maxRit;
|
||||
cInts::const_iterator maxIt;
|
||||
cInts::const_reverse_iterator maxRit;
|
||||
if (!m_Maxima.empty())
|
||||
{
|
||||
//get the first maxima in range (X) ...
|
||||
@ -2600,7 +2576,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||
|
||||
if(IsMaximaEdge)
|
||||
{
|
||||
if (m_StrictSimple) m_Maxima.push_back(e->Top.x());
|
||||
if (m_StrictSimple) m_Maxima.emplace_back(e->Top.x());
|
||||
TEdge* ePrev = e->PrevInAEL;
|
||||
DoMaxima(e);
|
||||
if( !ePrev ) e = m_ActiveEdges;
|
||||
@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts)
|
||||
void Clipper::BuildResult(Paths &polys)
|
||||
{
|
||||
polys.reserve(m_PolyOuts.size());
|
||||
for (OutRec* outRec : m_PolyOuts)
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
{
|
||||
assert(! outRec->IsOpen);
|
||||
if (!outRec->Pts) continue;
|
||||
assert(! outRec.IsOpen);
|
||||
if (!outRec.Pts) continue;
|
||||
Path pg;
|
||||
OutPt* p = outRec->Pts->Prev;
|
||||
OutPt* p = outRec.Pts->Prev;
|
||||
int cnt = PointCount(p);
|
||||
if (cnt < 2) continue;
|
||||
pg.reserve(cnt);
|
||||
@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree)
|
||||
polytree.Clear();
|
||||
polytree.AllNodes.reserve(m_PolyOuts.size());
|
||||
//add each output polygon/contour to polytree ...
|
||||
for (OutRec* outRec : m_PolyOuts)
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
{
|
||||
int cnt = PointCount(outRec->Pts);
|
||||
if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3))
|
||||
int cnt = PointCount(outRec.Pts);
|
||||
if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3))
|
||||
// Ignore an invalid output loop or a polyline.
|
||||
continue;
|
||||
|
||||
//skip OutRecs that (a) contain outermost polygons or
|
||||
//(b) already have the correct owner/child linkage ...
|
||||
if (outRec->FirstLeft &&
|
||||
(outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) {
|
||||
OutRec* orfl = outRec->FirstLeft;
|
||||
while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts))
|
||||
if (outRec.FirstLeft &&
|
||||
(outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) {
|
||||
OutRec* orfl = outRec.FirstLeft;
|
||||
while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts))
|
||||
orfl = orfl->FirstLeft;
|
||||
outRec->FirstLeft = orfl;
|
||||
outRec.FirstLeft = orfl;
|
||||
}
|
||||
|
||||
//nb: polytree takes ownership of all the PolyNodes
|
||||
polytree.AllNodes.emplace_back(PolyNode());
|
||||
PolyNode* pn = &polytree.AllNodes.back();
|
||||
outRec->PolyNd = pn;
|
||||
outRec.PolyNd = pn;
|
||||
pn->Parent = 0;
|
||||
pn->Index = 0;
|
||||
pn->Contour.reserve(cnt);
|
||||
OutPt *op = outRec->Pts->Prev;
|
||||
OutPt *op = outRec.Pts->Prev;
|
||||
for (int j = 0; j < cnt; j++)
|
||||
{
|
||||
pn->Contour.emplace_back(op->Pt);
|
||||
@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree)
|
||||
|
||||
//fixup PolyNode links etc ...
|
||||
polytree.Childs.reserve(m_PolyOuts.size());
|
||||
for (OutRec* outRec : m_PolyOuts)
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
{
|
||||
if (!outRec->PolyNd) continue;
|
||||
if (outRec->IsOpen)
|
||||
if (!outRec.PolyNd) continue;
|
||||
if (outRec.IsOpen)
|
||||
{
|
||||
outRec->PolyNd->m_IsOpen = true;
|
||||
polytree.AddChild(*outRec->PolyNd);
|
||||
outRec.PolyNd->m_IsOpen = true;
|
||||
polytree.AddChild(*outRec.PolyNd);
|
||||
}
|
||||
else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd)
|
||||
outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);
|
||||
else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd)
|
||||
outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd);
|
||||
else
|
||||
polytree.AddChild(*outRec->PolyNd);
|
||||
polytree.AddChild(*outRec.PolyNd);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -3193,26 +3169,26 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// This is potentially very expensive! O(n^3)!
|
||||
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const
|
||||
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
|
||||
{
|
||||
//tests if NewOutRec contains the polygon before reassigning FirstLeft
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
{
|
||||
if (!outRec->Pts || !outRec->FirstLeft) continue;
|
||||
OutRec* firstLeft = outRec->FirstLeft;
|
||||
if (!outRec.Pts || !outRec.FirstLeft) continue;
|
||||
OutRec* firstLeft = outRec.FirstLeft;
|
||||
// Skip empty polygons.
|
||||
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
|
||||
if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))
|
||||
outRec->FirstLeft = NewOutRec;
|
||||
if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts))
|
||||
outRec.FirstLeft = NewOutRec;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const
|
||||
void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec)
|
||||
{
|
||||
//reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec;
|
||||
for (OutRec &outRec : m_PolyOuts)
|
||||
if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec;
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges()
|
||||
if (m_UsingPolyTree)
|
||||
for (size_t j = 0; j < m_PolyOuts.size() - 1; j++)
|
||||
{
|
||||
OutRec* oRec = m_PolyOuts[j];
|
||||
OutRec* firstLeft = oRec->FirstLeft;
|
||||
OutRec &oRec = m_PolyOuts[j];
|
||||
OutRec* firstLeft = oRec.FirstLeft;
|
||||
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
|
||||
if (!oRec->Pts || firstLeft != outRec1 ||
|
||||
oRec->IsHole == outRec1->IsHole) continue;
|
||||
if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2))
|
||||
oRec->FirstLeft = outRec2;
|
||||
if (!oRec.Pts || firstLeft != outRec1 ||
|
||||
oRec.IsHole == outRec1->IsHole) continue;
|
||||
if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2))
|
||||
oRec.FirstLeft = outRec2;
|
||||
}
|
||||
|
||||
if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
|
||||
@ -3373,7 +3349,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
|
||||
break;
|
||||
}
|
||||
newNode->Contour.reserve(highI + 1);
|
||||
newNode->Contour.push_back(path[0]);
|
||||
newNode->Contour.emplace_back(path[0]);
|
||||
int j = 0, k = 0;
|
||||
for (int i = 1; i <= highI; i++) {
|
||||
bool same = false;
|
||||
@ -3386,7 +3362,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
|
||||
if (same)
|
||||
continue;
|
||||
j++;
|
||||
newNode->Contour.push_back(path[i]);
|
||||
newNode->Contour.emplace_back(path[i]);
|
||||
if (path[i].y() > newNode->Contour[k].y() ||
|
||||
(path[i].y() == newNode->Contour[k].y() &&
|
||||
path[i].x() < newNode->Contour[k].x())) k = j;
|
||||
@ -3514,7 +3490,7 @@ void ClipperOffset::DoOffset(double delta)
|
||||
{
|
||||
PolyNode& node = *m_polyNodes.Childs[i];
|
||||
if (node.m_endtype == etClosedPolygon)
|
||||
m_destPolys.push_back(node.Contour);
|
||||
m_destPolys.emplace_back(node.Contour);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -3556,7 +3532,7 @@ void ClipperOffset::DoOffset(double delta)
|
||||
double X = 1.0, Y = 0.0;
|
||||
for (cInt j = 1; j <= steps; j++)
|
||||
{
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[0].x() + X * delta),
|
||||
Round(m_srcPoly[0].y() + Y * delta)));
|
||||
double X2 = X;
|
||||
@ -3569,7 +3545,7 @@ void ClipperOffset::DoOffset(double delta)
|
||||
double X = -1.0, Y = -1.0;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[0].x() + X * delta),
|
||||
Round(m_srcPoly[0].y() + Y * delta)));
|
||||
if (X < 0) X = 1;
|
||||
@ -3577,32 +3553,32 @@ void ClipperOffset::DoOffset(double delta)
|
||||
else X = -1;
|
||||
}
|
||||
}
|
||||
m_destPolys.push_back(m_destPoly);
|
||||
m_destPolys.emplace_back(m_destPoly);
|
||||
continue;
|
||||
}
|
||||
//build m_normals ...
|
||||
m_normals.clear();
|
||||
m_normals.reserve(len);
|
||||
for (int j = 0; j < len - 1; ++j)
|
||||
m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
|
||||
m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
|
||||
if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)
|
||||
m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
|
||||
m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
|
||||
else
|
||||
m_normals.push_back(DoublePoint(m_normals[len - 2]));
|
||||
m_normals.emplace_back(DoublePoint(m_normals[len - 2]));
|
||||
|
||||
if (node.m_endtype == etClosedPolygon)
|
||||
{
|
||||
int k = len - 1;
|
||||
for (int j = 0; j < len; ++j)
|
||||
OffsetPoint(j, k, node.m_jointype);
|
||||
m_destPolys.push_back(m_destPoly);
|
||||
m_destPolys.emplace_back(m_destPoly);
|
||||
}
|
||||
else if (node.m_endtype == etClosedLine)
|
||||
{
|
||||
int k = len - 1;
|
||||
for (int j = 0; j < len; ++j)
|
||||
OffsetPoint(j, k, node.m_jointype);
|
||||
m_destPolys.push_back(m_destPoly);
|
||||
m_destPolys.emplace_back(m_destPoly);
|
||||
m_destPoly.clear();
|
||||
//re-build m_normals ...
|
||||
DoublePoint n = m_normals[len -1];
|
||||
@ -3612,7 +3588,7 @@ void ClipperOffset::DoOffset(double delta)
|
||||
k = 0;
|
||||
for (int j = len - 1; j >= 0; j--)
|
||||
OffsetPoint(j, k, node.m_jointype);
|
||||
m_destPolys.push_back(m_destPoly);
|
||||
m_destPolys.emplace_back(m_destPoly);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3625,9 +3601,9 @@ void ClipperOffset::DoOffset(double delta)
|
||||
{
|
||||
int j = len - 1;
|
||||
pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta));
|
||||
m_destPoly.push_back(pt1);
|
||||
m_destPoly.emplace_back(pt1);
|
||||
pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta));
|
||||
m_destPoly.push_back(pt1);
|
||||
m_destPoly.emplace_back(pt1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3652,9 +3628,9 @@ void ClipperOffset::DoOffset(double delta)
|
||||
if (node.m_endtype == etOpenButt)
|
||||
{
|
||||
pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta));
|
||||
m_destPoly.push_back(pt1);
|
||||
m_destPoly.emplace_back(pt1);
|
||||
pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta));
|
||||
m_destPoly.push_back(pt1);
|
||||
m_destPoly.emplace_back(pt1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3665,7 +3641,7 @@ void ClipperOffset::DoOffset(double delta)
|
||||
else
|
||||
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() );
|
||||
if (cosA > 0) // angle => 0 degrees
|
||||
{
|
||||
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
|
||||
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
|
||||
Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
|
||||
return;
|
||||
}
|
||||
@ -3692,10 +3668,10 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
|
||||
|
||||
if (m_sinA * m_delta < 0)
|
||||
{
|
||||
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
|
||||
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
|
||||
Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
|
||||
m_destPoly.push_back(m_srcPoly[j]);
|
||||
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
|
||||
m_destPoly.emplace_back(m_srcPoly[j]);
|
||||
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
|
||||
Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
|
||||
}
|
||||
else
|
||||
@ -3719,10 +3695,10 @@ void ClipperOffset::DoSquare(int j, int k)
|
||||
{
|
||||
double dx = std::tan(std::atan2(m_sinA,
|
||||
m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4);
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)),
|
||||
Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx))));
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)),
|
||||
Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx))));
|
||||
}
|
||||
@ -3731,7 +3707,7 @@ void ClipperOffset::DoSquare(int j, int k)
|
||||
void ClipperOffset::DoMiter(int j, int k, double r)
|
||||
{
|
||||
double q = m_delta / r;
|
||||
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q),
|
||||
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q),
|
||||
Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q)));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -3745,14 +3721,14 @@ void ClipperOffset::DoRound(int j, int k)
|
||||
double X = m_normals[k].x(), Y = m_normals[k].y(), X2;
|
||||
for (int i = 0; i < steps; ++i)
|
||||
{
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[j].x() + X * m_delta),
|
||||
Round(m_srcPoly[j].y() + Y * m_delta)));
|
||||
X2 = X;
|
||||
X = X * m_cos - m_sin * Y;
|
||||
Y = X2 * m_sin + Y * m_cos;
|
||||
}
|
||||
m_destPoly.push_back(IntPoint2d(
|
||||
m_destPoly.emplace_back(IntPoint2d(
|
||||
Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
|
||||
Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
|
||||
}
|
||||
@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons()
|
||||
size_t i = 0;
|
||||
while (i < m_PolyOuts.size())
|
||||
{
|
||||
OutRec* outrec = m_PolyOuts[i++];
|
||||
OutPt* op = outrec->Pts;
|
||||
if (!op || outrec->IsOpen) continue;
|
||||
OutRec &outrec = m_PolyOuts[i++];
|
||||
OutPt* op = outrec.Pts;
|
||||
if (!op || outrec.IsOpen) continue;
|
||||
do //for each Pt in Polygon until duplicate found do ...
|
||||
{
|
||||
OutPt* op2 = op->Next;
|
||||
while (op2 != outrec->Pts)
|
||||
while (op2 != outrec.Pts)
|
||||
{
|
||||
if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
|
||||
{
|
||||
@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons()
|
||||
op2->Prev = op3;
|
||||
op3->Next = op2;
|
||||
|
||||
outrec->Pts = op;
|
||||
outrec.Pts = op;
|
||||
OutRec* outrec2 = CreateOutRec();
|
||||
outrec2->Pts = op2;
|
||||
UpdateOutPtIdxs(*outrec2);
|
||||
if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
|
||||
if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts))
|
||||
{
|
||||
//OutRec2 is contained by OutRec1 ...
|
||||
outrec2->IsHole = !outrec->IsHole;
|
||||
outrec2->FirstLeft = outrec;
|
||||
outrec2->IsHole = !outrec.IsHole;
|
||||
outrec2->FirstLeft = &outrec;
|
||||
// For each m_PolyOuts, replace FirstLeft from outRec2 to outrec.
|
||||
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
|
||||
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec);
|
||||
}
|
||||
else
|
||||
if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
|
||||
if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts))
|
||||
{
|
||||
//OutRec1 is contained by OutRec2 ...
|
||||
outrec2->IsHole = outrec->IsHole;
|
||||
outrec->IsHole = !outrec2->IsHole;
|
||||
outrec2->FirstLeft = outrec->FirstLeft;
|
||||
outrec->FirstLeft = outrec2;
|
||||
outrec2->IsHole = outrec.IsHole;
|
||||
outrec.IsHole = !outrec2->IsHole;
|
||||
outrec2->FirstLeft = outrec.FirstLeft;
|
||||
outrec.FirstLeft = outrec2;
|
||||
// For each m_PolyOuts, replace FirstLeft from outrec to outrec2.
|
||||
if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
|
||||
if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//the 2 polygons are separate ...
|
||||
outrec2->IsHole = outrec->IsHole;
|
||||
outrec2->FirstLeft = outrec->FirstLeft;
|
||||
outrec2->IsHole = outrec.IsHole;
|
||||
outrec2->FirstLeft = outrec.FirstLeft;
|
||||
// For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2.
|
||||
//FIXME This is potentially very expensive! O(n^3)!
|
||||
if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
|
||||
if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2);
|
||||
}
|
||||
op2 = op; //ie get ready for the Next iteration
|
||||
}
|
||||
@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons()
|
||||
}
|
||||
op = op->Next;
|
||||
}
|
||||
while (op != outrec->Pts);
|
||||
while (op != outrec.Pts);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -3845,10 +3821,10 @@ void ReversePaths(Paths& p)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType)
|
||||
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */)
|
||||
{
|
||||
Clipper c;
|
||||
c.StrictlySimple(true);
|
||||
c.StrictlySimple(strictly_simple);
|
||||
c.AddPath(in_poly, ptSubject, true);
|
||||
Paths out;
|
||||
c.Execute(ctUnion, out, fillType, fillType);
|
||||
@ -3941,7 +3917,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<OutPt> outPts(size);
|
||||
OutPts outPts(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
outPts[i].Pt = in_poly[i];
|
||||
@ -4020,8 +3996,8 @@ void Minkowski(const Path& poly, const Path& path,
|
||||
Path p;
|
||||
p.reserve(polyCnt);
|
||||
for (size_t j = 0; j < poly.size(); ++j)
|
||||
p.push_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y()));
|
||||
pp.push_back(p);
|
||||
p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y()));
|
||||
pp.emplace_back(p);
|
||||
}
|
||||
else
|
||||
for (size_t i = 0; i < pathCnt; ++i)
|
||||
@ -4029,8 +4005,8 @@ void Minkowski(const Path& poly, const Path& path,
|
||||
Path p;
|
||||
p.reserve(polyCnt);
|
||||
for (size_t j = 0; j < poly.size(); ++j)
|
||||
p.push_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y()));
|
||||
pp.push_back(p);
|
||||
p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y()));
|
||||
pp.emplace_back(p);
|
||||
}
|
||||
|
||||
solution.clear();
|
||||
@ -4040,12 +4016,12 @@ void Minkowski(const Path& poly, const Path& path,
|
||||
{
|
||||
Path quad;
|
||||
quad.reserve(4);
|
||||
quad.push_back(pp[i % pathCnt][j % polyCnt]);
|
||||
quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);
|
||||
quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
|
||||
quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
|
||||
quad.emplace_back(pp[i % pathCnt][j % polyCnt]);
|
||||
quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]);
|
||||
quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
|
||||
quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]);
|
||||
if (!Orientation(quad)) ReversePath(quad);
|
||||
solution.push_back(quad);
|
||||
solution.emplace_back(quad);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -4105,7 +4081,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path
|
||||
else if (nodetype == ntOpen) return;
|
||||
|
||||
if (!polynode.Contour.empty() && match)
|
||||
paths.push_back(polynode.Contour);
|
||||
paths.emplace_back(polynode.Contour);
|
||||
for (int i = 0; i < polynode.ChildCount(); ++i)
|
||||
AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
|
||||
}
|
||||
@ -4117,7 +4093,7 @@ void AddPolyNodeToPaths(PolyNode&& polynode, NodeType nodetype, Paths& paths)
|
||||
else if (nodetype == ntOpen) return;
|
||||
|
||||
if (!polynode.Contour.empty() && match)
|
||||
paths.push_back(std::move(polynode.Contour));
|
||||
paths.emplace_back(std::move(polynode.Contour));
|
||||
for (int i = 0; i < polynode.ChildCount(); ++i)
|
||||
AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths);
|
||||
}
|
||||
@ -4155,7 +4131,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
|
||||
//Open paths are top level only, so ...
|
||||
for (int i = 0; i < polytree.ChildCount(); ++i)
|
||||
if (polytree.Childs[i]->IsOpen())
|
||||
paths.push_back(polytree.Childs[i]->Contour);
|
||||
paths.emplace_back(polytree.Childs[i]->Contour);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -39,6 +39,8 @@
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
#define CLIPPER_VERSION "6.2.6"
|
||||
|
||||
//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
|
||||
//#define use_deprecated
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
@ -112,8 +115,11 @@ using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef std::vector<IntPoint> Path;
|
||||
typedef std::vector<Path> Paths;
|
||||
template<typename BaseType>
|
||||
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 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};
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
typedef std::vector<PolyNode*, Allocator<PolyNode*>> PolyNodes;
|
||||
|
||||
class PolyNode
|
||||
{
|
||||
@ -186,7 +192,7 @@ public:
|
||||
private:
|
||||
PolyTree(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
|
||||
};
|
||||
|
||||
@ -194,7 +200,8 @@ double Area(const Path &poly);
|
||||
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
|
||||
int PointInPolygon(const IntPoint &pt, const Path &path);
|
||||
|
||||
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd);
|
||||
// Union with "strictly simple" fix enabled.
|
||||
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true);
|
||||
|
||||
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
|
||||
void CleanPolygon(Path& poly, double distance = 1.415);
|
||||
@ -277,7 +284,27 @@ enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
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 {
|
||||
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
|
||||
OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {}
|
||||
@ -312,7 +339,7 @@ public:
|
||||
if (num_paths == 1)
|
||||
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;
|
||||
size_t i = 0;
|
||||
for (const Path &pg : paths_provider) {
|
||||
@ -333,7 +360,7 @@ public:
|
||||
return false;
|
||||
|
||||
// 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.
|
||||
bool result = false;
|
||||
TEdge *p_edge = edges.data();
|
||||
@ -369,7 +396,7 @@ protected:
|
||||
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
||||
|
||||
// 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
|
||||
static constexpr const bool m_UseFullRange = false;
|
||||
@ -380,7 +407,8 @@ protected:
|
||||
#endif // CLIPPERLIB_INT32
|
||||
|
||||
// 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.
|
||||
bool m_PreserveCollinear;
|
||||
// Is any of the paths inserted by AddPath() or AddPaths() open?
|
||||
@ -424,22 +452,23 @@ protected:
|
||||
private:
|
||||
|
||||
// Output polygons.
|
||||
std::vector<OutRec*> m_PolyOuts;
|
||||
std::deque<OutRec, Allocator<OutRec>> m_PolyOuts;
|
||||
// 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.
|
||||
OutPt *m_OutPtsFree;
|
||||
size_t m_OutPtsChunkSize;
|
||||
size_t m_OutPtsChunkLast;
|
||||
|
||||
std::vector<Join> m_Joins;
|
||||
std::vector<Join> m_GhostJoins;
|
||||
std::vector<IntersectNode> m_IntersectList;
|
||||
std::vector<Join, Allocator<Join>> m_Joins;
|
||||
std::vector<Join, Allocator<Join>> m_GhostJoins;
|
||||
std::vector<IntersectNode, Allocator<IntersectNode>> m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
// 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().
|
||||
std::vector<cInt> m_Maxima;
|
||||
cInts m_Maxima;
|
||||
TEdge *m_ActiveEdges;
|
||||
TEdge *m_SortedEdges;
|
||||
PolyFillType m_ClipFillType;
|
||||
@ -473,7 +502,7 @@ private:
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutRec* GetOutRec(int idx);
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2) const;
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2);
|
||||
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
|
||||
OutRec* CreateOutRec();
|
||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
@ -489,7 +518,7 @@ private:
|
||||
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
|
||||
void BuildResult(Paths& polys);
|
||||
void BuildResult2(PolyTree& polytree);
|
||||
void SetHoleState(TEdge *e, OutRec *outrec) const;
|
||||
void SetHoleState(TEdge *e, OutRec *outrec);
|
||||
bool FixupIntersectionOrder();
|
||||
void FixupOutPolygon(OutRec &outrec);
|
||||
void FixupOutPolyline(OutRec &outrec);
|
||||
@ -499,8 +528,8 @@ private:
|
||||
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
|
||||
void JoinCommonEdges();
|
||||
void DoSimplePolygons();
|
||||
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;
|
||||
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
|
||||
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
#ifdef CLIPPERLIB_USE_XYZ
|
||||
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
|
||||
#endif
|
||||
@ -530,7 +559,7 @@ private:
|
||||
Paths m_destPolys;
|
||||
Path m_srcPoly;
|
||||
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_miterLim, m_StepsPerRad;
|
||||
// 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>
|
||||
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) {
|
||||
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) {
|
||||
Clipper c;
|
||||
c.StrictlySimple(true);
|
||||
c.StrictlySimple(strictly_simple);
|
||||
c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true);
|
||||
Paths out;
|
||||
c.Execute(ctUnion, out, fillType, fillType);
|
||||
|
@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES
|
||||
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
|
||||
|
||||
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)
|
||||
|
@ -339,15 +339,15 @@ public:
|
||||
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);
|
||||
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
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
@ -181,7 +180,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<Point> discretized = discretize(vd_edge, segments);
|
||||
Points discretized = discretize(vd_edge, segments);
|
||||
assert(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
|
||||
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();
|
||||
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.
|
||||
{
|
||||
@ -311,7 +310,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
|
||||
//Start generating points along the edge.
|
||||
Point a = start;
|
||||
Point b = end;
|
||||
std::vector<Point> ret;
|
||||
Points ret;
|
||||
ret.emplace_back(a);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
|
||||
const double fix_angle,
|
||||
const std::unordered_map<Point, Point, PointHash> &vertex_mapping)
|
||||
using PointMap = SkeletalTrapezoidation::PointMap;
|
||||
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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,
|
||||
const Polygons &polys,
|
||||
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;
|
||||
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) {
|
||||
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};
|
||||
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.
|
||||
Polygons polys_copy = polys;
|
||||
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
|
||||
@ -813,9 +814,11 @@ process_voronoi_diagram:
|
||||
edge.from->incident_edge = &edge;
|
||||
}
|
||||
|
||||
using NodeSet = SkeletalTrapezoidation::NodeSet;
|
||||
|
||||
void SkeletalTrapezoidation::separatePointyQuadEndNodes()
|
||||
{
|
||||
std::unordered_set<node_t*> visited_nodes;
|
||||
NodeSet visited_nodes;
|
||||
for (edge_t& edge : graph.edges)
|
||||
{
|
||||
if (edge.prev)
|
||||
@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c
|
||||
|
||||
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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <boost/polygon/voronoi.hpp>
|
||||
|
||||
#include <memory> // smart pointers
|
||||
#include <unordered_map>
|
||||
#include <utility> // pair
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <Arachne/utils/VoronoiUtils.hpp>
|
||||
|
||||
#include "utils/HalfEdgeGraph.hpp"
|
||||
@ -80,7 +82,9 @@ class SkeletalTrapezoidation
|
||||
const BeadingStrategy& beading_strategy;
|
||||
|
||||
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.
|
||||
@ -164,8 +168,8 @@ protected:
|
||||
* 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
|
||||
*/
|
||||
std::unordered_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::edge_type*, edge_t*> vd_edge_to_he_edge;
|
||||
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.
|
||||
|
||||
/*!
|
||||
@ -204,7 +208,7 @@ protected:
|
||||
* \return A number of coordinates along the edge where the edge is broken
|
||||
* 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
|
||||
|
@ -2,7 +2,8 @@
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "SkeletalTrapezoidationGraph.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const
|
||||
|
||||
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
|
||||
{
|
||||
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator;
|
||||
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator;
|
||||
ankerl::unordered_dense::map<edge_t*, Edges::iterator> edge_locator;
|
||||
ankerl::unordered_dense::map<node_t*, Nodes::iterator> node_locator;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
&& to_be_removed == &*current_edge_it)
|
||||
|
@ -2,7 +2,6 @@
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <algorithm> //For std::partition_copy and std::min_element.
|
||||
#include <unordered_set>
|
||||
|
||||
#include "WallToolPaths.hpp"
|
||||
|
||||
@ -233,7 +232,7 @@ std::unique_ptr<LocToLineGrid> cre
|
||||
void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
|
||||
{
|
||||
if (epsilon < 1) {
|
||||
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
|
||||
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
// 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;) {
|
||||
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
// so that we can easily find which two toolpaths are next to each other,
|
||||
|
@ -5,7 +5,8 @@
|
||||
#define CURAENGINE_WALLTOOLPATHS_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "BeadingStrategy/BeadingStrategyFactory.hpp"
|
||||
#include "utils/ExtrusionLine.hpp"
|
||||
@ -73,6 +74,7 @@ public:
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
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:
|
||||
/*!
|
||||
|
@ -21,8 +21,10 @@ class HalfEdgeGraph
|
||||
public:
|
||||
using edge_t = derived_edge_t;
|
||||
using node_t = derived_node_t;
|
||||
std::list<edge_t> edges;
|
||||
std::list<node_t> nodes;
|
||||
using Edges = std::list<edge_t>;
|
||||
using Nodes = std::list<node_t>;
|
||||
Edges edges;
|
||||
Nodes nodes;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "SparsePointGrid.hpp"
|
||||
#include "PolygonsPointIndex.hpp"
|
||||
#include "../../Polygon.hpp"
|
||||
#include <unordered_set>
|
||||
#include <cassert>
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_LINE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_POINT_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SparseGrid.hpp"
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "../../Point.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -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]));
|
||||
}
|
||||
};
|
||||
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
|
||||
// xx is point projected on the segment ab
|
||||
const Point a = segment.from();
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
|
@ -6,23 +6,19 @@
|
||||
|
||||
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 BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
|
||||
|
||||
void BoundingBox::polygon(Polygon* polygon) const
|
||||
{
|
||||
polygon->points.clear();
|
||||
polygon->points.resize(4);
|
||||
polygon->points[0](0) = this->min(0);
|
||||
polygon->points[0](1) = this->min(1);
|
||||
polygon->points[1](0) = this->max(0);
|
||||
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->points = {
|
||||
this->min,
|
||||
{ this->max.x(), this->min.y() },
|
||||
this->max,
|
||||
{ this->min.x(), this->max.y() }
|
||||
};
|
||||
}
|
||||
|
||||
Polygon BoundingBox::polygon() const
|
||||
@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.rotated(angle));
|
||||
out.merge(this->max.rotated(angle));
|
||||
out.merge(Point(this->min(0), this->max(1)).rotated(angle));
|
||||
out.merge(Point(this->max(0), this->min(1)).rotated(angle));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle));
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.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->max(0), this->min(1)).rotated(angle, center));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center));
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::scale(double factor)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
||||
{
|
||||
this->min *= 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<Vec3d>::scale(double factor);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(point);
|
||||
@ -74,22 +70,22 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
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<Vec2d>::merge(const Vec2d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointsType &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 <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
||||
template <class PointType, typename APointsType> void
|
||||
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 (this->defined) {
|
||||
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<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
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<Vec3d>::merge(const Vec3d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointsType &points)
|
||||
{
|
||||
this->merge(BoundingBox3Base(points));
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
||||
template <class PointType> void
|
||||
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 (this->defined) {
|
||||
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 <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::size() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
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 Vec2d BoundingBoxBase<Vec2d>::size() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::size() const
|
||||
template <class PointType> PointType
|
||||
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 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);
|
||||
double x = this->max(0) - this->min(0);
|
||||
double y = this->max(1) - this->min(1);
|
||||
return 0.5 * sqrt(x*x+y*y);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBoxBase<Point>::radius() const;
|
||||
template double BoundingBoxBase<Point, Points>::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);
|
||||
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);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBox3Base<Vec3d>::radius() const;
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta);
|
||||
PointType v(delta, delta);
|
||||
this->min -= 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 <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta, delta);
|
||||
PointType v(delta, delta, delta);
|
||||
this->min -= v;
|
||||
this->max += v;
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::center() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
BoundingBoxBase<PointType, APointsType>::center() const
|
||||
{
|
||||
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 Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::center() const
|
||||
template <class PointType> PointType
|
||||
BoundingBox3Base<PointType>::center() const
|
||||
{
|
||||
return (this->min + this->max) / 2;
|
||||
}
|
||||
template Vec3f BoundingBox3Base<Vec3f>::center() const;
|
||||
template Vec3d BoundingBox3Base<Vec3d>::center() const;
|
||||
|
||||
template <class PointClass> coordf_t
|
||||
BoundingBox3Base<PointClass>::max_size() const
|
||||
template <class PointType> coordf_t
|
||||
BoundingBox3Base<PointType>::max_size() const
|
||||
{
|
||||
PointClass s = size();
|
||||
return std::max(s(0), std::max(s(1), s(2)));
|
||||
PointType s = size();
|
||||
return std::max(s.x(), std::max(s.y(), s.z()));
|
||||
}
|
||||
template coordf_t BoundingBox3Base<Vec3f>::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)
|
||||
{
|
||||
if (this->defined) {
|
||||
min(0) = Slic3r::align_to_grid(min(0), cell_size);
|
||||
min(1) = Slic3r::align_to_grid(min(1), cell_size);
|
||||
min.x() = Slic3r::align_to_grid(min.x(), 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;
|
||||
|
||||
Vertices src_vertices;
|
||||
src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2);
|
||||
src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2);
|
||||
src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2);
|
||||
src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2);
|
||||
src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2);
|
||||
src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2);
|
||||
src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2);
|
||||
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2);
|
||||
src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z();
|
||||
src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z();
|
||||
src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z();
|
||||
src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z();
|
||||
src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z();
|
||||
src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z();
|
||||
src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z();
|
||||
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();
|
||||
|
||||
|
@ -8,53 +8,54 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template <class PointClass>
|
||||
template <typename PointType, typename APointsType = std::vector<PointType>>
|
||||
class BoundingBoxBase
|
||||
{
|
||||
public:
|
||||
PointClass min;
|
||||
PointClass max;
|
||||
using PointsType = APointsType;
|
||||
PointType min;
|
||||
PointType max;
|
||||
bool defined;
|
||||
|
||||
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointType &pmin, const PointType &pmax) :
|
||||
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); }
|
||||
|
||||
template<class It, class = IteratorOnly<It>>
|
||||
BoundingBoxBase(It from, It to)
|
||||
{ construct(*this, from, to); }
|
||||
|
||||
BoundingBoxBase(const std::vector<PointClass> &points)
|
||||
BoundingBoxBase(const PointsType &points)
|
||||
: BoundingBoxBase(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||
void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); }
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBoxBase<PointType, PointsType> &bb);
|
||||
void scale(double factor);
|
||||
PointClass size() const;
|
||||
PointType size() 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(const PointClass &v) { 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 PointType &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const {
|
||||
BoundingBoxBase<PointType, PointsType> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointType, PointsType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
bool contains(const PointType &point) const {
|
||||
return point.x() >= this->min.x() && point.x() <= this->max.x()
|
||||
&& 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);
|
||||
}
|
||||
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() ||
|
||||
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<PointClass> &rhs) { return ! (*this == rhs); }
|
||||
bool operator==(const BoundingBoxBase<PointType, PointsType> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
|
||||
bool operator!=(const BoundingBoxBase<PointType, PointsType> &rhs) { return ! (*this == rhs); }
|
||||
|
||||
private:
|
||||
// to access construct()
|
||||
@ -69,10 +70,10 @@ private:
|
||||
{
|
||||
if (from != to) {
|
||||
auto it = from;
|
||||
out.min = it->template cast<typename PointClass::Scalar>();
|
||||
out.min = it->template cast<typename PointType::Scalar>();
|
||||
out.max = out.min;
|
||||
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.max = out.max.cwiseMax(vec);
|
||||
}
|
||||
@ -81,16 +82,18 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <class PointClass>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointClass>
|
||||
template <class PointType>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointType, std::vector<PointType>>
|
||||
{
|
||||
public:
|
||||
BoundingBox3Base() : BoundingBoxBase<PointClass>() {}
|
||||
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase<PointClass>(pmin, pmax)
|
||||
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointClass>::defined = false; }
|
||||
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); }
|
||||
using PointsType = std::vector<PointType>;
|
||||
|
||||
BoundingBox3Base() : BoundingBoxBase<PointType>() {}
|
||||
BoundingBox3Base(const PointType &pmin, const PointType &pmax) :
|
||||
BoundingBoxBase<PointType>(pmin, pmax)
|
||||
{ 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)
|
||||
{
|
||||
@ -98,67 +101,67 @@ public:
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointClass::Scalar>();
|
||||
this->min = it->template cast<typename PointType::Scalar>();
|
||||
this->max = this->min;
|
||||
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->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());
|
||||
}
|
||||
|
||||
BoundingBox3Base(const std::vector<PointClass> &points)
|
||||
BoundingBox3Base(const PointsType &points)
|
||||
: BoundingBox3Base(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||
PointClass size() const;
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBox3Base<PointType> &bb);
|
||||
PointType size() 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 offset(coordf_t delta);
|
||||
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
BoundingBox3Base<PointType> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
bool contains(const PointClass &point) const {
|
||||
return BoundingBoxBase<PointClass>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
|
||||
bool contains(const PointType &point) const {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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() &&
|
||||
this->min.z() < other.max.z() && this->max.z() > other.min.z();
|
||||
}
|
||||
};
|
||||
|
||||
// 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<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<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<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<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<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 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 Point BoundingBoxBase<Point>::center() const;
|
||||
extern template Point BoundingBoxBase<Point, Points>::center() const;
|
||||
extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
|
||||
extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
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<Vec3d>::max_size() const;
|
||||
|
||||
class BoundingBox : public BoundingBoxBase<Point>
|
||||
class BoundingBox : public BoundingBoxBase<Point, Points>
|
||||
{
|
||||
public:
|
||||
void polygon(Polygon* polygon) const;
|
||||
@ -187,9 +190,9 @@ public:
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
||||
BoundingBox() : BoundingBoxBase<Point>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {}
|
||||
BoundingBox() : BoundingBoxBase<Point, Points>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBoxBase<VT> &bb)
|
||||
template<typename PointType, typename PointsType>
|
||||
inline bool empty(const BoundingBoxBase<PointType, PointsType> &bb)
|
||||
{
|
||||
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y();
|
||||
}
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBox3Base<VT> &bb)
|
||||
template<typename PointType>
|
||||
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();
|
||||
}
|
||||
|
@ -276,10 +276,21 @@ set(SLIC3R_SOURCES
|
||||
SlicingAdaptive.hpp
|
||||
Subdivide.cpp
|
||||
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.hpp
|
||||
SupportMaterial.cpp
|
||||
SupportMaterial.hpp
|
||||
Surface.cpp
|
||||
Surface.hpp
|
||||
SurfaceCollection.cpp
|
||||
@ -291,10 +302,6 @@ set(SLIC3R_SOURCES
|
||||
Tesselate.cpp
|
||||
Tesselate.hpp
|
||||
TextConfiguration.hpp
|
||||
TreeSupport.cpp
|
||||
TreeSupport.hpp
|
||||
TreeModelVolumes.cpp
|
||||
TreeModelVolumes.hpp
|
||||
TriangleMesh.cpp
|
||||
TriangleMesh.hpp
|
||||
TriangleMeshSlicer.cpp
|
||||
@ -485,6 +492,7 @@ target_link_libraries(libslic3r
|
||||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
TBB::tbbmalloc
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
PNG::PNG
|
||||
|
@ -3,6 +3,20 @@
|
||||
#include "ShortestPath.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
|
||||
|
||||
#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.
|
||||
// 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.
|
||||
template<typename PointType>
|
||||
inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox, std::vector<PointType> &out)
|
||||
template<typename PointsType>
|
||||
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();
|
||||
const size_t cnt = src.size();
|
||||
if (cnt < 3)
|
||||
@ -108,10 +124,10 @@ namespace ClipperUtils {
|
||||
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); }
|
||||
|
||||
template<typename PointType>
|
||||
[[nodiscard]] std::vector<PointType> clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox)
|
||||
template<typename PointsType>
|
||||
[[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);
|
||||
return out;
|
||||
}
|
||||
@ -258,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon>
|
||||
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::Paths out;
|
||||
out.reserve(paths.size());
|
||||
@ -299,6 +317,8 @@ TResult clipper_do(
|
||||
TClip && clip,
|
||||
const ClipperLib::PolyFillType fillType)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, 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
|
||||
const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
|
||||
TResult retval;
|
||||
@ -366,6 +388,8 @@ template<> void remove_outermost_polygon<ClipperLib::PolyTree>(ClipperLib::PolyT
|
||||
template<class TResult, typename PathsProvider>
|
||||
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);
|
||||
TResult out;
|
||||
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).
|
||||
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.
|
||||
ClipperLib::Paths contours;
|
||||
{
|
||||
@ -613,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree(
|
||||
PathProvider2 &&clip,
|
||||
const ClipperLib::PolyFillType fillType)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// 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
|
||||
// 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); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
@ -751,6 +781,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject)
|
||||
template<typename PathsProvider1, typename PathsProvider2>
|
||||
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false);
|
||||
clipper.AddPaths(std::forward<PathsProvider2>(clip), ClipperLib::ptClip, true);
|
||||
@ -934,31 +966,30 @@ Polygons union_pt_chained_outside_in(const Polygons &subject)
|
||||
return retval;
|
||||
}
|
||||
|
||||
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
|
||||
Polygons simplify_polygons(const Polygons &subject)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths output;
|
||||
if (preserve_collinear) {
|
||||
ClipperLib::Clipper c;
|
||||
c.PreserveCollinear(true);
|
||||
c.StrictlySimple(true);
|
||||
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
|
||||
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
} else {
|
||||
output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero);
|
||||
}
|
||||
|
||||
ClipperLib::Clipper c;
|
||||
// c.PreserveCollinear(true);
|
||||
//FIXME StrictlySimple is very expensive! Is it needed?
|
||||
c.StrictlySimple(true);
|
||||
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
|
||||
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
|
||||
// convert into Slic3r polygons
|
||||
return to_polygons(std::move(output));
|
||||
}
|
||||
|
||||
ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
|
||||
{
|
||||
if (! preserve_collinear)
|
||||
return union_ex(simplify_polygons(subject, false));
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::PolyTree polytree;
|
||||
ClipperLib::Clipper c;
|
||||
c.PreserveCollinear(true);
|
||||
// c.PreserveCollinear(true);
|
||||
//FIXME StrictlySimple is very expensive! Is it needed?
|
||||
c.StrictlySimple(true);
|
||||
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
|
||||
c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
@ -969,6 +1000,8 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
|
||||
|
||||
Polygons top_level_islands(const Slic3r::Polygons &polygons)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// init Clipper
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
@ -992,6 +1025,8 @@ ClipperLib::Paths fix_after_outer_offset(
|
||||
ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive
|
||||
bool reverse_result) // = false
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
ClipperLib::Clipper clipper;
|
||||
@ -1010,6 +1045,8 @@ ClipperLib::Paths fix_after_inner_offset(
|
||||
ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative
|
||||
bool reverse_result) // = true
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
assert(contour.size() == deltas.size());
|
||||
|
||||
#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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths 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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths 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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths 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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths contours, holes;
|
||||
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
|
||||
|
||||
|
@ -133,21 +133,21 @@ namespace ClipperUtils {
|
||||
const std::vector<PathType> &m_paths;
|
||||
};
|
||||
|
||||
template<typename MultiPointType>
|
||||
template<typename MultiPointsType>
|
||||
class MultiPointsProvider {
|
||||
public:
|
||||
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {}
|
||||
MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
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; }
|
||||
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
const Points& operator++(int) { return (m_it ++)->points; }
|
||||
iterator& operator++() { ++ m_it; return *this; }
|
||||
private:
|
||||
typename std::vector<MultiPointType>::const_iterator m_it;
|
||||
typename MultiPointsType::const_iterator m_it;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_multipoints.begin()); }
|
||||
@ -157,11 +157,11 @@ namespace ClipperUtils {
|
||||
size_t size() const { return m_multipoints.size(); }
|
||||
|
||||
private:
|
||||
const std::vector<MultiPointType> &m_multipoints;
|
||||
const MultiPointsType &m_multipoints;
|
||||
};
|
||||
|
||||
using PolygonsProvider = MultiPointsProvider<Polygon>;
|
||||
using PolylinesProvider = MultiPointsProvider<Polyline>;
|
||||
using PolygonsProvider = MultiPointsProvider<Polygons>;
|
||||
using PolylinesProvider = MultiPointsProvider<Polylines>;
|
||||
|
||||
struct ExPolygonProvider {
|
||||
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::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
|
||||
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
@ -596,8 +599,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
|
||||
|
||||
|
||||
/* OTHER */
|
||||
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject);
|
||||
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject);
|
||||
|
||||
Polygons top_level_islands(const Slic3r::Polygons &polygons);
|
||||
|
||||
|
@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
|
||||
// Convert multiple paths to paths with a given Z coordinate.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
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;
|
||||
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.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
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());
|
||||
for (const ZPoints &path : paths)
|
||||
out.emplace_back(from_zpath<Open>(path));
|
||||
}
|
||||
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);
|
||||
return out;
|
||||
}
|
||||
|
@ -1991,7 +1991,7 @@ public:
|
||||
|
||||
void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) {
|
||||
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;
|
||||
enum_def->set_labels(il);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
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 *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 *end() const { return m_end; }
|
||||
|
@ -184,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const
|
||||
{
|
||||
Polygon p = this->contour;
|
||||
p.points.push_back(p.points.front());
|
||||
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||
p.points = MultiPoint::douglas_peucker(p.points, tolerance);
|
||||
p.points.pop_back();
|
||||
pp.emplace_back(std::move(p));
|
||||
}
|
||||
// holes
|
||||
for (Polygon p : this->holes) {
|
||||
p.points.push_back(p.points.front());
|
||||
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||
p.points = MultiPoint::douglas_peucker(p.points, tolerance);
|
||||
p.points.pop_back();
|
||||
pp.emplace_back(std::move(p));
|
||||
}
|
||||
@ -397,7 +397,7 @@ bool has_duplicate_points(const ExPolygon &expoly)
|
||||
size_t cnt = expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
std::vector<Point> allpts;
|
||||
Points allpts;
|
||||
allpts.reserve(cnt);
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
@ -420,7 +420,7 @@ bool has_duplicate_points(const ExPolygons &expolys)
|
||||
// Check globally.
|
||||
#if 0
|
||||
// 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));
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
|
@ -176,7 +176,7 @@ Flow Flow::with_cross_section(float area_new) const
|
||||
return this->with_width(width_new);
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
} else
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
speed);
|
||||
variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(),
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../Config.hpp"
|
||||
#include "../Line.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -20,107 +21,32 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
float distance;
|
||||
size_t nearest_prev_layer_line;
|
||||
float curvature;
|
||||
};
|
||||
|
||||
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L>
|
||||
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points,
|
||||
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 POINTS &input_points,
|
||||
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f)
|
||||
{
|
||||
using P = typename POINTS::value_type;
|
||||
|
||||
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
|
||||
if (input_points.empty())
|
||||
return {};
|
||||
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>(); };
|
||||
|
||||
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())};
|
||||
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.nearest_prev_layer_line = nearest_line;
|
||||
points.push_back(start_point);
|
||||
}
|
||||
for (size_t i = 1; i < input_points.size(); 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>());
|
||||
next_point.distance = distance + boundary_offset;
|
||||
next_point.nearest_prev_layer_line = nearest_line;
|
||||
|
||||
if (ADD_INTERSECTIONS &&
|
||||
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
|
||||
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>()});
|
||||
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);
|
||||
@ -170,12 +97,18 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
|
||||
if (t0 < 1.0) {
|
||||
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>());
|
||||
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) {
|
||||
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>());
|
||||
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);
|
||||
auto [p_dist, p_near_l,
|
||||
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());
|
||||
@ -207,6 +143,9 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
|
||||
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) {
|
||||
ExtendedPoint &a = points[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;
|
||||
while (prev_point_idx > 0) {
|
||||
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;
|
||||
while (next_point_index < int(points.size()) - 1) {
|
||||
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) {
|
||||
float distance = (prev.position - a.position).norm();
|
||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
||||
cestim.add_point(distance, alfa);
|
||||
}
|
||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
||||
angles_for_curvature[point_idx] = 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;
|
||||
@ -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>> 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;
|
||||
|
||||
public:
|
||||
@ -253,18 +231,22 @@ public:
|
||||
|
||||
void prepare_for_new_layer(const Layer *layer)
|
||||
{
|
||||
if (layer == nullptr) return;
|
||||
const PrintObject *object = layer->object();
|
||||
prev_layer_boundaries[object] = next_layer_boundaries[object];
|
||||
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
|
||||
if (layer == nullptr)
|
||||
return;
|
||||
const PrintObject *object = layer->object();
|
||||
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,
|
||||
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
|
||||
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
|
||||
size_t extruder_id,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed)
|
||||
std::vector<ProcessedPoint> estimate_speed_from_extrusion_quality(
|
||||
const ExtrusionPath &path,
|
||||
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
|
||||
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
|
||||
size_t extruder_id,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed)
|
||||
{
|
||||
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
|
||||
std::map<float, float> speed_sections;
|
||||
@ -292,6 +274,22 @@ public:
|
||||
const ExtendedPoint &curr = extended_points[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 upper_dist = values.lower_bound(distance);
|
||||
if (upper_dist == values.end()) {
|
||||
@ -306,12 +304,14 @@ public:
|
||||
return (1.0f - t) * lower_dist->second + t * upper_dist->second;
|
||||
};
|
||||
|
||||
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
|
||||
interpolate_speed(speed_sections, next.distance));
|
||||
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
|
||||
interpolate_speed(fan_speed_sections, next.distance));
|
||||
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
|
||||
interpolate_speed(speed_sections, next.distance));
|
||||
float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines);
|
||||
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;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
#include "libslic3r/GCodeWriter.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
@ -441,9 +442,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
@ -461,9 +460,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
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_flavor = config.gcode_flavor;
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
m_result.backtrace_enabled = is_XL_printer(config);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
size_t extruders_count = config.nozzle_diameter.values.size();
|
||||
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);
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Helper class to modify and export gcode to file
|
||||
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]);
|
||||
#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
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = false;
|
||||
#else
|
||||
unsigned int extra_lines_count = 0;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
// remove trailing '\n'
|
||||
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) {
|
||||
line = line.substr(1);
|
||||
if (m_time_processor.export_remaining_time_enabled &&
|
||||
@ -3739,29 +3722,16 @@ void GCodeProcessor::post_process()
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
if (machine.enabled) {
|
||||
// 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(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
|
||||
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
|
||||
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);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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;
|
||||
#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",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
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",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
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);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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);
|
||||
@ -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,
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
&export_lines]
|
||||
#else
|
||||
// String output
|
||||
&export_line]
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
(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) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++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),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
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(),
|
||||
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;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
// export remaining time to next printer stop
|
||||
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 (to_export_stop > 0) {
|
||||
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));
|
||||
#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;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -3953,22 +3885,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
|
||||
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))
|
||||
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
|
||||
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)));
|
||||
#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;
|
||||
#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
|
||||
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);
|
||||
@ -4027,37 +3945,15 @@ void GCodeProcessor::post_process()
|
||||
});
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
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;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Backtrace data for Tx gcode lines
|
||||
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
|
||||
// to flush the backtrace cache accordingly
|
||||
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.
|
||||
@ -4081,24 +3977,15 @@ void GCodeProcessor::post_process()
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.update(line_id, g1_lines_counter);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = process_placeholders(gcode_line);
|
||||
if (processed)
|
||||
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)
|
||||
processed = process_used_filament(gcode_line);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (!processed && !is_temporary_decoration(gcode_line)) {
|
||||
if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
|
||||
// add lines M73 where needed
|
||||
@ -4113,18 +4000,6 @@ void GCodeProcessor::post_process()
|
||||
if (!gcode_line.empty())
|
||||
export_lines.append_line(gcode_line);
|
||||
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();
|
||||
}
|
||||
// Skip EOL.
|
||||
@ -4139,30 +4014,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.flush(out, m_result, out_path);
|
||||
#else
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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))
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +
|
||||
|
@ -127,9 +127,7 @@ namespace Slic3r {
|
||||
float max_print_height;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool backtrace_enabled;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
|
@ -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
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,6 @@ public:
|
||||
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) {
|
||||
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();
|
||||
return { cmd.begin(), cmd.end() };
|
||||
}
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
|
@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point);
|
||||
|
||||
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
|
||||
{
|
||||
Polygons pp;
|
||||
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) {
|
||||
Polygon p = *it;
|
||||
p.points.push_back(p.points.front());
|
||||
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||
p.points.pop_back();
|
||||
pp.push_back(p);
|
||||
Polygons simplified_raw;
|
||||
for (const Polygon &source_polygon : polygons) {
|
||||
Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance);
|
||||
if (simplified.size() > 3) {
|
||||
simplified.pop_back();
|
||||
simplified_raw.push_back(Polygon{ std::move(simplified) });
|
||||
}
|
||||
}
|
||||
*retval = Slic3r::simplify_polygons(pp);
|
||||
*retval = Slic3r::simplify_polygons(simplified_raw);
|
||||
}
|
||||
|
||||
double linint(double value, double oldmin, double oldmax, double newmin, double newmax)
|
||||
|
@ -12,11 +12,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*>;
|
||||
}
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
// Generic result of an orientation predicate.
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
//#define DEBUG_FILES
|
||||
#ifdef DEBUG_FILES
|
||||
#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;
|
||||
Lines obstacles;
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
obstacles.reserve(layer->curled_lines.size());
|
||||
for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
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)>>;
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
#endif
|
||||
|
||||
std::vector<Pixel> tmp_path;
|
||||
std::vector<Pixel, PointsAllocator<Pixel>> tmp_path;
|
||||
tmp_path.reserve(out_path.size());
|
||||
// Some path found, reverse and remove points that do not change direction
|
||||
std::reverse(out_path.begin(), out_path.end());
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_Layer_hpp_
|
||||
#define slic3r_Layer_hpp_
|
||||
|
||||
#include "Line.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Flow.hpp"
|
||||
@ -325,7 +326,7 @@ public:
|
||||
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.
|
||||
Lines malformed_lines;
|
||||
CurledLines curled_lines;
|
||||
|
||||
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
|
||||
// (with possibly differing extruder ID and slicing parameters) and merged.
|
||||
|
@ -230,10 +230,13 @@ Surfaces expand_bridges_detect_orientations(
|
||||
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) &&
|
||||
// One may ignore holes, they are irrelevant for intersection test.
|
||||
! 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
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>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
using VecType = Vec<Dim<L>, double>;
|
||||
const VecType v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const VecType va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm();
|
||||
if (l2 == 0.0) {
|
||||
// a == b case
|
||||
*nearest_point = get_a(line);
|
||||
@ -53,19 +54,20 @@ double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, V
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
const double t = va.dot(v);
|
||||
if (t <= 0.0) {
|
||||
// beyond the 'a' end of the segment
|
||||
*nearest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
} else if (t >= 1.0) {
|
||||
} else if (t >= l2) {
|
||||
// beyond the 'b' end of the segment
|
||||
*nearest_point = get_b(line);
|
||||
return (point - get_b(line)).template cast<double>().squaredNorm();
|
||||
}
|
||||
|
||||
*nearest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
|
||||
return (t * v - va).squaredNorm();
|
||||
const VecType w = ((t / l2) * v).eval();
|
||||
*nearest_point = (get_a(line).template cast<double>() + w).template cast<Scalar<L>>();
|
||||
return (w - va).squaredNorm();
|
||||
}
|
||||
|
||||
// Distance to the closest point of line.
|
||||
@ -209,6 +211,18 @@ public:
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -2405,12 +2405,8 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
|
||||
arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
||||
{
|
||||
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
|
||||
|
||||
Vec3d rotation = get_rotation();
|
||||
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);
|
||||
Polygon p = get_object()->convex_hull_2d(this->get_matrix());
|
||||
|
||||
// if (!p.points.empty()) {
|
||||
// Polygons pp{p};
|
||||
@ -2420,12 +2416,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
||||
|
||||
arrangement::ArrangePolygon ret;
|
||||
ret.poly.contour = std::move(p);
|
||||
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
|
||||
ret.rotation = get_rotation(Z);
|
||||
ret.translation = Vec2crd::Zero();
|
||||
ret.rotation = 0.;
|
||||
|
||||
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
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
|
@ -1167,14 +1167,7 @@ public:
|
||||
arrangement::ArrangePolygon get_arrange_polygon() const;
|
||||
|
||||
// Apply the arrange result on the ModelInstance
|
||||
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();
|
||||
}
|
||||
void apply_arrange_result(const Vec2d& offs, double rotation);
|
||||
|
||||
protected:
|
||||
friend class Print;
|
||||
|
@ -103,10 +103,10 @@ bool MultiPoint::remove_duplicate_points()
|
||||
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;
|
||||
double tolerance_sq = tolerance * tolerance;
|
||||
Points result_pts;
|
||||
auto tolerance_sq = int64_t(sqr(tolerance));
|
||||
if (! pts.empty()) {
|
||||
const Point *anchor = &pts.front();
|
||||
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.emplace_back(floater_idx);
|
||||
for (;;) {
|
||||
double max_dist_sq = 0.0;
|
||||
size_t furthest_idx = anchor_idx;
|
||||
int64_t max_dist_sq = 0;
|
||||
size_t furthest_idx = anchor_idx;
|
||||
// find point furthest from line seg created by (anchor, floater) and note it
|
||||
for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
|
||||
double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater);
|
||||
if (dist_sq > max_dist_sq) {
|
||||
max_dist_sq = dist_sq;
|
||||
furthest_idx = i;
|
||||
{
|
||||
const Point a = *anchor;
|
||||
const Point f = *floater;
|
||||
const Vec2i64 v = (f - a).cast<int64_t>();
|
||||
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
|
||||
|
@ -81,7 +81,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
static Points douglas_peucker(const Points &points, const double tolerance);
|
||||
static Points visivalingam(const Points& pts, const double& tolerance);
|
||||
|
||||
inline auto begin() { return points.begin(); }
|
||||
@ -110,7 +110,7 @@ public:
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
inline double length(const Points &pts) {
|
||||
|
@ -40,10 +40,11 @@
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
// #define ARACHNE_DEBUG
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
@ -569,7 +570,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
|
||||
size_t occurrence = 0;
|
||||
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) {
|
||||
++point_occurrence[path.polyline.first_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.
|
||||
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::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++)
|
||||
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) {
|
||||
auto after_it = map_extrusion_to_idx.find(after);
|
||||
++blocked[after_it->second];
|
||||
|
@ -1644,9 +1644,9 @@ namespace client
|
||||
}
|
||||
if (! evaluated) {
|
||||
// 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);
|
||||
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);
|
||||
else
|
||||
// The value is really outside the table range.
|
||||
|
@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point ¢er)
|
||||
(*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());
|
||||
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.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
BoundingBox get_extents(const VecOfPoints &pts)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const Points &p : pts)
|
||||
bbox.merge(get_extents<IncludeBoundary>(p));
|
||||
return bbox;
|
||||
}
|
||||
template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
template BoundingBox get_extents<true>(const VecOfPoints &pts);
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#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 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 PointConstPtrs = std::vector<const Point*>;
|
||||
using Points3 = std::vector<Vec3crd>;
|
||||
@ -57,6 +63,8 @@ using Pointfs = std::vector<Vec2d>;
|
||||
using Vec2ds = std::vector<Vec2d>;
|
||||
using Pointf3s = std::vector<Vec3d>;
|
||||
|
||||
using VecOfPoints = std::vector<Points, PointsAllocator<Points>>;
|
||||
|
||||
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix2d = Eigen::Matrix<double, 2, 2, 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.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary = false>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
BoundingBox get_extents(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<true>(const VecOfPoints &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.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(std::vector<Point> &&pts);
|
||||
inline bool has_duplicate_points(const std::vector<Point> &pts)
|
||||
bool has_duplicate_points(Points &&pts);
|
||||
inline bool has_duplicate_points(const Points &pts)
|
||||
{
|
||||
std::vector<Point> cpy = pts;
|
||||
Points cpy = pts;
|
||||
return has_duplicate_points(std::move(cpy));
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// 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)
|
||||
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.
|
||||
// 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());
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ bool Polygon::make_clockwise()
|
||||
void Polygon::douglas_peucker(double tolerance)
|
||||
{
|
||||
this->points.push_back(this->points.front());
|
||||
Points p = MultiPoint::_douglas_peucker(this->points, tolerance);
|
||||
Points p = MultiPoint::douglas_peucker(this->points, tolerance);
|
||||
p.pop_back();
|
||||
this->points = std::move(p);
|
||||
}
|
||||
@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const
|
||||
// on the whole polygon
|
||||
Points points = this->points;
|
||||
points.push_back(points.front());
|
||||
Polygon p(MultiPoint::_douglas_peucker(points, tolerance));
|
||||
Polygon p(MultiPoint::douglas_peucker(points, tolerance));
|
||||
p.points.pop_back();
|
||||
|
||||
Polygons pp;
|
||||
@ -404,7 +404,7 @@ bool has_duplicate_points(const Polygons &polys)
|
||||
// Check globally.
|
||||
#if 0
|
||||
// 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));
|
||||
for (const Polygon &poly : polys)
|
||||
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
|
||||
@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys)
|
||||
remove_collinear(poly);
|
||||
}
|
||||
|
||||
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
|
||||
static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out)
|
||||
{
|
||||
Points simplified = MultiPoint::douglas_peucker(points, tolerance);
|
||||
// then remove the last (repeated) point.
|
||||
simplified.pop_back();
|
||||
// Simplify the decimated contour by ClipperLib.
|
||||
bool ccw = ClipperLib::Area(simplified) > 0.;
|
||||
for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) {
|
||||
if (!ccw)
|
||||
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
|
||||
std::reverse(path.begin(), path.end());
|
||||
out.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(source_polygons.size());
|
||||
for (Polygon &source_polygon : source_polygons) {
|
||||
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
|
||||
source_polygon.points.emplace_back(source_polygon.points.front());
|
||||
simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(source_polygons.size());
|
||||
for (const Polygon &source_polygon : source_polygons) {
|
||||
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
|
||||
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
|
||||
// then remove the last (repeated) point.
|
||||
simplified.pop_back();
|
||||
// Simplify the decimated contour by ClipperLib.
|
||||
bool ccw = ClipperLib::Area(simplified) > 0.;
|
||||
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) {
|
||||
if (! ccw)
|
||||
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
|
||||
std::reverse(path.begin(), path.end());
|
||||
out.emplace_back(std::move(path));
|
||||
}
|
||||
simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -5,15 +5,16 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "MultiPoint.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Polygon;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using PolygonPtrs = std::vector<Polygon*>;
|
||||
using ConstPolygonPtrs = std::vector<const Polygon*>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
|
||||
using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
|
||||
|
||||
// Returns true if inside. Returns border_result if on boundary.
|
||||
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)
|
||||
{
|
||||
@ -241,7 +243,7 @@ inline Polylines to_polylines(Polygons &&polys)
|
||||
return polylines;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(const std::vector<Points> &paths)
|
||||
inline Polygons to_polygons(const VecOfPoints &paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
@ -250,7 +252,7 @@ inline Polygons to_polygons(const std::vector<Points> &paths)
|
||||
return out;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(std::vector<Points> &&paths)
|
||||
inline Polygons to_polygons(VecOfPoints &&paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
|
@ -17,7 +17,7 @@ namespace EdgeGrid {
|
||||
|
||||
struct TrimmedLoop
|
||||
{
|
||||
std::vector<Point> points;
|
||||
Points points;
|
||||
// Number of points per segment. Empty if the loop is
|
||||
std::vector<unsigned int> segments;
|
||||
|
||||
|
@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const
|
||||
|
||||
void Polyline::simplify(double tolerance)
|
||||
{
|
||||
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
|
||||
this->points = MultiPoint::douglas_peucker(this->points, tolerance);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -158,8 +158,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
|
||||
// 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
|
||||
// either with src piece or dst piece.
|
||||
template<typename PointType>
|
||||
inline void polylines_merge(std::vector<PointType> &dst, bool dst_first, std::vector<PointType> &&src, bool src_first)
|
||||
template<typename PointsType>
|
||||
inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first)
|
||||
{
|
||||
if (dst_first) {
|
||||
if (src_first)
|
||||
|
@ -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_contact_distance", "support_material_bottom_contact_distance",
|
||||
"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",
|
||||
"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",
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
@ -880,7 +879,6 @@ void Print::process()
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->make_perimeters();
|
||||
this->set_status(70, _u8L("Infilling layers"));
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->infill();
|
||||
for (PrintObject *obj : m_objects)
|
||||
@ -1132,9 +1130,9 @@ Polygons Print::first_layer_islands() const
|
||||
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()) {
|
||||
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;
|
||||
|
@ -626,7 +626,7 @@ private:
|
||||
// Islands of objects and their supports extruded at the 1st layer.
|
||||
Polygons first_layer_islands() const;
|
||||
// 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.
|
||||
// That means data shared by all print objects of the print_objects span may still use the shared data.
|
||||
|
@ -2939,6 +2939,18 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
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
|
||||
// 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.
|
||||
|
@ -554,6 +554,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, support_tree_angle_slow))
|
||||
((ConfigOptionFloat, support_tree_branch_diameter))
|
||||
((ConfigOptionFloat, support_tree_branch_diameter_angle))
|
||||
((ConfigOptionFloat, support_tree_branch_diameter_double_wall))
|
||||
((ConfigOptionPercent, support_tree_top_rate))
|
||||
((ConfigOptionFloat, support_tree_branch_distance))
|
||||
((ConfigOptionFloat, support_tree_tip_diameter))
|
||||
|
@ -17,8 +17,8 @@
|
||||
#include "MutablePolygon.hpp"
|
||||
#include "PrintBase.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "TreeSupport.hpp"
|
||||
#include "Support/SupportMaterial.hpp"
|
||||
#include "Support/TreeSupport.hpp"
|
||||
#include "Surface.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
@ -27,7 +27,7 @@
|
||||
#include "Fill/FillAdaptive.hpp"
|
||||
#include "Fill/FillLightning.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "Support/SupportMaterial.hpp"
|
||||
#include "SupportSpotsGenerator.hpp"
|
||||
#include "TriangleSelectorWrapper.hpp"
|
||||
#include "format.hpp"
|
||||
@ -56,6 +56,20 @@
|
||||
|
||||
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
|
||||
#define SLIC3R_DEBUG
|
||||
#endif
|
||||
@ -178,6 +192,7 @@ void PrintObject::make_perimeters()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size() - 1),
|
||||
[this, ®ion, 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) {
|
||||
m_print->throw_if_canceled();
|
||||
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
|
||||
@ -237,6 +252,7 @@ void PrintObject::make_perimeters()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_perimeters();
|
||||
@ -408,6 +424,7 @@ void PrintObject::infill()
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
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.
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_ironing();
|
||||
@ -491,7 +509,9 @@ void PrintObject::generate_support_material()
|
||||
void PrintObject::estimate_curled_extrusions()
|
||||
{
|
||||
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";
|
||||
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)));
|
||||
// ^ 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()),
|
||||
[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) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
// and gather them.
|
||||
for (size_t i = 1; i < overhangs.size(); ++ 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_branch_diameter"
|
||||
|| 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_branch_distance"
|
||||
|| opt_key == "support_tree_tip_diameter"
|
||||
@ -909,6 +931,7 @@ void PrintObject::detect_surfaces_type()
|
||||
// In non-spiral vase mode, go over all layers.
|
||||
m_layers.size()),
|
||||
[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
|
||||
// the support from the print.
|
||||
SurfaceType surface_type_bottom_other =
|
||||
@ -1057,6 +1080,7 @@ void PrintObject::detect_surfaces_type()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
|
||||
@ -1115,6 +1139,7 @@ void PrintObject::process_external_surfaces()
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
|
||||
if (layer_expansions_and_voids[layer_idx + 1]) {
|
||||
// Layer above is partially filled with solid infill (top, bottom, bridging...),
|
||||
@ -1140,6 +1165,7 @@ void PrintObject::process_external_surfaces()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
// 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::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[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 size_t num_regions = this->num_printing_regions();
|
||||
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::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[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 };
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
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";
|
||||
grain_size = 1;
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[this, region_id, &cache_top_botom_regions]
|
||||
(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());
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
m_print->throw_if_canceled();
|
||||
@ -1624,6 +1654,7 @@ void PrintObject::bridge_over_infill()
|
||||
tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
const Layer *layer = po->get_layer(lidx);
|
||||
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),
|
||||
&layers_to_generate_infill,
|
||||
&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++) {
|
||||
size_t lidx = layers_to_generate_infill[job_idx];
|
||||
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,
|
||||
&layer_area_covered_by_candidates](
|
||||
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++) {
|
||||
size_t lidx = layers_with_candidates[job_idx];
|
||||
for (const auto &candidate : surfaces_by_layer.at(lidx)) {
|
||||
@ -1990,8 +2023,8 @@ void PrintObject::bridge_over_infill()
|
||||
// reconstruct polygon from polygon sections
|
||||
struct TracedPoly
|
||||
{
|
||||
std::vector<Point> lows;
|
||||
std::vector<Point> highs;
|
||||
Points lows;
|
||||
Points highs;
|
||||
};
|
||||
|
||||
std::vector<TracedPoly> current_traced_polys;
|
||||
@ -2070,6 +2103,7 @@ void PrintObject::bridge_over_infill()
|
||||
determine_bridging_angle,
|
||||
construct_anchored_polygon](
|
||||
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 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];
|
||||
@ -2242,6 +2276,7 @@ void PrintObject::bridge_over_infill()
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end())
|
||||
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) {
|
||||
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;
|
||||
|
||||
// Transform the triangle into worlds coords.
|
||||
|
@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp)
|
||||
Points ConcaveHull::calculate_centroids() const
|
||||
{
|
||||
// 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::back_inserter(centroids),
|
||||
[](const Polygon &poly) { return centroid(poly); });
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
@ -13,7 +15,7 @@ namespace Slic3r {
|
||||
class ExPolygon;
|
||||
class Polygon;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <libslic3r/Polygon.hpp>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/AABBMesh.hpp>
|
||||
|
||||
@ -14,9 +15,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
struct SupportTreeConfig
|
||||
|
@ -305,7 +305,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st
|
||||
|
||||
bench.stop();
|
||||
|
||||
if (!m.empty())
|
||||
if (!po.m_preview_meshes[step]->empty())
|
||||
BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec();
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Preview failed!";
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
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
|
||||
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<Vec3i> final_indices;
|
||||
final_indices.reserve(face_indices.size());
|
||||
|
@ -8,10 +8,13 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ClipperLib { class PolyNode; }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
|
||||
}
|
||||
|
||||
class 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);
|
||||
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.
|
||||
// Returns pairs of PrintObject idx and instance of that PrintObject.
|
||||
|
1999
src/libslic3r/Support/SupportCommon.cpp
Normal file
1999
src/libslic3r/Support/SupportCommon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
155
src/libslic3r/Support/SupportCommon.hpp
Normal file
155
src/libslic3r/Support/SupportCommon.hpp
Normal 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_ */
|
108
src/libslic3r/Support/SupportDebug.cpp
Normal file
108
src/libslic3r/Support/SupportDebug.cpp
Normal 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 */
|
18
src/libslic3r/Support/SupportDebug.hpp
Normal file
18
src/libslic3r/Support/SupportDebug.hpp
Normal 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_ */
|
146
src/libslic3r/Support/SupportLayer.hpp
Normal file
146
src/libslic3r/Support/SupportLayer.hpp
Normal 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_ */
|
File diff suppressed because it is too large
Load Diff
101
src/libslic3r/Support/SupportMaterial.hpp
Normal file
101
src/libslic3r/Support/SupportMaterial.hpp
Normal 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_ */
|
144
src/libslic3r/Support/SupportParameters.cpp
Normal file
144
src/libslic3r/Support/SupportParameters.cpp
Normal 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 ®ion = 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
|
96
src/libslic3r/Support/SupportParameters.hpp
Normal file
96
src/libslic3r/Support/SupportParameters.hpp
Normal 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_ */
|
@ -9,14 +9,15 @@
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "TreeSupport.hpp"
|
||||
|
||||
#include "BuildVolume.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "../BuildVolume.hpp"
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Point.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#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()
|
||||
#define error_level_not_in_cache error
|
||||
|
||||
static constexpr const bool polygons_strictly_simple = false;
|
||||
|
||||
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
|
||||
{
|
||||
const PrintConfig &print_config = print_object.print()->config();
|
||||
@ -76,7 +79,9 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
|
||||
// this->support_interface_skip_height =
|
||||
// this->support_infill_angles =
|
||||
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->support_roof_angles =
|
||||
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())))),
|
||||
[&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
|
||||
outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution));
|
||||
outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution, polygons_strictly_simple);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@ -424,7 +429,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
|
||||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
tree_supports_show_error("Not precalculated Placeable areas requested."sv, false);
|
||||
tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false);
|
||||
}
|
||||
if (orig_radius == 0)
|
||||
// Placable areas for radius 0 are calculated in the general collision code.
|
||||
@ -585,7 +590,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
if (processing_last_mesh) {
|
||||
if (! dst.empty())
|
||||
collisions = union_(collisions, dst);
|
||||
dst = polygons_simplify(collisions, min_resolution);
|
||||
dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
|
||||
} else
|
||||
append(dst, std::move(collisions));
|
||||
throw_on_cancel();
|
||||
@ -595,21 +600,24 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
// 3) Optionally calculate placables.
|
||||
if (calculate_placable) {
|
||||
// Now calculate the placable areas.
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(data.idx_begin, 1), data.idx_end),
|
||||
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh,
|
||||
min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel]
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end),
|
||||
[&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
|
||||
min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
|
||||
(const tbb::blocked_range<LayerIndex>& range) {
|
||||
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
|
||||
LayerIndex layer_idx_below = layer_idx - 1;
|
||||
LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1;
|
||||
assert(layer_idx_below >= 0);
|
||||
const Polygons ¤t = collision_areas_offsetted[layer_idx];
|
||||
const Polygons &below = collision_areas_offsetted[layer_idx_below];
|
||||
Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
|
||||
const Polygons &below = outlines[layer_idx_below];
|
||||
Polygons placable = diff(
|
||||
// Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface.
|
||||
offset(below, xy_distance),
|
||||
layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
|
||||
auto &dst = data_placeable[layer_idx];
|
||||
if (processing_last_mesh) {
|
||||
if (! dst.empty())
|
||||
placable = union_(placable, dst);
|
||||
dst = polygons_simplify(placable, min_resolution);
|
||||
dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
|
||||
} else
|
||||
append(dst, placable);
|
||||
throw_on_cancel();
|
||||
@ -657,7 +665,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerP
|
||||
data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify(
|
||||
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
|
||||
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
|
||||
m_min_resolution));
|
||||
m_min_resolution, polygons_strictly_simple));
|
||||
throw_on_cancel();
|
||||
}
|
||||
}
|
||||
@ -744,7 +752,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
|
||||
ClipperLib::jtRound, m_min_resolution));
|
||||
if (task.to_model)
|
||||
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
|
||||
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution);
|
||||
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple);
|
||||
data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
|
||||
throw_on_cancel();
|
||||
}
|
||||
@ -865,12 +873,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector<RadiusLayerPa
|
||||
data[layer_idx - min_layer_bottom] = polygons_simplify(
|
||||
// radius contains m_current_min_xy_dist_delta already if required
|
||||
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())
|
||||
data_min[layer_idx - min_layer_bottom] =
|
||||
polygons_simplify(
|
||||
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
|
||||
m_min_resolution);
|
||||
m_min_resolution, polygons_strictly_simple);
|
||||
throw_on_cancel();
|
||||
}
|
||||
});
|
@ -14,9 +14,9 @@
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "../Point.hpp"
|
||||
#include "../Polygon.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@ -94,7 +94,9 @@ struct TreeSupportMeshGroupSettings {
|
||||
bool support_roof_enable { false };
|
||||
// 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.
|
||||
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 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.)) };
|
||||
@ -215,6 +217,7 @@ public:
|
||||
void clear() {
|
||||
this->clear_all_but_object_collision();
|
||||
m_collision_cache.clear();
|
||||
m_placeable_areas_cache.clear();
|
||||
}
|
||||
void clear_all_but_object_collision() {
|
||||
//m_collision_cache.clear_all_but_radius0();
|
||||
@ -223,7 +226,7 @@ public:
|
||||
m_avoidance_cache_slow.clear();
|
||||
m_avoidance_cache_to_model.clear();
|
||||
m_avoidance_cache_to_model_slow.clear();
|
||||
m_placeable_areas_cache.clear();
|
||||
m_placeable_areas_cache.clear_all_but_radius0();
|
||||
m_avoidance_cache_holefree.clear();
|
||||
m_avoidance_cache_holefree_to_model.clear();
|
||||
m_wall_restrictions_cache.clear();
|
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@
|
||||
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Support/SupportLayer.hpp"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
@ -39,10 +40,7 @@ namespace Slic3r
|
||||
// Forward declarations
|
||||
class Print;
|
||||
class PrintObject;
|
||||
class SupportGeneratorLayer;
|
||||
struct SlicingParameters;
|
||||
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
|
||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
|
||||
namespace FFFTreeSupport
|
||||
{
|
||||
@ -93,6 +91,8 @@ struct AreaIncreaseSettings
|
||||
|
||||
struct TreeSupportSettings;
|
||||
|
||||
// #define TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
|
||||
struct SupportElementStateBits {
|
||||
SupportElementStateBits() :
|
||||
@ -102,6 +102,10 @@ struct SupportElementStateBits {
|
||||
supports_roof(false),
|
||||
can_use_safe_radius(false),
|
||||
skip_ovalisation(false),
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
lost(false),
|
||||
verylost(false),
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
deleted(false),
|
||||
marked(false)
|
||||
{}
|
||||
@ -136,6 +140,12 @@ struct SupportElementStateBits {
|
||||
*/
|
||||
bool skip_ovalisation : 1;
|
||||
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
// Likely a lost branch, debugging information.
|
||||
bool lost : 1;
|
||||
bool verylost : 1;
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// Not valid anymore, to be deleted.
|
||||
bool deleted : 1;
|
||||
|
||||
@ -354,10 +364,6 @@ public:
|
||||
* \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;
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
@ -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_ */
|
@ -13,6 +13,7 @@
|
||||
#include "PrintBase.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "tbb/parallel_for.h"
|
||||
#include "tbb/blocked_range.h"
|
||||
@ -24,6 +25,10 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
#include <oneapi/tbb/concurrent_vector.h>
|
||||
#include <oneapi/tbb/parallel_for.h>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#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)
|
||||
{
|
||||
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(
|
||||
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;
|
||||
if (fabs(point.distance) < 1.5 * flow_width) {
|
||||
curled_up_height = 0.85 * prev_line_curled_height;
|
||||
float curled_up_height = 0;
|
||||
if (fabs(distance) < 3.0 * flow_width) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -230,15 +318,8 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
|
||||
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
|
||||
const Params ¶ms)
|
||||
{
|
||||
if (entity->is_collection()) {
|
||||
std::vector<ExtrusionLine> checked_lines_out;
|
||||
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()) {
|
||||
assert(!entity->is_collection());
|
||||
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
|
||||
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
|
||||
return {};
|
||||
@ -312,18 +393,19 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
|
||||
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};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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
|
||||
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;
|
||||
|
||||
SupportPointCause 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;
|
||||
}
|
||||
// Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
|
||||
// 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,
|
||||
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) {
|
||||
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) {
|
||||
line_out.support_point_generated = potential_cause;
|
||||
line_out.form_quality = 0.5f;
|
||||
@ -349,8 +431,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
|
||||
bridged_distance = 0.0f;
|
||||
}
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance, 0.5 * (prev_point.curvature + curr_point.curvature),
|
||||
layer_region->layer()->height, flow_width, bottom_line.curled_up_height,
|
||||
params);
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
@ -737,7 +782,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params ¶ms)
|
||||
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
|
||||
const PrecomputedSliceConnections &precomputed_slices_connections,
|
||||
const PrintTryCancel &cancel_func,
|
||||
const Params ¶ms)
|
||||
{
|
||||
SupportPoints supp_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) {
|
||||
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
|
||||
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
|
||||
SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer);
|
||||
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
|
||||
const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx];
|
||||
|
||||
#ifdef DETAILED_DEBUG_LOGS
|
||||
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;
|
||||
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, ¶ms](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{};
|
||||
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.
|
||||
// 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) {
|
||||
const LayerSlice &slice = layer->lslices_ex.at(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];
|
||||
|
||||
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
|
||||
weakest_conn.print_info("weakest connection info: ");
|
||||
#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
|
||||
// account for most of the curling and possible crashes, so on them we will run also global stability check
|
||||
for (const auto &island : slice.islands) {
|
||||
// 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();
|
||||
// }
|
||||
for (const auto &l : unstable_lines_per_slice[slice_idx]) {
|
||||
assert(l.support_point_generated.has_value());
|
||||
reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero());
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) ||
|
||||
line.len < EPSILON) {
|
||||
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_slice_ext_perims_lines.end());
|
||||
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(),
|
||||
current_slice_lines_distancer.get_lines().end());
|
||||
} // slice iterations
|
||||
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
|
||||
} // 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 ¶ms)
|
||||
{
|
||||
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
|
||||
auto [supp_points, objects] = results;
|
||||
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{};
|
||||
|
||||
for (SupportLayer *l : layers) {
|
||||
l->malformed_lines.clear();
|
||||
l->curled_lines.clear();
|
||||
std::vector<ExtrusionLine> current_layer_lines;
|
||||
|
||||
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);
|
||||
|
||||
for (size_t i = 0; i < annotated_points.size(); ++i) {
|
||||
ExtendedPoint &curr_point = annotated_points[i];
|
||||
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
|
||||
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
|
||||
curr_point.position.cast<float>(), line_len, extrusion};
|
||||
const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
|
||||
const ExtendedPoint &b = annotated_points[i];
|
||||
ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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 v2 = (curr_point.position.cast<float>() - nearest_prev_layer_line.a);
|
||||
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
|
||||
if (d > 0) {
|
||||
curr_point.distance *= -1.0f;
|
||||
}
|
||||
Vec2f v1 = (bottom_line.b - bottom_line.a);
|
||||
Vec2f v2 = (a.position.cast<float>() - bottom_line.a);
|
||||
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
|
||||
float sign = (d > 0) ? -1.0f : 1.0f;
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height,
|
||||
flow_width, bottom_line.curled_up_height, params);
|
||||
|
||||
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) {
|
||||
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 ¶ms)
|
||||
{
|
||||
#ifdef DEBUG_FILES
|
||||
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
|
||||
|
||||
LD prev_layer_lines{};
|
||||
|
||||
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>();
|
||||
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
|
||||
std::vector<ExtrusionLine> current_layer_lines;
|
||||
for (const LayerRegion *layer_region : l->regions()) {
|
||||
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
|
||||
|
||||
if (!extrusion->role().is_external_perimeter()) continue;
|
||||
if (!extrusion->role().is_external_perimeter())
|
||||
continue;
|
||||
|
||||
Points extrusion_pts;
|
||||
extrusion->collect_points(extrusion_pts);
|
||||
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,
|
||||
params.bridge_distance);
|
||||
auto annotated_points = estimate_points_properties<true, true, false, false>(extrusion_pts, prev_layer_lines, flow_width,
|
||||
params.bridge_distance);
|
||||
for (size_t i = 0; i < annotated_points.size(); ++i) {
|
||||
ExtendedPoint &curr_point = annotated_points[i];
|
||||
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
|
||||
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
|
||||
curr_point.position.cast<float>(), line_len, extrusion};
|
||||
const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
|
||||
const ExtendedPoint &b = annotated_points[i];
|
||||
ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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 :
|
||||
1.0f;
|
||||
curr_point.distance *= sign;
|
||||
// correctify the distance sign using slice polygons
|
||||
float sign = (prev_layer_boundary.distance_from_lines<true>(middle.cast<double>()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature),
|
||||
l->height, flow_width, bottom_line.curled_up_height, params);
|
||||
|
||||
current_layer_lines.push_back(line_out);
|
||||
}
|
||||
@ -1153,7 +1205,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms)
|
||||
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
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 ¶ms)
|
||||
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) {
|
||||
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]);
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
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]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -42,7 +42,7 @@ struct Params
|
||||
BrimType brim_type;
|
||||
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 curling_tolerance_limit = 0.1f;
|
||||
|
||||
|
@ -58,8 +58,6 @@
|
||||
|
||||
// Enable alternative version of file_wildcards()
|
||||
#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_
|
||||
|
@ -10,3 +10,12 @@ Slic3r::Timer::~Timer()
|
||||
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,5 +27,66 @@ public:
|
||||
~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
|
||||
#endif // libslic3r_Timer_hpp_
|
||||
|
||||
#endif // libslic3r_Timer_hpp_
|
||||
|
@ -10,11 +10,15 @@
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/scalable_allocator.h>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
// #define EXPENSIVE_DEBUG_CHECKS
|
||||
@ -32,6 +36,13 @@
|
||||
#include <boost/thread/mutex.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
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
@ -139,7 +150,7 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
using IntersectionLines = std::vector<IntersectionLine>;
|
||||
using IntersectionLines = std::vector<IntersectionLine, tbb::scalable_allocator<IntersectionLine>>;
|
||||
|
||||
enum class FacetSliceType {
|
||||
NoSlice = 0,
|
||||
@ -351,6 +362,21 @@ inline FacetSliceType slice_facet(
|
||||
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>
|
||||
void slice_facet_at_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.
|
||||
const std::vector<float> &zs,
|
||||
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)]) };
|
||||
|
||||
@ -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) {
|
||||
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -395,8 +421,8 @@ static inline std::vector<IntersectionLines> slice_make_lines(
|
||||
const std::vector<float> &zs,
|
||||
const ThrowOnCancel throw_on_cancel_fn)
|
||||
{
|
||||
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines());
|
||||
std::array<std::mutex, 64> lines_mutex;
|
||||
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines{});
|
||||
LinesMutexes lines_mutex;
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
@ -475,7 +501,7 @@ void slice_facet_with_slabs(
|
||||
const int num_edges,
|
||||
const std::vector<float> &zs,
|
||||
SlabLines &lines,
|
||||
std::array<std::mutex, 64> &lines_mutex)
|
||||
LinesMutexes &lines_mutex)
|
||||
{
|
||||
const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx];
|
||||
stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] };
|
||||
@ -494,7 +520,7 @@ void slice_facet_with_slabs(
|
||||
auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) {
|
||||
if (reverse)
|
||||
il.reverse();
|
||||
boost::lock_guard<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);
|
||||
};
|
||||
|
||||
@ -530,7 +556,7 @@ void slice_facet_with_slabs(
|
||||
};
|
||||
// Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
|
||||
// if (! ProjectionFromTop) il.reverse();
|
||||
boost::lock_guard<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);
|
||||
}
|
||||
} else {
|
||||
@ -649,7 +675,7 @@ void slice_facet_with_slabs(
|
||||
if (! ProjectionFromTop)
|
||||
il.reverse();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -804,8 +830,8 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
|
||||
std::pair<SlabLines, SlabLines> out;
|
||||
SlabLines &lines_top = out.first;
|
||||
SlabLines &lines_bottom = out.second;
|
||||
std::array<std::mutex, 64> lines_mutex_top;
|
||||
std::array<std::mutex, 64> lines_mutex_bottom;
|
||||
LinesMutexes lines_mutex_top;
|
||||
LinesMutexes lines_mutex_bottom;
|
||||
|
||||
if (top) {
|
||||
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.
|
||||
static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
|
||||
static ExPolygons make_expolygons_simple(IntersectionLines &lines)
|
||||
{
|
||||
ExPolygons slices;
|
||||
Polygons holes;
|
||||
|
@ -106,8 +106,8 @@ enum Axis {
|
||||
NUM_AXES_WITH_UNKNOWN,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
template <typename T, typename Alloc, typename Alloc2>
|
||||
inline void append(std::vector<T, Alloc> &dest, const std::vector<T, Alloc2> &src)
|
||||
{
|
||||
if (dest.empty())
|
||||
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());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
||||
template <typename T, typename Alloc>
|
||||
inline void append(std::vector<T, Alloc> &dest, std::vector<T, Alloc> &&src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
|
@ -107,6 +107,7 @@
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/scalable_allocator.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/task_group.h>
|
||||
|
||||
|
@ -291,7 +291,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
(config->opt_bool("support_material") ||
|
||||
config->opt_int("support_material_enforce_layers") > 0);
|
||||
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);
|
||||
|
||||
for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",
|
||||
|
@ -615,7 +615,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
|
||||
if (m_layer_height_profile_modified) {
|
||||
wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit"));
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2944,8 +2944,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
const double delta = direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta();
|
||||
if (wxGetKeyState(WXK_SHIFT)) {
|
||||
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 auto mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() });
|
||||
const Vec3d screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 });
|
||||
const Vec3d mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() });
|
||||
const Vec3d displacement = mouse_3d_pos - screen_center_3d_pos;
|
||||
wxGetApp().plater()->get_camera().translate_world(displacement);
|
||||
const double origin_zoom = wxGetApp().plater()->get_camera().get_zoom();
|
||||
@ -3190,9 +3190,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
m_canvas->SetFocus();
|
||||
|
||||
if (evt.Entering()) {
|
||||
if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown())
|
||||
// reset dragging state if the user released the mouse button outside the 3D scene
|
||||
m_mouse.dragging = false;
|
||||
if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown()) {
|
||||
// ensure to stop layers editing if enabled
|
||||
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__)
|
||||
// // 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
|
||||
std::set<int> obj_idx_for_update_info_items;
|
||||
for (const std::pair<int, int>& i : done) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
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->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
|
||||
// similar to void Plater::priv::selection_changed()
|
||||
@ -3723,6 +3733,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||
}
|
||||
|
||||
// Fixes sinking/flying instances
|
||||
std::set<int> obj_idx_for_update_info_items;
|
||||
for (const std::pair<int, int>& i : done) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
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);
|
||||
}
|
||||
|
||||
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())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||
@ -3792,6 +3806,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||
}
|
||||
|
||||
// Fixes sinking/flying instances
|
||||
std::set<int> obj_idx_for_update_info_items;
|
||||
for (const std::pair<int, int>& i : done) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
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->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())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||
@ -3855,6 +3873,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
||||
}
|
||||
|
||||
// Fixes sinking/flying instances
|
||||
std::set<int> obj_idx_for_update_info_items;
|
||||
for (const std::pair<int, int>& i : done) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
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->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));
|
||||
|
||||
@ -3917,6 +3939,7 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type)
|
||||
}
|
||||
|
||||
// Fixes sinking/flying instances
|
||||
std::set<int> obj_idx_for_update_info_items;
|
||||
for (const std::pair<int, int>& i : done) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
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->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));
|
||||
|
||||
@ -6239,10 +6265,10 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
|
||||
m_layers_editing.last_action =
|
||||
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);
|
||||
}
|
||||
|
||||
m_layers_editing.adjust_layer_height_profile();
|
||||
_refresh_if_shown_on_screen();
|
||||
m_layers_editing.adjust_layer_height_profile();
|
||||
_refresh_if_shown_on_screen();
|
||||
}
|
||||
|
||||
// Automatic action on mouse down with the same coordinate.
|
||||
_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)
|
||||
{
|
||||
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()
|
||||
|
@ -14,7 +14,7 @@ namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Polygon;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
class BuildVolume;
|
||||
|
||||
namespace GUI {
|
||||
|
@ -3381,6 +3381,9 @@ void GUI_App::on_version_read(wxCommandEvent& evt)
|
||||
? _u8L("Check for application update has failed.")
|
||||
: 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
|
||||
, NotificationManager::NotificationLevel::RegularNotificationLevel
|
||||
, text
|
||||
|
@ -743,6 +743,14 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}, menu_item_printable->GetId());
|
||||
|
@ -85,6 +85,17 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||
|
||||
// describe control behavior
|
||||
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
|
||||
// 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
|
||||
@ -2983,21 +2994,19 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
|
||||
wxGetApp().notification_manager()->push_updated_item_info_notification(type);
|
||||
}
|
||||
else if (shows && ! should_show) {
|
||||
if (!selections)
|
||||
if (!selections && IsSelected(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);
|
||||
}
|
||||
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()
|
||||
{
|
||||
// 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;
|
||||
GetSelections(sels);
|
||||
if (sels.IsEmpty())
|
||||
|
@ -434,18 +434,15 @@ void GLGizmoFdmSupports::apply_data_from_backend()
|
||||
mesh_id++;
|
||||
auto selector = selectors.find(mv->id().id);
|
||||
if (selector != selectors.end()) {
|
||||
mv->supported_facets.set(selector->second.selector);
|
||||
m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true);
|
||||
m_triangle_selectors[mesh_id]->deserialize(selector->second.selector.serialize(), true);
|
||||
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
|
||||
|
@ -24,14 +24,8 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filen
|
||||
|
||||
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 (m_hover_id != -1) {
|
||||
m_mouse_left_down = true;
|
||||
Selection &selection = m_parent.get_selection();
|
||||
if (selection.is_single_full_instance()) {
|
||||
// Rotate the object so the normal points downward:
|
||||
@ -42,16 +36,8 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mouse_event.LeftUp()) {
|
||||
if (m_mouse_left_down) {
|
||||
// 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;
|
||||
else if (mouse_event.LeftUp())
|
||||
return m_hover_id != -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ private:
|
||||
|
||||
std::vector<PlaneData> m_planes;
|
||||
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;
|
||||
int m_old_instance_id{ -1 };
|
||||
|
||||
|
@ -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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 (mo) {
|
||||
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();
|
||||
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)
|
||||
last_comp_step = -1;
|
||||
|
||||
@ -123,6 +127,8 @@ void GLGizmoSlaSupports::on_render()
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
show_sla_supports(!m_editing_mode);
|
||||
|
||||
render_volumes();
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -261,11 +261,12 @@ void Raycaster::on_update()
|
||||
// For sla printers we use the mesh generated by the backend
|
||||
std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr;
|
||||
const SLAPrintObject* po = get_pool()->selection_info()->print_object();
|
||||
if (po)
|
||||
if (po != nullptr)
|
||||
preview_mesh_ptr = po->get_mesh_to_print();
|
||||
else
|
||||
preview_mesh_ptr.reset();
|
||||
|
||||
if (preview_mesh_ptr)
|
||||
m_sla_mesh_cache = TriangleMesh{*preview_mesh_ptr};
|
||||
m_sla_mesh_cache = (preview_mesh_ptr != nullptr) ? TriangleMesh{ *preview_mesh_ptr } : TriangleMesh();
|
||||
|
||||
if (!m_sla_mesh_cache.empty()) {
|
||||
m_sla_mesh_cache.transform(po->trafo().inverse());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user