Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_sla_switch_view

This commit is contained in:
enricoturri1966 2023-05-10 08:15:35 +02:00
commit b13a51f400
45 changed files with 3818 additions and 3141 deletions

View File

@ -546,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES})
endforeach()
find_package(NLopt 1.4 REQUIRED)
slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release)
if(SLIC3R_STATIC)
set(OPENVDB_USE_STATIC_LIBS ON)

View File

@ -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;
}
@ -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)
{
@ -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);
@ -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);
@ -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);
}
//------------------------------------------------------------------------------

View File

@ -52,6 +52,7 @@
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <array>
#include <vector>
#include <deque>
#include <stdexcept>
@ -199,7 +200,8 @@ double Area(const Path &poly);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path);
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd);
// Union with "strictly simple" fix enabled.
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415);
@ -284,7 +286,25 @@ enum EdgeSide { esLeft = 1, esRight = 2};
using OutPts = std::vector<OutPt, Allocator<OutPt>>;
struct OutRec;
// Output polygon.
struct OutRec {
int Idx;
bool IsHole;
bool IsOpen;
//The 'FirstLeft' field points to another OutRec that contains or is the
//'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
//parsed left from the current edge (owning OutRec) until the owner OutRec
//is found. This field simplifies sorting the polygons into a tree structure
//which reflects the parent/child relationships of all polygons.
//This field should be renamed Parent, and will be later.
OutRec* FirstLeft;
// Used only by void Clipper::BuildResult2(PolyTree& polytree)
PolyNode* PolyNd;
// Linked list of output points, dynamically allocated.
OutPt* Pts;
OutPt* BottomPt;
};
struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {}
@ -432,12 +452,12 @@ protected:
private:
// Output polygons.
std::vector<OutRec*, Allocator<OutRec*>> m_PolyOuts;
std::deque<OutRec, Allocator<OutRec>> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize.
std::vector<OutPt*, Allocator<OutPt*>> m_OutPts;
static constexpr const size_t m_OutPtsChunkSize = 32;
std::deque<std::array<OutPt, m_OutPtsChunkSize>, Allocator<std::array<OutPt, m_OutPtsChunkSize>>> m_OutPts;
// List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk.
OutPt *m_OutPtsFree;
size_t m_OutPtsChunkSize;
size_t m_OutPtsChunkLast;
std::vector<Join, Allocator<Join>> m_Joins;
@ -482,7 +502,7 @@ private:
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2) const;
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
@ -498,7 +518,7 @@ private:
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec) const;
void SetHoleState(TEdge *e, OutRec *outrec);
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec);
@ -508,8 +528,8 @@ private:
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
@ -567,10 +587,11 @@ class clipperException : public std::exception
};
//------------------------------------------------------------------------------
// Union with "strictly simple" fix enabled.
template<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);

View File

@ -232,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;
}
@ -273,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
}
}
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
}
/*!

View File

@ -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

View File

@ -685,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
@ -964,21 +966,18 @@ Polygons union_pt_chained_outside_in(const Polygons &subject)
return retval;
}
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
Polygons simplify_polygons(const Polygons &subject)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths output;
if (preserve_collinear) {
ClipperLib::Clipper c;
c.PreserveCollinear(true);
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} else {
output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero);
}
ClipperLib::Clipper c;
// c.PreserveCollinear(true);
//FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
// convert into Slic3r polygons
return to_polygons(std::move(output));
}
@ -987,12 +986,10 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
if (! preserve_collinear)
return union_ex(simplify_polygons(subject, false));
ClipperLib::PolyTree polytree;
ClipperLib::Clipper c;
c.PreserveCollinear(true);
// c.PreserveCollinear(true);
//FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);

View File

@ -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);

View File

@ -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));
}

View File

@ -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

View File

@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point);
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{
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)

View File

@ -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;
}
}
}

View File

@ -40,11 +40,12 @@ template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l)
// Distance to the closest point of line.
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.

View File

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

View File

@ -81,7 +81,7 @@ public:
}
}
static Points _douglas_peucker(const Points &points, const double tolerance);
static Points douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double& tolerance);
inline auto begin() { return points.begin(); }

View File

@ -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.

View File

@ -96,7 +96,7 @@ bool Polygon::make_clockwise()
void Polygon::douglas_peucker(double tolerance)
{
this->points.push_back(this->points.front());
Points p = MultiPoint::_douglas_peucker(this->points, tolerance);
Points p = MultiPoint::douglas_peucker(this->points, tolerance);
p.pop_back();
this->points = std::move(p);
}
@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const
// on the whole polygon
Points points = this->points;
points.push_back(points.front());
Polygon p(MultiPoint::_douglas_peucker(points, tolerance));
Polygon p(MultiPoint::douglas_peucker(points, tolerance));
p.points.pop_back();
Polygons pp;
@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly);
}
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out)
{
Points simplified = MultiPoint::douglas_peucker(points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) {
if (!ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
}
Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */)
{
Polygons out;
out.reserve(source_polygons.size());
for (Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
source_polygon.points.emplace_back(source_polygon.points.front());
simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out);
}
return out;
}
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */)
{
Polygons out;
out.reserve(source_polygons.size());
for (const Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) {
if (! ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out);
}
return out;
}

View File

@ -149,7 +149,8 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
}
}
Polygons polygons_simplify(const Polygons &polys, double tolerance);
Polygons polygons_simplify(Polygons &&polys, double tolerance, bool strictly_simple = true);
Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true);
inline void polygons_rotate(Polygons &polys, double angle)
{

View File

@ -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

View File

@ -444,7 +444,8 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_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",

View File

@ -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"

View File

@ -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.

View File

@ -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))

View File

@ -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"
@ -712,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"

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,14 +9,15 @@
#include "TreeModelVolumes.hpp"
#include "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 &current = 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();
}
});

View File

@ -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();

View File

@ -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.
*/

View File

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

View File

@ -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;

View File

@ -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>

View File

@ -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",

View File

@ -3916,6 +3916,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);
@ -3924,8 +3925,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()
@ -4003,6 +4007,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);
@ -4013,8 +4018,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));
@ -4072,6 +4080,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);
@ -4081,8 +4090,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));
@ -4135,6 +4147,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);
@ -4144,8 +4157,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));
@ -4197,6 +4213,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);
@ -4206,8 +4223,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));

View File

@ -2998,21 +2998,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);
}
}
}
}

View File

@ -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

View File

@ -1634,6 +1634,8 @@ void ImGuiWrapper::init_font(bool compress)
ImFontGlyphRangesBuilder builder;
builder.AddRanges(m_glyph_ranges);
builder.AddChar(ImWchar(0x2026)); // …
if (m_font_cjk) {
// This is a temporary fix of https://github.com/prusa3d/PrusaSlicer/issues/8171. The translation
// contains characters not in the ImGui ranges for simplified Chinese. For now, just add them manually.

View File

@ -1536,6 +1536,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_tree_angle_slow", category_path + "tree_angle_slow");
optgroup->append_single_option_line("support_tree_branch_diameter", category_path + "tree_branch_diameter");
optgroup->append_single_option_line("support_tree_branch_diameter_angle", category_path + "tree_branch_diameter_angle");
optgroup->append_single_option_line("support_tree_branch_diameter_double_wall", category_path + "tree_branch_diameter_double_wall");
optgroup->append_single_option_line("support_tree_tip_diameter", category_path + "tree_tip_diameter");
optgroup->append_single_option_line("support_tree_branch_distance", category_path + "tree_branch_distance");
optgroup->append_single_option_line("support_tree_top_rate", category_path + "tree_top_rate");