Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_custom_gcode_detection

This commit is contained in:
enricoturri1966 2021-04-21 12:32:17 +02:00
commit c7771a576b
73 changed files with 2263 additions and 2165 deletions

View file

@ -287,6 +287,11 @@ bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
}
//----------------------------------------------------------------------
#ifdef CLIPPERLIB_INT32
inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool /* UseFullInt64Range */) {
return int64_t(dy1) * int64_t(dx2) == int64_t(dx1) * int64_t(dy2);
}
#else
inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool UseFullInt64Range) {
return (UseFullInt64Range) ?
// |dx1| < 2^63, |dx2| < 2^63 etc,
@ -296,6 +301,8 @@ inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cI
// therefore the following computation could be done with 64bit arithmetics.
dy1 * dx2 == dx1 * dy2;
}
#endif
inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
{ return SlopesEqual(e1.Delta.X, e1.Delta.Y, e2.Delta.X, e2.Delta.Y, UseFullInt64Range); }
inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range)
@ -363,8 +370,8 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
}
else
{
b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;
b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;
b1 = double(Edge1.Bot.X) - double(Edge1.Bot.Y) * Edge1.Dx;
b2 = double(Edge2.Bot.X) - double(Edge2.Bot.Y) * Edge2.Dx;
double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);
ip.Y = Round(q);
ip.X = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ?
@ -569,6 +576,7 @@ bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
// ClipperBase class methods ...
//------------------------------------------------------------------------------
#ifndef CLIPPERLIB_INT32
// Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates.
inline void RangeTest(const IntPoint& Pt, bool& useFullRange)
{
@ -583,6 +591,7 @@ inline void RangeTest(const IntPoint& Pt, bool& useFullRange)
RangeTest(Pt, useFullRange);
}
}
#endif // CLIPPERLIB_INT32
//------------------------------------------------------------------------------
// Called from ClipperBase::AddPath() to construct the Local Minima List.
@ -805,13 +814,17 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
try
{
edges[1].Curr = pg[1];
#ifndef CLIPPERLIB_INT32
RangeTest(pg[0], m_UseFullRange);
RangeTest(pg[highI], m_UseFullRange);
#endif // CLIPPERLIB_INT32
InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);
InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);
for (int i = highI - 1; i >= 1; --i)
{
#ifndef CLIPPERLIB_INT32
RangeTest(pg[i], m_UseFullRange);
#endif // CLIPPERLIB_INT32
InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
}
}
@ -967,7 +980,9 @@ void ClipperBase::Clear()
CLIPPERLIB_PROFILE_FUNC();
m_MinimaList.clear();
m_edges.clear();
#ifndef CLIPPERLIB_INT32
m_UseFullRange = false;
#endif // CLIPPERLIB_INT32
m_HasOpenPaths = false;
}
//------------------------------------------------------------------------------
@ -3322,9 +3337,9 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
if(pt2.X == pt1.X && pt2.Y == pt1.Y)
return DoublePoint(0, 0);
double Dx = (double)(pt2.X - pt1.X);
double dy = (double)(pt2.Y - pt1.Y);
double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy );
double Dx = double(pt2.X - pt1.X);
double dy = double(pt2.Y - pt1.Y);
double f = 1.0 / std::sqrt( Dx*Dx + dy*dy );
Dx *= f;
dy *= f;
return DoublePoint(dy, -Dx);
@ -3530,8 +3545,9 @@ void ClipperOffset::DoOffset(double delta)
}
//see offset_triginometry3.svg in the documentation folder ...
if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit);
else m_miterLim = 0.5;
m_miterLim = (MiterLimit > 2) ?
2. / (MiterLimit * MiterLimit) :
0.5;
double y;
if (ArcTolerance <= 0.0) y = def_arc_tolerance;
@ -3633,11 +3649,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt)
{
int j = len - 1;
pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X *
delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
pt1 = IntPoint(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);
pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X *
delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
pt1 = IntPoint(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);
}
else
@ -3662,11 +3676,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt)
{
pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta),
Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
pt1 = IntPoint(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);
pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta),
Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
pt1 = IntPoint(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);
}
else
@ -3753,7 +3765,7 @@ void ClipperOffset::DoRound(int j, int k)
{
double a = std::atan2(m_sinA,
m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1);
auto steps = std::max<int>(Round(m_StepsPerRad * std::fabs(a)), 1);
double X = m_normals[k].X, Y = m_normals[k].Y, X2;
for (int i = 0; i < steps; ++i)
@ -3885,8 +3897,8 @@ void SimplifyPolygons(Paths &polys, PolyFillType fillType)
inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
{
double Dx = ((double)pt1.X - pt2.X);
double dy = ((double)pt1.Y - pt2.Y);
auto Dx = double(pt1.X - pt2.X);
auto dy = double(pt1.Y - pt2.Y);
return (Dx*Dx + dy*dy);
}
//------------------------------------------------------------------------------
@ -3937,8 +3949,8 @@ bool SlopesNearCollinear(const IntPoint& pt1,
bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
{
double Dx = (double)pt1.X - pt2.X;
double dy = (double)pt1.Y - pt2.Y;
auto Dx = double(pt1.X - pt2.X);
auto dy = double(pt1.Y - pt2.Y);
return ((Dx * Dx) + (dy * dy) <= distSqrd);
}
//------------------------------------------------------------------------------

View file

@ -71,12 +71,22 @@ enum PolyType { ptSubject, ptClip };
//see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
// If defined, Clipper will work with 32bit signed int coordinates to reduce memory
// consumption and to speed up exact orientation predicate calculation.
// In that case, coordinates and their differences (vectors of the coordinates) have to fit int32_t.
#define CLIPPERLIB_INT32
// Point coordinate type
typedef int64_t cInt;
// Maximum cInt value to allow a cross product calculation using 32bit expressions.
static cInt const loRange = 0x3FFFFFFF;
// Maximum allowed cInt value.
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#ifdef CLIPPERLIB_INT32
// Coordinates and their differences (vectors of the coordinates) have to fit int32_t.
typedef int32_t cInt;
#else
typedef int64_t cInt;
// Maximum cInt value to allow a cross product calculation using 32bit expressions.
static constexpr cInt const loRange = 0x3FFFFFFF; // 0x3FFFFFFF = 1 073 741 823
// Maximum allowed cInt value.
static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#endif // CLIPPERLIB_INT32
struct IntPoint {
cInt X;
@ -289,7 +299,11 @@ enum EdgeSide { esLeft = 1, esRight = 2};
class ClipperBase
{
public:
ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {}
ClipperBase() :
#ifndef CLIPPERLIB_INT32
m_UseFullRange(false),
#endif // CLIPPERLIB_INT32
m_HasOpenPaths(false) {}
~ClipperBase() { Clear(); }
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
@ -310,9 +324,14 @@ protected:
// Local minima (Y, left edge, right edge) sorted by ascending Y.
std::vector<LocalMinimum> m_MinimaList;
#ifdef CLIPPERLIB_INT32
static constexpr const bool m_UseFullRange = false;
#else // CLIPPERLIB_INT32
// True if the input polygons have abs values higher than loRange, but lower than hiRange.
// False if the input polygons have abs values lower or equal to loRange.
bool m_UseFullRange;
#endif // CLIPPERLIB_INT32
// A vector of edges per each input path.
std::vector<std::vector<TEdge>> m_edges;
// Don't remove intermediate vertices of a collinear sequence of points.

View file

@ -474,8 +474,8 @@ inline _Box<P> _Box<P>::infinite(const P& center) {
// It is important for Mx and My to be strictly less than half of the
// range of type C. width(), height() and area() will not overflow this way.
C Mx = C((std::numeric_limits<C>::lowest() + 2 * getX(center)) / 2.01);
C My = C((std::numeric_limits<C>::lowest() + 2 * getY(center)) / 2.01);
C Mx = C((std::numeric_limits<C>::lowest() + 2 * getX(center)) / 4.01);
C My = C((std::numeric_limits<C>::lowest() + 2 * getY(center)) / 4.01);
ret.maxCorner() = center - P{Mx, My};
ret.minCorner() = center + P{Mx, My};

View file

@ -220,7 +220,9 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
auto next = std::next(first);
while(next != sl::cend(sh)) {
edgelist.emplace_back(*(first), *(next));
if (pl::magnsq(*next - *first) > 0)
edgelist.emplace_back(*(first), *(next));
++first; ++next;
}
}
@ -230,7 +232,9 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
auto next = std::next(first);
while(next != sl::cend(other)) {
edgelist.emplace_back(*(next), *(first));
if (pl::magnsq(*next - *first) > 0)
edgelist.emplace_back(*(next), *(first));
++first; ++next;
}
}

View file

@ -40,7 +40,7 @@ void BridgeDetector::initialize()
this->angle = -1.;
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
Polygons grown = offset(this->expolygons, float(this->spacing));
// Detect possible anchoring edges of this bridging region.
// Detect what edges lie on lower slices by turning bridge contour and holes

View file

@ -57,6 +57,7 @@ err:
}
#endif /* CLIPPER_UTILS_DEBUG */
#ifdef CLIPPERUTILS_OFFSET_SCALE
void scaleClipperPolygon(ClipperLib::Path &polygon)
{
CLIPPERUTILS_PROFILE_FUNC();
@ -98,6 +99,7 @@ void unscaleClipperPolygons(ClipperLib::Paths &polygons)
pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
}
}
#endif // CLIPPERUTILS_OFFSET_SCALE
//-----------------------------------------------------------
// legacy code from Clipper documentation
@ -222,8 +224,10 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset
ClipperLib::ClipperOffset co;
@ -231,14 +235,20 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType
co.ArcTolerance = miterLimit;
else
co.MiterLimit = miterLimit;
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta;
#endif // CLIPPERUTILS_OFFSET_SCALE
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPaths(input, joinType, endType);
ClipperLib::Paths retval;
co.Execute(retval, delta_scaled);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(retval);
#endif // CLIPPERUTILS_OFFSET_SCALE
return retval;
}
@ -257,14 +267,24 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
{
// printf("new ExPolygon offset\n");
// 1) Offset the outer contour.
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta;
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::Paths contours;
{
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -278,17 +298,23 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
holes.reserve(expolygon.holes.size());
for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths out;
co.Execute(out, - delta_scaled);
holes.insert(holes.end(), out.begin(), out.end());
append(holes, std::move(out));
}
}
@ -305,7 +331,9 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
}
// 4) Unscale the output.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return output;
}
@ -315,7 +343,11 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta,
ClipperLib::JoinType joinType, double miterLimit)
{
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta;
#endif // CLIPPERUTILS_OFFSET_SCALE
// Offsetted ExPolygons before they are united.
ClipperLib::Paths contours_cummulative;
contours_cummulative.reserve(expolygons.size());
@ -327,10 +359,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
ClipperLib::Paths contours;
{
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -351,10 +389,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
{
for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -413,8 +457,10 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
output = std::move(contours_cummulative);
}
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output.
unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return output;
}
@ -425,8 +471,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
// read input
ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
// prepare ClipperOffset object
ClipperLib::ClipperOffset co;
@ -435,8 +483,13 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
} else {
co.MiterLimit = miterLimit;
}
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE);
float delta_scaled2 = delta2 * float(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
float delta_scaled1 = delta1;
float delta_scaled2 = delta2;
#endif // CLIPPERUTILS_OFFSET_SCALE
co.ShortestEdgeLength = double(std::max(std::abs(delta_scaled1), std::abs(delta_scaled2)) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR);
// perform first offset
@ -450,8 +503,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
ClipperLib::Paths retval;
co.Execute(retval, delta_scaled2);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(retval);
#endif // CLIPPERUTILS_OFFSET_SCALE
return retval;
}
@ -789,8 +844,10 @@ void safety_offset(ClipperLib::Paths* paths)
{
CLIPPERUTILS_PROFILE_FUNC();
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(*paths);
#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset (delta = scale 1e-05)
ClipperLib::ClipperOffset co;
@ -816,7 +873,11 @@ void safety_offset(ClipperLib::Paths* paths)
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute);
// offset outside by 10um
ClipperLib::Paths out_this;
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE));
#else // CLIPPERUTILS_OFFSET_SCALE
co.Execute(out_this, ccw ? 10.f : -10.f);
#endif // CLIPPERUTILS_OFFSET_SCALE
if (! ccw) {
// Reverse the resulting contours once again.
for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it)
@ -830,8 +891,10 @@ void safety_offset(ClipperLib::Paths* paths)
}
*paths = std::move(out);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(*paths);
#endif // CLIPPERUTILS_OFFSET_SCALE
}
Polygons top_level_islands(const Slic3r::Polygons &polygons)
@ -925,8 +988,10 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
// Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt.
auto add_offset_point = [&out](Vec2d pt) {
#ifdef CLIPPERUTILS_OFFSET_SCALE
pt *= double(CLIPPER_OFFSET_SCALE);
pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0));
#endif // CLIPPERUTILS_OFFSET_SCALE
pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0));
out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
};
@ -1075,8 +1140,10 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output.
unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return ClipperPaths_to_Slic3rPolygons(output);
}
@ -1119,8 +1186,10 @@ for (const std::vector<float>& ds : deltas)
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output.
unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return ClipperPaths_to_Slic3rPolygons(output);
}
@ -1152,7 +1221,9 @@ for (const std::vector<float>& ds : deltas)
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(contours);
#endif // CLIPPERUTILS_OFFSET_SCALE
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
@ -1160,7 +1231,9 @@ for (const std::vector<float>& ds : deltas)
output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
} else {
ClipperLib::Clipper clipper;
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(holes);
#endif // CLIPPERUTILS_OFFSET_SCALE
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
@ -1200,7 +1273,9 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(contours);
#endif // CLIPPERUTILS_OFFSET_SCALE
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
@ -1208,7 +1283,9 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
} else {
ClipperLib::Clipper clipper;
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(holes);
#endif // CLIPPERUTILS_OFFSET_SCALE
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;

View file

@ -12,17 +12,23 @@ using ClipperLib::jtMiter;
using ClipperLib::jtRound;
using ClipperLib::jtSquare;
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
// which is optionally executed by other functions (union, intersection, diff).
// This scaling (cca 130t) is applied over the usual SCALING_FACTOR.
// By the way, is the scalling for offset needed at all?
// The reason to apply this scaling may be to match the resolution of the double mantissa.
#define CLIPPER_OFFSET_POWER_OF_2 17
// 2^17=131072
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE)
#define CLIPPERUTILS_UNSAFE_OFFSET
// #define CLIPPERUTILS_OFFSET_SCALE
#ifdef CLIPPERUTILS_OFFSET_SCALE
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
// which is optionally executed by other functions (union, intersection, diff).
// This scaling (cca 130t) is applied over the usual SCALING_FACTOR.
// By the way, is the scalling for offset needed at all?
// The reason to apply this scaling may be to match the resolution of the double mantissa.
#define CLIPPER_OFFSET_POWER_OF_2 17
// 2^17=131072
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE)
#endif // CLIPPERUTILS_OFFSET_SCALE
namespace Slic3r {
@ -47,8 +53,11 @@ ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType,
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#endif // CLIPPERUTILS_UNSAFE_OFFSET
// offset Polylines
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
@ -65,13 +74,18 @@ inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#endif // CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
@ -81,6 +95,8 @@ Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
#endif // CLIPPERUTILS_UNSAFE_OFFSET
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
@ -319,6 +335,7 @@ void safety_offset(ClipperLib::Paths* paths);
Polygons top_level_islands(const Slic3r::Polygons &polygons);
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit);
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);

View file

@ -471,8 +471,8 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
{
t_config_option_key opt_key = opt_key_src;
std::string value = value_src;
// Both opt_key and value may be modified by _handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by _handle_legacy().
// Both opt_key and value may be modified by handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
this->handle_legacy(opt_key, value);
if (opt_key.empty())
// Ignore the option.

View file

@ -1967,8 +1967,9 @@ public:
int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); }
// In ConfigManipulation::toggle_print_fff_options, it is called on option with type ConfigOptionEnumGeneric* and also ConfigOptionEnum*.
// Thus the virtual method getInt() is used to retrieve the enum value.
template<typename ENUM>
ENUM opt_enum(const t_config_option_key &opt_key) const { return this->option<ConfigOptionEnum<ENUM>>(opt_key)->value; }
ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast<ENUM>(this->option(opt_key)->getInt()); }
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }

View file

@ -217,6 +217,28 @@ inline Polygons to_polygons(const ExPolygons &src)
return polygons;
}
inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygon &src)
{
ConstPolygonPtrs polygons;
polygons.reserve(src.holes.size() + 1);
polygons.emplace_back(&src.contour);
for (const Polygon &hole : src.holes)
polygons.emplace_back(&hole);
return polygons;
}
inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygons &src)
{
ConstPolygonPtrs polygons;
polygons.reserve(number_polygons(src));
for (const ExPolygon &expoly : src) {
polygons.emplace_back(&expoly.contour);
for (const Polygon &hole : expoly.holes)
polygons.emplace_back(&hole);
}
return polygons;
}
inline Polygons to_polygons(ExPolygon &&src)
{
Polygons polygons;

View file

@ -24,11 +24,11 @@ void FillConcentric::_fill_surface_single(
this->spacing = unscale<double>(distance);
}
Polygons loops = to_polygons(std::move(expolygon));
Polygons last = loops;
Polygons loops = to_polygons(expolygon);
ExPolygons last { std::move(expolygon) };
while (! last.empty()) {
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
append(loops, last);
last = offset2_ex(last, -(distance + min_spacing/2), +min_spacing/2);
append(loops, to_polygons(last));
}
// generate paths from the outermost to the innermost, to avoid

View file

@ -58,7 +58,7 @@ void FillLine::_fill_surface_single(
pts.push_back(it->a);
pts.push_back(it->b);
}
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
Polylines polylines = intersection_pl(polylines_src, offset(expolygon, scale_(0.02)), false);
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;

View file

@ -238,13 +238,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
const PrintConfig &print_config = object->print()->config();
const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width;
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)));
float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value)));
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)

View file

@ -1111,7 +1111,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects().front();
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
const double first_layer_height = print.config().first_layer_height.get_abs_value(layer_height);
for (const PrintRegion* region : print.regions()) {
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());
@ -1778,6 +1778,10 @@ namespace ProcessLayer
else {
gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id);
gcode += "\n";
//FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after
// return from M600. Thus the G-code generated by the following line is ignored.
// see GH issue #6362
gcodegen.writer().unretract();
}
}
else {

View file

@ -1087,11 +1087,13 @@ bool
MedialAxis::validate_edge(const VD::edge_type* edge)
{
// prevent overflows and detect almost-infinite edges
#ifndef CLIPPERLIB_INT32
if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED))
return false;
#endif // CLIPPERLIB_INT32
// construct the line representing this edge of the Voronoi diagram
const Line line(

View file

@ -216,7 +216,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
break;
}
// Grown by 3mm.
Polygons polys = offset(to_polygons(bridges[i].expolygon), margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset(bridges[i].expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
if (idx_island == -1) {
BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
} else {

View file

@ -11,7 +11,9 @@
namespace Slic3r {
class Polygon;
typedef std::vector<Polygon> Polygons;
using Polygons = std::vector<Polygon>;
using PolygonPtrs = std::vector<Polygon*>;
using ConstPolygonPtrs = std::vector<const Polygon*>;
class Polygon : public MultiPoint
{

View file

@ -296,6 +296,13 @@ void Preset::normalize(DynamicPrintConfig &config)
if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled)
gap_fill_enabled->value = false;
}
if (auto *first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height", false); first_layer_height && first_layer_height->percent)
if (const auto *layer_height = config.option<ConfigOptionFloat>("layer_height", false); layer_height) {
// Legacy conversion - first_layer_height moved from PrintObject setting to a Print setting, thus we are getting rid of the dependency
// of first_layer_height on PrintObject specific layer_height. Covert the first layer heigth to an absolute value.
first_layer_height->value = first_layer_height->get_abs_value(layer_height->value);
first_layer_height->percent = false;
}
}
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
@ -427,7 +434,7 @@ const std::vector<std::string>& Preset::print_options()
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",

View file

@ -995,10 +995,8 @@ void PrintConfigDef::init_fff_params()
def->label = L("First layer height");
def->category = L("Layers and Perimeters");
def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker "
"bottom layer to improve adhesion and tolerance for non perfect build plates. "
"This can be expressed as an absolute value or as a percentage (for example: 150%) "
"over the default layer height.");
def->sidetext = L("mm or %");
"bottom layer to improve adhesion and tolerance for non perfect build plates.");
def->sidetext = L("mm");
def->ratio_over = "layer_height";
def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false));
@ -2366,6 +2364,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(-1));
def = this->add("support_material_closing_radius", coFloat);
def->label = L("Closing radius");
def->category = L("Support material");
def->tooltip = L("For snug supports, the support regions will be merged using morphological closing operation."
" Gaps smaller than the closing radius will be filled in.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2));
def = this->add("support_material_interface_spacing", coFloat);
def->label = L("Interface pattern spacing");
def->category = L("Support material");
@ -3618,7 +3626,7 @@ std::string FullPrintConfig::validate()
return "--layer-height must be a multiple of print resolution";
// --first-layer-height
if (this->get_abs_value("first_layer_height") <= 0)
if (first_layer_height.value <= 0)
return "Invalid value for --first-layer-height";
// --filament-diameter

View file

@ -496,7 +496,6 @@ public:
ConfigOptionBool dont_support_bridges;
ConfigOptionFloat elefant_foot_compensation;
ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
// Force the generation of solid shells between adjacent materials/volumes.
ConfigOptionBool interface_shells;
@ -530,6 +529,8 @@ public:
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
ConfigOptionEnum<SupportMaterialInterfacePattern> support_material_interface_pattern;
// Morphological closing of support areas. Only used for "sung" supports.
ConfigOptionFloat support_material_closing_radius;
// Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
@ -553,7 +554,6 @@ protected:
OPT_PTR(dont_support_bridges);
OPT_PTR(elefant_foot_compensation);
OPT_PTR(extrusion_width);
OPT_PTR(first_layer_height);
OPT_PTR(infill_only_where_needed);
OPT_PTR(interface_shells);
OPT_PTR(layer_height);
@ -579,6 +579,7 @@ protected:
OPT_PTR(support_material_interface_extruder);
OPT_PTR(support_material_interface_layers);
OPT_PTR(support_material_bottom_interface_layers);
OPT_PTR(support_material_closing_radius);
OPT_PTR(support_material_interface_spacing);
OPT_PTR(support_material_interface_speed);
OPT_PTR(support_material_pattern);
@ -947,6 +948,7 @@ public:
ConfigOptionFloat first_layer_acceleration;
ConfigOptionInts first_layer_bed_temperature;
ConfigOptionFloatOrPercent first_layer_extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionFloatOrPercent first_layer_speed;
ConfigOptionInts first_layer_temperature;
ConfigOptionInts full_fan_speed_layer;
@ -1022,6 +1024,7 @@ protected:
OPT_PTR(first_layer_acceleration);
OPT_PTR(first_layer_bed_temperature);
OPT_PTR(first_layer_extrusion_width);
OPT_PTR(first_layer_height);
OPT_PTR(first_layer_speed);
OPT_PTR(first_layer_temperature);
OPT_PTR(full_fan_speed_layer);

View file

@ -591,6 +591,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_material_style"
|| opt_key == "support_material_xy_spacing"
|| opt_key == "support_material_spacing"
|| opt_key == "support_material_closing_radius"
|| opt_key == "support_material_synchronize_layers"
|| opt_key == "support_material_threshold"
|| opt_key == "support_material_with_sheath"

View file

@ -64,9 +64,9 @@ SlicingParameters SlicingParameters::create_from_config(
coordf_t object_height,
const std::vector<unsigned int> &object_extruders)
{
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ?
object_config.layer_height.value :
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
print_config.first_layer_height.get_abs_value(object_config.layer_height.value);
// If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0,
// print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter,
// which is consistent with the requirement that if support_material_extruder == 0 resp. support_material_interface_extruder == 0,

View file

@ -672,6 +672,7 @@ struct SupportGridParams {
grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()),
support_angle(Geometry::deg2rad(object_config.support_material_angle.value)),
extrusion_width(support_material_flow.spacing()),
support_material_closing_radius(object_config.support_material_closing_radius.value),
expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)),
expansion_to_propagate(-3) {}
@ -679,6 +680,7 @@ struct SupportGridParams {
double grid_resolution;
double support_angle;
double extrusion_width;
double support_material_closing_radius;
coord_t expansion_to_slice;
coord_t expansion_to_propagate;
};
@ -694,7 +696,9 @@ public:
const SupportGridParams &params) :
m_style(params.style),
m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons),
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle)
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle),
m_extrusion_width(params.extrusion_width),
m_support_material_closing_radius(params.support_material_closing_radius)
{
switch (m_style) {
case smsGrid:
@ -878,9 +882,10 @@ public:
return out;
}
case smsSnug:
// Just close the gaps.
float thr = scaled<float>(0.5);
return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
// Merge the support polygons by applying morphological closing and inwards smoothing.
auto closing_distance = scaled<float>(m_support_material_closing_radius);
auto smoothing_distance = scaled<float>(m_extrusion_width);
return smooth_outward(offset(offset_ex(*m_support_polygons, closing_distance), - closing_distance), smoothing_distance);
}
assert(false);
return Polygons();
@ -1128,6 +1133,9 @@ private:
coordf_t m_support_angle;
// X spacing of the support lines parallel with the Y axis.
coordf_t m_support_spacing;
coordf_t m_extrusion_width;
// For snug supports: Morphological closing of support areas.
coordf_t m_support_material_closing_radius;
#ifdef SUPPORT_USE_AGG_RASTERIZER
Vec2i m_grid_size;
@ -3178,7 +3186,7 @@ struct MyLayerExtruded
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
this->layer = rhs.layer;
this->extrusions = std::move(rhs.extrusions);
this->m_polygons_to_extrude = std::move(m_polygons_to_extrude);
this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
rhs.layer = nullptr;
return *this;
}

View file

@ -57,6 +57,8 @@
#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_4_0_ALPHA0)
// Enable exporting lines M73 for remaining time to next printer stop to gcode
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
// Enable a modified version of automatic downscale on load of objects too big
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
#endif // _prusaslicer_technologies_h_

View file

@ -511,20 +511,22 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
//FIXME This could be extremely slow! Use it for tiny meshes only!
ExPolygons TriangleMesh::horizontal_projection() const
{
Polygons pp;
pp.reserve(this->stl.stats.number_of_facets);
ClipperLib::Paths paths;
Polygon p;
p.points.assign(3, Point());
auto delta = scaled<float>(0.01);
std::vector<float> deltas { delta, delta, delta };
paths.reserve(this->stl.stats.number_of_facets);
for (const stl_facet &facet : this->stl.facet_start) {
Polygon p;
p.points.resize(3);
p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
pp.emplace_back(p);
p.make_counter_clockwise();
paths.emplace_back(mittered_offset_path_scaled(p.points, deltas, 3.));
}
// the offset factor was tuned using groovemount.stl
return union_ex(offset(pp, scale_(0.01)), true);
return ClipperPaths_to_Slic3rExPolygons(paths);
}
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
@ -1797,7 +1799,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float clos
// append to the supplied collection
if (safety_offset > 0)
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
expolygons_append(*slices, offset2_ex(union_ex(loops, false), +safety_offset, -safety_offset));
else
expolygons_append(*slices, union_ex(loops, false));
}

View file

@ -113,7 +113,7 @@ void Bed3D::Axes::render() const
glsafe(::glPopMatrix());
};
m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
const_cast<GLModel*>(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
@ -143,13 +143,6 @@ void Bed3D::Axes::render() const
glsafe(::glDisable(GL_DEPTH_TEST));
}
Bed3D::Bed3D()
: m_type(Custom)
, m_vbo_id(0)
, m_scale_factor(1.0f)
{
}
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
auto check_texture = [](const std::string& texture) {
@ -193,7 +186,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
ExPolygon poly;
for (const Vec2d& p : m_shape) {
poly.contour.append(Point(scale_(p(0)), scale_(p(1))));
poly.contour.append({ scale_(p(0)), scale_(p(1)) });
}
calc_triangles(poly);
@ -228,7 +221,8 @@ Point Bed3D::point_projection(const Point& point) const
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture) const
{
m_scale_factor = scale_factor;
float* factor = const_cast<float*>(&m_scale_factor);
*factor = scale_factor;
if (show_axes)
render_axes();
@ -247,22 +241,24 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
void Bed3D::calc_bounding_boxes() const
{
m_bounding_box = BoundingBoxf3();
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
*bounding_box = BoundingBoxf3();
for (const Vec2d& p : m_shape) {
m_bounding_box.merge(Vec3d(p(0), p(1), 0.0));
bounding_box->merge({ p(0), p(1), 0.0 });
}
m_extended_bounding_box = m_bounding_box;
BoundingBoxf3* extended_bounding_box = const_cast<BoundingBoxf3*>(&m_extended_bounding_box);
*extended_bounding_box = m_bounding_box;
// extend to contain axes
m_extended_bounding_box.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
m_extended_bounding_box.merge(m_extended_bounding_box.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, m_extended_bounding_box.max(2)));
extended_bounding_box->merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
extended_bounding_box->merge(extended_bounding_box->min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, extended_bounding_box->max(2)));
// extend to contain model, if any
BoundingBoxf3 model_bb = m_model.get_bounding_box();
if (model_bb.defined) {
model_bb.translate(m_model_offset);
m_extended_bounding_box.merge(model_bb);
extended_bounding_box->merge(model_bb);
}
}
@ -339,21 +335,24 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) co
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
{
GLTexture* texture = const_cast<GLTexture*>(&m_texture);
GLTexture* temp_texture = const_cast<GLTexture*>(&m_temp_texture);
if (m_texture_filename.empty()) {
m_texture.reset();
texture->reset();
render_default(bottom);
return;
}
if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename)) {
m_texture.reset();
if (texture->get_id() == 0 || texture->get_source() != m_texture_filename) {
texture->reset();
if (boost::algorithm::iends_with(m_texture_filename, ".svg")) {
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) {
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
render_default(bottom);
return;
}
@ -361,15 +360,15 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
if (!texture->load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
render_default(bottom);
return;
}
}
else if (boost::algorithm::iends_with(m_texture_filename, ".png")) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) {
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) {
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
if (!temp_texture->load_from_file(m_texture_filename, false, GLTexture::None, false)) {
render_default(bottom);
return;
}
@ -377,7 +376,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
if (!texture->load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
render_default(bottom);
return;
}
@ -387,13 +386,13 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
return;
}
}
else if (m_texture.unsent_compressed_data_available()) {
else if (texture->unsent_compressed_data_available()) {
// sends to gpu the already available compressed levels of the main texture
m_texture.send_compressed_data_to_gpu();
texture->send_compressed_data_to_gpu();
// the temporary texture is not needed anymore, reset it
if (m_temp_texture.get_id() != 0)
m_temp_texture.reset();
if (temp_texture->get_id() != 0)
temp_texture->reset();
canvas.request_extra_frame();
@ -406,9 +405,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
shader->set_uniform("transparent_background", bottom);
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
if (m_vbo_id == 0) {
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
unsigned int* vbo_id = const_cast<unsigned int*>(&m_vbo_id);
if (*vbo_id == 0) {
glsafe(::glGenBuffers(1, vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
@ -428,12 +429,12 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
GLint tex_coords_id = shader->get_attrib_location("v_tex_coords");
// show the temporary texture while no compressed data is available
GLuint tex_id = (GLuint)m_temp_texture.get_id();
GLuint tex_id = (GLuint)temp_texture->get_id();
if (tex_id == 0)
tex_id = (GLuint)m_texture.get_id();
tex_id = (GLuint)texture->get_id();
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id));
if (position_id != -1) {
glsafe(::glEnableVertexAttribArray(position_id));
@ -471,24 +472,26 @@ void Bed3D::render_model() const
if (m_model_filename.empty())
return;
if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename)) {
GLModel* model = const_cast<GLModel*>(&m_model);
if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) {
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
shift(2) = -0.03;
m_model_offset = shift;
*const_cast<Vec3d*>(&m_model_offset) = shift;
// update extended bounding box
calc_bounding_boxes();
}
if (!m_model.get_filename().empty()) {
if (!model->get_filename().empty()) {
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("uniform_color", m_model_color);
::glPushMatrix();
::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2));
m_model.render();
model->render();
::glPopMatrix();
shader->stop_using();
}
@ -511,7 +514,7 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) co
void Bed3D::render_default(bool bottom) const
{
m_texture.reset();
const_cast<GLTexture*>(&m_texture)->reset();
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0) {

View file

@ -48,7 +48,7 @@ class Bed3D
private:
Vec3d m_origin{ Vec3d::Zero() };
float m_stem_length{ DefaultStemLength };
mutable GLModel m_arrow;
GLModel m_arrow;
public:
const Vec3d& get_origin() const { return m_origin; }
@ -67,28 +67,28 @@ public:
};
private:
EType m_type;
EType m_type{ Custom };
Pointfs m_shape;
std::string m_texture_filename;
std::string m_model_filename;
mutable BoundingBoxf3 m_bounding_box;
mutable BoundingBoxf3 m_extended_bounding_box;
BoundingBoxf3 m_bounding_box;
BoundingBoxf3 m_extended_bounding_box;
Polygon m_polygon;
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
mutable GLTexture m_texture;
mutable GLModel m_model;
mutable Vec3d m_model_offset{ Vec3d::Zero() };
std::array<float, 4> m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f };
GLTexture m_texture;
// temporary texture shown until the main texture has still no levels compressed
mutable GLTexture m_temp_texture;
mutable unsigned int m_vbo_id;
GLTexture m_temp_texture;
GLModel m_model;
Vec3d m_model_offset{ Vec3d::Zero() };
std::array<float, 4> m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f };
unsigned int m_vbo_id{ 0 };
Axes m_axes;
mutable float m_scale_factor;
float m_scale_factor{ 1.0f };
public:
Bed3D();
Bed3D() = default;
~Bed3D() { reset(); }
EType get_type() const { return m_type; }

View file

@ -421,20 +421,24 @@ const BoundingBoxf3& GLVolume::transformed_bounding_box() const
const BoundingBoxf3& box = bounding_box();
assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
if (m_transformed_bounding_box_dirty)
{
m_transformed_bounding_box = box.transformed(world_matrix());
m_transformed_bounding_box_dirty = false;
BoundingBoxf3* transformed_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_bounding_box);
bool* transformed_bounding_box_dirty = const_cast<bool*>(&m_transformed_bounding_box_dirty);
if (*transformed_bounding_box_dirty) {
*transformed_bounding_box = box.transformed(world_matrix());
*transformed_bounding_box_dirty = false;
}
return m_transformed_bounding_box;
return *transformed_bounding_box;
}
const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
{
if (m_transformed_convex_hull_bounding_box_dirty)
m_transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
return m_transformed_convex_hull_bounding_box;
BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_convex_hull_bounding_box);
bool* transformed_convex_hull_bounding_box_dirty = const_cast<bool*>(&m_transformed_convex_hull_bounding_box_dirty);
if (*transformed_convex_hull_bounding_box_dirty) {
*transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
*transformed_convex_hull_bounding_box_dirty = false;
}
return *transformed_convex_hull_bounding_box;
}
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
@ -795,7 +799,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisable(GL_BLEND));
}
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state)
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const
{
if (config == nullptr)
return false;
@ -805,7 +809,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
return false;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0 }, { unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height") });
// Allow the objects to protrude below the print bed
print_volume.min(2) = -1e10;
print_volume.min(0) -= BedEpsilon;
@ -817,9 +821,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
bool contained_min_one = false;
for (GLVolume* volume : this->volumes)
{
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
for (GLVolume* volume : this->volumes) {
if (volume == nullptr || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || (volume->composite_id.volume_id < 0 && !volume->shader_outside_printer_detection_enabled))
continue;
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
@ -832,10 +835,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
if (contained)
contained_min_one = true;
if ((state == ModelInstancePVS_Inside) && volume->is_outside)
if (state == ModelInstancePVS_Inside && volume->is_outside)
state = ModelInstancePVS_Fully_Outside;
if ((state == ModelInstancePVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb))
state = ModelInstancePVS_Partly_Outside;
}
@ -845,7 +848,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
return contained_min_one;
}
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut)
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) const
{
if (config == nullptr)
return false;
@ -867,9 +870,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, b
partlyOut = false;
fullyOut = false;
for (GLVolume* volume : this->volumes)
{
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
for (GLVolume* volume : this->volumes) {
if (volume == nullptr || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || (volume->composite_id.volume_id < 0 && !volume->shader_outside_printer_detection_enabled))
continue;
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();

View file

@ -267,15 +267,15 @@ private:
// Shift in z required by sla supports+pad
double m_sla_shift_z;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_bounding_box;
BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the transformed bounding box.
mutable bool m_transformed_bounding_box_dirty;
bool m_transformed_bounding_box_dirty;
// Convex hull of the volume, if any.
std::shared_ptr<const TriangleMesh> m_convex_hull;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
BoundingBoxf3 m_transformed_convex_hull_bounding_box;
// Whether or not is needed to recalculate the transformed convex hull bounding box.
mutable bool m_transformed_convex_hull_bounding_box_dirty;
bool m_transformed_convex_hull_bounding_box_dirty;
public:
// Color of the triangles / quads held by this volume.
@ -568,8 +568,8 @@ public:
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state);
bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut);
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const;
bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) const;
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);

View file

@ -21,12 +21,6 @@ double Camera::FrustrumMinNearZ = 100.0;
double Camera::FrustrumZMargin = 10.0;
double Camera::MaxFovDeg = 60.0;
Camera::Camera()
: requires_zoom_to_bed(false)
{
set_default_orientation();
}
std::string Camera::get_type_as_string() const
{
switch (m_type)
@ -49,11 +43,6 @@ void Camera::set_type(EType type)
}
}
void Camera::set_type(const std::string& type)
{
set_type((type == "1") ? Perspective : Ortho);
}
void Camera::select_next_type()
{
unsigned char next = (unsigned char)m_type + 1;
@ -65,24 +54,18 @@ void Camera::select_next_type()
void Camera::set_target(const Vec3d& target)
{
Vec3d new_target = validate_target(target);
Vec3d new_displacement = new_target - m_target;
if (!new_displacement.isApprox(Vec3d::Zero()))
{
const Vec3d new_target = validate_target(target);
const Vec3d new_displacement = new_target - m_target;
if (!new_displacement.isApprox(Vec3d::Zero())) {
m_target = new_target;
m_view_matrix.translate(-new_displacement);
}
}
void Camera::update_zoom(double delta_zoom)
{
set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1));
}
void Camera::set_zoom(double zoom)
{
// Don't allow to zoom too far outside the scene.
double zoom_min = min_zoom();
const double zoom_min = min_zoom();
if (zoom_min > 0.0)
zoom = std::max(zoom, zoom_min);
@ -123,7 +106,7 @@ double Camera::get_fov() const
void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
{
glsafe(::glViewport(0, 0, w, h));
glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data()));
glsafe(::glGetIntegerv(GL_VIEWPORT, const_cast<std::array<int, 4>*>(&m_viewport)->data()));
}
void Camera::apply_view_matrix() const
@ -138,22 +121,23 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
double w = 0.0;
double h = 0.0;
double old_distance = m_distance;
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
const double old_distance = m_distance;
std::pair<double, double>* frustrum_zs = const_cast<std::pair<double, double>*>(&m_frustrum_zs);
*frustrum_zs = calc_tight_frustrum_zs_around(box);
if (m_distance != old_distance)
// the camera has been moved re-apply view matrix
apply_view_matrix();
if (near_z > 0.0)
m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ);
frustrum_zs->first = std::max(std::min(frustrum_zs->first, near_z), FrustrumMinNearZ);
if (far_z > 0.0)
m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z);
frustrum_zs->second = std::max(frustrum_zs->second, far_z);
w = 0.5 * (double)m_viewport[2];
h = 0.5 * (double)m_viewport[3];
double inv_zoom = get_inv_zoom();
const double inv_zoom = get_inv_zoom();
w *= inv_zoom;
h *= inv_zoom;
@ -162,16 +146,16 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
default:
case Ortho:
{
m_gui_scale = 1.0;
*const_cast<double*>(&m_gui_scale) = 1.0;
break;
}
case Perspective:
{
// scale near plane to keep w and h constant on the plane at z = m_distance
double scale = m_frustrum_zs.first / m_distance;
const double scale = frustrum_zs->first / m_distance;
w *= scale;
h *= scale;
m_gui_scale = scale;
*const_cast<double*>(&m_gui_scale) = scale;
break;
}
}
@ -184,26 +168,25 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
default:
case Ortho:
{
glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
glsafe(::glOrtho(-w, w, -h, h, frustrum_zs->first, frustrum_zs->second));
break;
}
case Perspective:
{
glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
glsafe(::glFrustum(-w, w, -h, h, frustrum_zs->first, frustrum_zs->second));
break;
}
}
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, const_cast<Transform3d*>(&m_projection_matrix)->data()));
glsafe(::glMatrixMode(GL_MODELVIEW));
}
void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor)
{
// Calculate the zoom factor needed to adjust the view around the given box.
double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor);
if (zoom > 0.0)
{
const double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor);
if (zoom > 0.0) {
m_zoom = zoom;
// center view around box center
set_target(box.center());
@ -213,9 +196,8 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor)
void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor)
{
Vec3d center;
double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor);
if (zoom > 0.0)
{
const double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor);
if (zoom > 0.0) {
m_zoom = zoom;
// center view around the calculated center
set_target(center);
@ -289,8 +271,8 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b
}
}
Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ());
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
const auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ());
m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right());
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
@ -299,10 +281,10 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b
// Virtual trackball, rotate around an axis, where the eucledian norm of the axis gives the rotation angle in radians.
void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
{
double angle = rotation_rad.norm();
const double angle = rotation_rad.norm();
if (std::abs(angle) > EPSILON) {
Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized();
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
const Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized();
m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis));
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
@ -310,18 +292,13 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
}
}
double Camera::min_zoom() const
{
return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box);
}
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
{
std::pair<double, double> ret;
auto& [near_z, far_z] = ret;
// box in eye space
BoundingBoxf3 eye_box = box.transformed(m_view_matrix);
const BoundingBoxf3 eye_box = box.transformed(m_view_matrix);
near_z = -eye_box.max(2);
far_z = -eye_box.min(2);
@ -330,73 +307,71 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
far_z += FrustrumZMargin;
// ensure min size
if (far_z - near_z < FrustrumMinZRange)
{
double mid_z = 0.5 * (near_z + far_z);
double half_size = 0.5 * FrustrumMinZRange;
if (far_z - near_z < FrustrumMinZRange) {
const double mid_z = 0.5 * (near_z + far_z);
const double half_size = 0.5 * FrustrumMinZRange;
near_z = mid_z - half_size;
far_z = mid_z + half_size;
}
if (near_z < FrustrumMinNearZ)
{
float delta = FrustrumMinNearZ - near_z;
if (near_z < FrustrumMinNearZ) {
const double delta = FrustrumMinNearZ - near_z;
set_distance(m_distance + delta);
near_z += delta;
far_z += delta;
}
else if ((near_z > 2.0 * FrustrumMinNearZ) && (m_distance > DefaultDistance))
{
float delta = m_distance - DefaultDistance;
set_distance(DefaultDistance);
near_z -= delta;
far_z -= delta;
}
// The following is commented out because it causes flickering of the 3D scene GUI
// when the bounding box of the scene gets large enough
// We need to introduce some smarter code to move the camera back and forth in such case
// else if (near_z > 2.0 * FrustrumMinNearZ && m_distance > DefaultDistance) {
// float delta = m_distance - DefaultDistance;
// set_distance(DefaultDistance);
// near_z -= delta;
// far_z -= delta;
// }
return ret;
}
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor) const
{
double max_bb_size = box.max_size();
const double max_bb_size = box.max_size();
if (max_bb_size == 0.0)
return -1.0;
// project the box vertices on a plane perpendicular to the camera forward axis
// then calculates the vertices coordinate on this plane along the camera xy axes
Vec3d right = get_dir_right();
Vec3d up = get_dir_up();
Vec3d forward = get_dir_forward();
Vec3d bb_center = box.center();
const Vec3d right = get_dir_right();
const Vec3d up = get_dir_up();
const Vec3d forward = get_dir_forward();
const Vec3d bb_center = box.center();
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
const std::vector<Vec3d> vertices = {
box.min,
{ box.max(0), box.min(1), box.min(2) },
{ box.max(0), box.max(1), box.min(2) },
{ box.min(0), box.max(1), box.min(2) },
{ box.min(0), box.min(1), box.max(2) },
{ box.max(0), box.min(1), box.max(2) },
box.max,
{ box.min(0), box.max(1), box.max(2) }
};
double min_x = DBL_MAX;
double min_y = DBL_MAX;
double max_x = -DBL_MAX;
double max_y = -DBL_MAX;
for (const Vec3d& v : vertices)
{
for (const Vec3d& v : vertices) {
// project vertex on the plane perpendicular to camera forward axis
Vec3d pos = v - bb_center;
Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
const Vec3d pos = v - bb_center;
const Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
// calculates vertex coordinate along camera xy axes
double x_on_plane = proj_on_plane.dot(right);
double y_on_plane = proj_on_plane.dot(up);
const double x_on_plane = proj_on_plane.dot(right);
const double y_on_plane = proj_on_plane.dot(up);
min_x = std::min(min_x, x_on_plane);
min_y = std::min(min_y, y_on_plane);
@ -406,7 +381,7 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double
double dx = max_x - min_x;
double dy = max_y - min_y;
if ((dx <= 0.0) || (dy <= 0.0))
if (dx <= 0.0 || dy <= 0.0)
return -1.0f;
dx *= margin_factor;
@ -423,13 +398,12 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c
// project the volumes vertices on a plane perpendicular to the camera forward axis
// then calculates the vertices coordinate on this plane along the camera xy axes
Vec3d right = get_dir_right();
Vec3d up = get_dir_up();
Vec3d forward = get_dir_forward();
const Vec3d right = get_dir_right();
const Vec3d up = get_dir_up();
const Vec3d forward = get_dir_forward();
BoundingBoxf3 box;
for (const GLVolume* volume : volumes)
{
for (const GLVolume* volume : volumes) {
box.merge(volume->transformed_bounding_box());
}
center = box.center();
@ -439,24 +413,22 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c
double max_x = -DBL_MAX;
double max_y = -DBL_MAX;
for (const GLVolume* volume : volumes)
{
for (const GLVolume* volume : volumes) {
const Transform3d& transform = volume->world_matrix();
const TriangleMesh* hull = volume->convex_hull();
if (hull == nullptr)
continue;
for (const Vec3f& vertex : hull->its.vertices)
{
Vec3d v = transform * vertex.cast<double>();
for (const Vec3f& vertex : hull->its.vertices) {
const Vec3d v = transform * vertex.cast<double>();
// project vertex on the plane perpendicular to camera forward axis
Vec3d pos = v - center;
Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
const Vec3d pos = v - center;
const Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
// calculates vertex coordinate along camera xy axes
double x_on_plane = proj_on_plane.dot(right);
double y_on_plane = proj_on_plane.dot(up);
const double x_on_plane = proj_on_plane.dot(right);
const double y_on_plane = proj_on_plane.dot(up);
min_x = std::min(min_x, x_on_plane);
min_y = std::min(min_y, y_on_plane);
@ -467,10 +439,10 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c
center += 0.5 * (max_x + min_x) * right + 0.5 * (max_y + min_y) * up;
double dx = margin_factor * (max_x - min_x);
double dy = margin_factor * (max_y - min_y);
const double dx = margin_factor * (max_x - min_x);
const double dy = margin_factor * (max_y - min_y);
if ((dx <= 0.0) || (dy <= 0.0))
if (dx <= 0.0 || dy <= 0.0)
return -1.0f;
return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy);
@ -478,22 +450,21 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c
void Camera::set_distance(double distance) const
{
if (m_distance != distance)
{
m_view_matrix.translate((distance - m_distance) * get_dir_forward());
m_distance = distance;
if (m_distance != distance) {
const_cast<Transform3d*>(&m_view_matrix)->translate((distance - m_distance) * get_dir_forward());
*const_cast<double*>(&m_distance) = distance;
}
}
void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up)
{
Vec3d unit_z = (position - target).normalized();
Vec3d unit_x = up.cross(unit_z).normalized();
Vec3d unit_y = unit_z.cross(unit_x).normalized();
const Vec3d unit_z = (position - target).normalized();
const Vec3d unit_x = up.cross(unit_z).normalized();
const Vec3d unit_y = unit_z.cross(unit_x).normalized();
m_target = target;
m_distance = (position - target).norm();
Vec3d new_position = m_target + m_distance * unit_z;
const Vec3d new_position = m_target + m_distance * unit_z;
m_view_matrix(0, 0) = unit_x(0);
m_view_matrix(0, 1) = unit_x(1);
@ -525,10 +496,10 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up
void Camera::set_default_orientation()
{
m_zenit = 45.0f;
double theta_rad = Geometry::deg2rad(-(double)m_zenit);
double phi_rad = Geometry::deg2rad(45.0);
double sin_theta = ::sin(theta_rad);
Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
const double theta_rad = Geometry::deg2rad(-(double)m_zenit);
const double phi_rad = Geometry::deg2rad(45.0);
const double sin_theta = ::sin(theta_rad);
const Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ());
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- camera_pos), m_view_rotation, Vec3d(1., 1., 1.));
@ -543,9 +514,9 @@ Vec3d Camera::validate_target(const Vec3d& target) const
test_box.scale(ScaleFactor);
test_box.translate(m_scene_box.center());
return Vec3d(std::clamp(target(0), test_box.min(0), test_box.max(0)),
std::clamp(target(1), test_box.min(1), test_box.max(1)),
std::clamp(target(2), test_box.min(2), test_box.max(2)));
return { std::clamp(target(0), test_box.min(0), test_box.max(0)),
std::clamp(target(1), test_box.min(1), test_box.max(1)),
std::clamp(target(2), test_box.min(2), test_box.max(2)) };
}
void Camera::update_zenit()

View file

@ -26,7 +26,7 @@ struct Camera
Num_types
};
bool requires_zoom_to_bed;
bool requires_zoom_to_bed{ false };
private:
EType m_type{ Perspective };
@ -35,26 +35,26 @@ private:
float m_zenit{ 45.0f };
double m_zoom{ 1.0 };
// Distance between camera position and camera target measured along the camera Z axis
mutable double m_distance{ DefaultDistance };
mutable double m_gui_scale{ 1.0 };
double m_distance{ DefaultDistance };
double m_gui_scale{ 1.0 };
mutable std::array<int, 4> m_viewport;
mutable Transform3d m_view_matrix{ Transform3d::Identity() };
std::array<int, 4> m_viewport;
Transform3d m_view_matrix{ Transform3d::Identity() };
// We are calculating the rotation part of the m_view_matrix from m_view_rotation.
mutable Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 };
mutable Transform3d m_projection_matrix{ Transform3d::Identity() };
mutable std::pair<double, double> m_frustrum_zs;
Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 };
Transform3d m_projection_matrix{ Transform3d::Identity() };
std::pair<double, double> m_frustrum_zs;
BoundingBoxf3 m_scene_box;
public:
Camera();
Camera() { set_default_orientation(); }
EType get_type() const { return m_type; }
std::string get_type_as_string() const;
void set_type(EType type);
// valid values for type: "0" -> ortho, "1" -> perspective
void set_type(const std::string& type);
void set_type(const std::string& type) { set_type((type == "1") ? Perspective : Ortho); }
void select_next_type();
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
@ -67,7 +67,7 @@ public:
double get_zoom() const { return m_zoom; }
double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; }
void update_zoom(double delta_zoom);
void update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); }
void set_zoom(double zoom);
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
@ -119,8 +119,7 @@ public:
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
// forces camera right vector to be parallel to XY plane
void recover_from_free_camera()
{
void recover_from_free_camera() {
if (std::abs(get_dir_right()(2)) > EPSILON)
look_at(get_position(), m_target, Vec3d::UnitZ());
}
@ -128,7 +127,7 @@ public:
void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up);
double max_zoom() const { return 250.0; }
double min_zoom() const;
double min_zoom() const { return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box); }
private:
// returns tight values for nearZ and farZ plane around the given bounding box

View file

@ -278,6 +278,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto");
bool have_support_interface = config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0;
auto support_material_style = config->opt_enum<SupportMaterialStyle>("support_material_style");
for (auto el : { "support_material_style", "support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle",
"support_material_interface_pattern", "support_material_interface_layers",
@ -286,6 +287,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field(el, have_support_material);
toggle_field("support_material_threshold", have_support_material_auto);
toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble);
toggle_field("support_material_closing_radius", have_support_material && support_material_style == smsSnug);
for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" })

View file

@ -2165,7 +2165,7 @@ static std::string get_custom_code(const std::string& code_in, double height)
if (dlg.ShowModal() != wxID_OK)
return "";
value = dlg.GetValue().ToStdString();
value = into_u8(dlg.GetValue());
valid = GUI::Tab::validate_custom_gcode("Custom G-code", value);
} while (!valid);
return value;
@ -2173,7 +2173,7 @@ static std::string get_custom_code(const std::string& code_in, double height)
if (dlg.ShowModal() != wxID_OK)
return "";
return dlg.GetValue().ToStdString();
return into_u8(dlg.GetValue());
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
}

View file

@ -508,6 +508,7 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
m_layer_height_profile.clear();
m_layers_texture.valid = false;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().obj_list()->update_info_items(last_object_id);
}
void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor)
@ -517,6 +518,7 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
m_layers_texture.valid = false;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().obj_list()->update_info_items(last_object_id);
}
void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params)
@ -526,6 +528,7 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas,
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
m_layers_texture.valid = false;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().obj_list()->update_info_items(last_object_id);
}
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
@ -565,6 +568,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit")));
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().obj_list()->update_info_items(last_object_id);
}
}
m_layer_height_profile_modified = false;
@ -3524,7 +3528,7 @@ Vec2d GLCanvas3D::get_local_mouse_position() const
void GLCanvas3D::set_tooltip(const std::string& tooltip) const
{
if (m_canvas != nullptr)
m_tooltip.set_text(tooltip);
const_cast<Tooltip*>(&m_tooltip)->set_text(tooltip);
}
void GLCanvas3D::do_move(const std::string& snapshot_type)
@ -3541,22 +3545,19 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
Selection::EMode selection_mode = m_selection.get_mode();
for (const GLVolume* v : m_volumes.volumes)
{
for (const GLVolume* v : m_volumes.volumes) {
int object_idx = v->object_idx();
int instance_idx = v->instance_idx();
int volume_idx = v->volume_idx();
std::pair<int, int> done_id(object_idx, instance_idx);
if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size()))
{
if (0 <= object_idx && object_idx < (int)m_model->objects.size()) {
done.insert(done_id);
// Move instances/volumes
ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr)
{
if (model_object != nullptr) {
if (selection_mode == Selection::Instance)
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
else if (selection_mode == Selection::Volume)
@ -3572,8 +3573,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
}
// Fixes sinking/flying instances
for (const std::pair<int, int>& i : done)
{
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
m_selection.translate(i.first, i.second, shift);
@ -3932,13 +3932,13 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
em *= m_retina_helper->get_scale_factor();
#endif
if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, m_mouse_wheel))
m_imgui_undo_redo_hovered_pos = hovered;
int* mouse_wheel = const_cast<int*>(&m_mouse_wheel);
if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, *mouse_wheel))
*const_cast<int*>(&m_imgui_undo_redo_hovered_pos) = hovered;
else
m_imgui_undo_redo_hovered_pos = -1;
*const_cast<int*>(&m_imgui_undo_redo_hovered_pos) = -1;
if (selected >= 0)
{
if (selected >= 0) {
is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected);
action_taken = true;
}
@ -3979,9 +3979,10 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
char *s = new char[255];
strcpy(s, search_line.empty() ? _u8L("Enter a search term").c_str() : search_line.c_str());
imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s,
sidebar.get_searcher().view_params,
selected, edited, m_mouse_wheel, wxGetApp().is_localized());
int* mouse_wheel = const_cast<int*>(&m_mouse_wheel);
imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s,
sidebar.get_searcher().view_params,
selected, edited, *mouse_wheel, wxGetApp().is_localized());
search_line = s;
delete [] s;
@ -4840,8 +4841,10 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
void GLCanvas3D::_picking_pass() const
{
std::vector<int>* hover_volume_idxs = const_cast<std::vector<int>*>(&m_hover_volume_idxs);
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
m_hover_volume_idxs.clear();
hover_volume_idxs->clear();
// Render the object for picking.
// FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
@ -4856,9 +4859,10 @@ void GLCanvas3D::_picking_pass() const
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
if (m_camera_clipping_plane.is_active()) {
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
ClippingPlane* camera_clipping_plane = const_cast<ClippingPlane*>(&m_camera_clipping_plane);
*camera_clipping_plane = m_gizmos.get_clipping_plane();
if (camera_clipping_plane->is_active()) {
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)camera_clipping_plane->get_data());
::glEnable(GL_CLIP_PLANE0);
}
_render_volumes_for_picking();
@ -4884,11 +4888,11 @@ void GLCanvas3D::_picking_pass() const
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) {
// do not add the volume id if any gizmo is active and CTRL is pressed
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL))
m_hover_volume_idxs.emplace_back(volume_id);
m_gizmos.set_hover_id(-1);
hover_volume_idxs->emplace_back(volume_id);
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(-1);
}
else
m_gizmos.set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1);
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1);
_update_volumes_hover_state();
}
@ -4896,12 +4900,11 @@ void GLCanvas3D::_picking_pass() const
void GLCanvas3D::_rectangular_selection_picking_pass() const
{
m_gizmos.set_hover_id(-1);
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(-1);
std::set<int> idxs;
if (m_picking_enabled)
{
if (m_picking_enabled) {
if (m_multisample_allowed)
// This flag is often ignored by NVIDIA drivers if rendering into a screen buffer.
glsafe(::glDisable(GL_MULTISAMPLE));
@ -4922,8 +4925,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
int left = (int)m_rectangle_selection.get_left();
int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top();
if ((left >= 0) && (top >= 0))
{
if (left >= 0 && top >= 0) {
#define USE_PARALLEL 1
#if USE_PARALLEL
struct Pixel
@ -4943,7 +4945,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
for (size_t i = range.begin(); i < range.end(); ++i)
if (frame[i].valid()) {
int volume_id = frame[i].id();
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) {
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) {
mutex.lock();
idxs.insert(volume_id);
mutex.unlock();
@ -4958,14 +4960,14 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
{
int px_id = 4 * i;
int volume_id = frame[px_id] + (frame[px_id + 1] << 8) + (frame[px_id + 2] << 16);
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size())
idxs.insert(volume_id);
}
#endif // USE_PARALLEL
}
}
m_hover_volume_idxs.assign(idxs.begin(), idxs.end());
const_cast<std::vector<int>*>(&m_hover_volume_idxs)->assign(idxs.begin(), idxs.end());
_update_volumes_hover_state();
}
@ -5059,7 +5061,9 @@ void GLCanvas3D::_render_objects() const
glsafe(::glEnable(GL_DEPTH_TEST));
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
ClippingPlane* camera_clipping_plane = const_cast<ClippingPlane*>(&m_camera_clipping_plane);
GLVolumeCollection* volumes = const_cast<GLVolumeCollection*>(&m_volumes);
*camera_clipping_plane = m_gizmos.get_clipping_plane();
if (m_picking_enabled) {
// Update the layer editing selection to the first object selected, update the current object maximum Z.
@ -5067,17 +5071,17 @@ void GLCanvas3D::_render_objects() const
if (m_config != nullptr) {
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
m_volumes.check_outside_state(m_config, nullptr);
volumes->set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
volumes->check_outside_state(m_config, nullptr);
}
}
if (m_use_clipping_planes)
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
volumes->set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
else
m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
volumes->set_z_range(-FLT_MAX, FLT_MAX);
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
volumes->set_clipping_plane(camera_clipping_plane->get_data());
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
if (shader != nullptr) {
@ -5085,16 +5089,16 @@ void GLCanvas3D::_render_objects() const
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
volumes->render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
m_layers_editing.render_volumes(*this, this->m_volumes);
m_layers_editing.render_volumes(*this, *volumes);
} else {
// do not cull backfaces to show broken geometry, if any
m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
// do not cull backfaces to show broken geometry, if any
volumes->render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
}
@ -5112,11 +5116,11 @@ void GLCanvas3D::_render_objects() const
}
}
m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
volumes->render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
shader->stop_using();
}
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
*camera_clipping_plane = ClippingPlane::ClipsNothing();
}
void GLCanvas3D::_render_gcode() const
@ -5157,13 +5161,13 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() const
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
#if ENABLE_RETINA_GL
const float sc = m_retina_helper->get_scale_factor() * scale;
m_main_toolbar.set_scale(sc);
m_undoredo_toolbar.set_scale(sc);
const_cast<GLToolbar*>(&m_main_toolbar)->set_scale(sc);
const_cast<GLToolbar*>(&m_undoredo_toolbar)->set_scale(sc);
collapse_toolbar.set_scale(sc);
size *= m_retina_helper->get_scale_factor();
#else
m_main_toolbar.set_icons_size(size);
m_undoredo_toolbar.set_icons_size(size);
const_cast<GLToolbar*>(&m_main_toolbar)->set_icons_size(size);
const_cast<GLToolbar*>(&m_undoredo_toolbar)->set_icons_size(size);
collapse_toolbar.set_icons_size(size);
#endif // ENABLE_RETINA_GL
@ -5211,13 +5215,13 @@ void GLCanvas3D::_render_overlays() const
// to correctly place them
#if ENABLE_RETINA_GL
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/);
m_main_toolbar.set_scale(scale);
m_undoredo_toolbar.set_scale(scale);
const_cast<GLToolbar*>(&m_main_toolbar)->set_scale(scale);
const_cast<GLToolbar*>(&m_undoredo_toolbar)->set_scale(scale);
wxGetApp().plater()->get_collapse_toolbar().set_scale(scale);
#else
const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/));
m_main_toolbar.set_icons_size(size);
m_undoredo_toolbar.set_icons_size(size);
const_cast<GLToolbar*>(&m_main_toolbar)->set_icons_size(size);
const_cast<GLToolbar*>(&m_undoredo_toolbar)->set_icons_size(size);
wxGetApp().plater()->get_collapse_toolbar().set_icons_size(size);
#endif // ENABLE_RETINA_GL
@ -5292,12 +5296,12 @@ void GLCanvas3D::_render_gizmos_overlay() const
#if ENABLE_RETINA_GL
// m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale();
m_gizmos.set_overlay_scale(scale); //! #ys_FIXME_experiment
const_cast<GLGizmosManager*>(&m_gizmos)->set_overlay_scale(scale); //! #ys_FIXME_experiment
#else
// m_gizmos.set_overlay_scale(m_canvas->GetContentScaleFactor());
// m_gizmos.set_overlay_scale(wxGetApp().em_unit()*0.1f);
const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale());
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale());
const_cast<GLGizmosManager*>(&m_gizmos)->set_overlay_icon_size(size); //! #ys_FIXME_experiment
#endif /* __WXMSW__ */
m_gizmos.render_overlay();
@ -5316,8 +5320,9 @@ void GLCanvas3D::_render_main_toolbar() const
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width) * inv_zoom;
m_main_toolbar.set_position(top, left);
m_main_toolbar.render(*this);
GLToolbar* main_toolbar = const_cast<GLToolbar*>(&m_main_toolbar);
main_toolbar->set_position(top, left);
main_toolbar->render(*this);
}
void GLCanvas3D::_render_undoredo_toolbar() const
@ -5332,8 +5337,10 @@ void GLCanvas3D::_render_undoredo_toolbar() const
const GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width)) * inv_zoom;
m_undoredo_toolbar.set_position(top, left);
m_undoredo_toolbar.render(*this);
GLToolbar* undoredo_toolbar = const_cast<GLToolbar*>(&m_undoredo_toolbar);
undoredo_toolbar->set_position(top, left);
undoredo_toolbar->render(*this);
}
void GLCanvas3D::_render_collapse_toolbar() const
@ -5424,20 +5431,21 @@ void GLCanvas3D::_render_sla_slices() const
if (!obj->is_step_done(slaposSliceSupports))
continue;
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i);
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i);
SlaCap* sla_caps = const_cast<SlaCap*>(m_sla_caps);
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = sla_caps[0].triangles.find(i);
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = sla_caps[1].triangles.find(i);
{
if (it_caps_bottom == m_sla_caps[0].triangles.end())
it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
if (! m_sla_caps[0].matches(clip_min_z)) {
m_sla_caps[0].z = clip_min_z;
if (it_caps_bottom == sla_caps[0].triangles.end())
it_caps_bottom = sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
if (!sla_caps[0].matches(clip_min_z)) {
sla_caps[0].z = clip_min_z;
it_caps_bottom->second.object.clear();
it_caps_bottom->second.supports.clear();
}
if (it_caps_top == m_sla_caps[1].triangles.end())
it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
if (! m_sla_caps[1].matches(clip_max_z)) {
m_sla_caps[1].z = clip_max_z;
if (it_caps_top == sla_caps[1].triangles.end())
it_caps_top = sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
if (!sla_caps[1].matches(clip_max_z)) {
sla_caps[1].z = clip_max_z;
it_caps_top->second.object.clear();
it_caps_top->second.supports.clear();
}
@ -5543,7 +5551,7 @@ void GLCanvas3D::_update_volumes_hover_state() const
if (alt_pressed && (shift_pressed || ctrl_pressed)) {
// illegal combinations of keys
m_hover_volume_idxs.clear();
const_cast<std::vector<int>*>(&m_hover_volume_idxs)->clear();
return;
}
@ -5567,7 +5575,7 @@ void GLCanvas3D::_update_volumes_hover_state() const
if (hover_modifiers_only && !hover_from_single_instance) {
// do not allow to select volumes from different instances
m_hover_volume_idxs.clear();
const_cast<std::vector<int>*>(&m_hover_volume_idxs)->clear();
return;
}
@ -5588,23 +5596,15 @@ void GLCanvas3D::_update_volumes_hover_state() const
(deselect && !m_selection.is_single_full_instance() && (volume.object_idx() == m_selection.get_object_idx()) && (volume.instance_idx() == m_selection.get_instance_idx()))
);
if (as_volume) {
if (deselect)
volume.hover = GLVolume::HS_Deselect;
else
volume.hover = GLVolume::HS_Select;
}
if (as_volume)
volume.hover = deselect ? GLVolume::HS_Deselect : GLVolume::HS_Select;
else {
int object_idx = volume.object_idx();
int instance_idx = volume.instance_idx();
for (GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx) {
if (deselect)
v->hover = GLVolume::HS_Deselect;
else
v->hover = GLVolume::HS_Select;
}
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx)
v->hover = deselect ? GLVolume::HS_Deselect : GLVolume::HS_Select;
}
}
}
@ -6311,31 +6311,47 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
#if ENABLE_WARNING_TEXTURE_REMOVAL
void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
{
enum ErrorType{
PLATER_WARNING,
PLATER_ERROR,
SLICING_ERROR
};
std::string text;
bool error = false;
ErrorType error = ErrorType::PLATER_WARNING;
switch (warning) {
case EWarning::ObjectOutside: text = _u8L("An object outside the print area was detected."); break;
case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = true; break;
case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = true; break;
case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break;
case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break;
case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible."); break;
case EWarning::ObjectClashed:
text = _u8L("An object outside the print area was detected.\n"
"Resolve the current problem to continue slicing.");
error = true;
error = ErrorType::PLATER_ERROR;
break;
}
auto& notification_manager = *wxGetApp().plater()->get_notification_manager();
if (state) {
if (error)
notification_manager.push_plater_error_notification(text);
else
switch (error)
{
case PLATER_WARNING:
if (state)
notification_manager.push_plater_warning_notification(text);
}
else {
if (error)
notification_manager.close_plater_error_notification(text);
else
notification_manager.close_plater_warning_notification(text);
break;
case PLATER_ERROR:
if (state)
notification_manager.push_plater_error_notification(text);
else
notification_manager.close_plater_error_notification(text);
break;
case SLICING_ERROR:
if (state)
notification_manager.push_slicing_error_notification(text);
else
notification_manager.close_slicing_error_notification(text);
break;
default:
break;
}
}
#else

View file

@ -449,13 +449,13 @@ private:
wxTimer m_timer;
LayersEditing m_layers_editing;
Mouse m_mouse;
mutable GLGizmosManager m_gizmos;
mutable GLToolbar m_main_toolbar;
mutable GLToolbar m_undoredo_toolbar;
GLGizmosManager m_gizmos;
GLToolbar m_main_toolbar;
GLToolbar m_undoredo_toolbar;
ClippingPlane m_clipping_planes[2];
mutable ClippingPlane m_camera_clipping_plane;
ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
SlaCap m_sla_caps[2];
std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
@ -463,7 +463,7 @@ private:
int m_extra_frame_requested_delayed { std::numeric_limits<int>::max() };
bool m_event_handlers_bound{ false };
mutable GLVolumeCollection m_volumes;
GLVolumeCollection m_volumes;
GCodeViewer m_gcode_viewer;
RenderTimer m_render_timer;
@ -478,7 +478,6 @@ private:
bool m_dirty;
bool m_initialized;
bool m_apply_zoom_to_volumes_filter;
mutable std::vector<int> m_hover_volume_idxs;
bool m_picking_enabled;
bool m_moving_enabled;
bool m_dynamic_background_enabled;
@ -487,6 +486,7 @@ private:
bool m_tab_down;
ECursorType m_cursor_type;
GLSelectionRectangle m_rectangle_selection;
std::vector<int> m_hover_volume_idxs;
// Following variable is obsolete and it should be safe to remove it.
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)
@ -504,13 +504,13 @@ private:
RenderStats m_render_stats;
#endif // ENABLE_RENDER_STATISTICS
mutable int m_imgui_undo_redo_hovered_pos{ -1 };
mutable int m_mouse_wheel {0};
int m_imgui_undo_redo_hovered_pos{ -1 };
int m_mouse_wheel{ 0 };
int m_selected_extruder;
Labels m_labels;
mutable Tooltip m_tooltip;
mutable bool m_tooltip_enabled{ true };
Tooltip m_tooltip;
bool m_tooltip_enabled{ true };
Slope m_slope;
ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla,
@ -519,8 +519,7 @@ private:
PrinterTechnology current_printer_technology() const;
template<class Self>
static auto & get_arrange_settings(Self *self)
{
static auto & get_arrange_settings(Self *self) {
PrinterTechnology ptech = self->current_printer_technology();
auto *ptr = &self->m_arrange_settings_fff;
@ -529,11 +528,10 @@ private:
ptr = &self->m_arrange_settings_sla;
} else if (ptech == ptFFF) {
auto co_opt = self->m_config->template option<ConfigOptionBool>("complete_objects");
if (co_opt && co_opt->value) {
if (co_opt && co_opt->value)
ptr = &self->m_arrange_settings_fff_seq_print;
} else {
else
ptr = &self->m_arrange_settings_fff;
}
}
return *ptr;
@ -715,10 +713,9 @@ public:
double m_rotation = 0.;
BoundingBoxf m_bb;
friend class GLCanvas3D;
public:
inline operator bool() const
{
public:
inline operator bool() const {
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
}
@ -763,8 +760,7 @@ public:
void use_slope(bool use) { m_slope.use(use); }
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
ArrangeSettings get_arrange_settings() const
{
ArrangeSettings get_arrange_settings() const {
const ArrangeSettings &settings = get_arrange_settings(this);
ArrangeSettings ret = settings;
if (&settings == &m_arrange_settings_fff_seq_print) {

View file

@ -428,8 +428,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
bool processed = false;
// mouse anywhere
if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr))
{
if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && m_mouse_capture.parent != nullptr) {
if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) {
// prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it,
// as when switching between views
@ -441,38 +440,31 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
if (evt.Moving())
update_hover_state(mouse_pos, parent);
else if (evt.LeftUp())
{
if (m_mouse_capture.left)
{
else if (evt.LeftUp()) {
if (m_mouse_capture.left) {
processed = true;
m_mouse_capture.left = false;
}
else
return false;
}
else if (evt.MiddleUp())
{
if (m_mouse_capture.middle)
{
else if (evt.MiddleUp()) {
if (m_mouse_capture.middle) {
processed = true;
m_mouse_capture.middle = false;
}
else
return false;
}
else if (evt.RightUp())
{
if (m_mouse_capture.right)
{
else if (evt.RightUp()) {
if (m_mouse_capture.right) {
processed = true;
m_mouse_capture.right = false;
}
else
return false;
}
else if (evt.Dragging())
{
else if (evt.Dragging()) {
if (m_mouse_capture.any())
// if the button down was done on this toolbar, prevent from dragging into the scene
processed = true;
@ -481,35 +473,29 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
}
int item_id = contains_mouse(mouse_pos, parent);
if (item_id != -1)
{
if (item_id != -1) {
// mouse inside toolbar
if (evt.LeftDown() || evt.LeftDClick())
{
if (evt.LeftDown() || evt.LeftDClick()) {
m_mouse_capture.left = true;
m_mouse_capture.parent = &parent;
processed = true;
if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)))
{
if (item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
(m_pressed_toggable_id == -1 || m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)) {
// mouse is inside an icon
do_action(GLToolbarItem::Left, item_id, parent, true);
parent.set_as_dirty();
}
}
else if (evt.MiddleDown())
{
else if (evt.MiddleDown()) {
m_mouse_capture.middle = true;
m_mouse_capture.parent = &parent;
}
else if (evt.RightDown())
{
else if (evt.RightDown()) {
m_mouse_capture.right = true;
m_mouse_capture.parent = &parent;
processed = true;
if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)))
{
if (item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() &&
(m_pressed_toggable_id == -1 || m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)) {
// mouse is inside an icon
do_action(GLToolbarItem::Right, item_id, parent, true);
parent.set_as_dirty();
@ -522,24 +508,26 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
void GLToolbar::calc_layout() const
{
switch (m_layout.type)
Layout* layout = const_cast<Layout*>(&m_layout);
switch (layout->type)
{
default:
case Layout::Horizontal:
{
m_layout.width = get_width_horizontal();
m_layout.height = get_height_horizontal();
layout->width = get_width_horizontal();
layout->height = get_height_horizontal();
break;
}
case Layout::Vertical:
{
m_layout.width = get_width_vertical();
m_layout.height = get_height_vertical();
layout->width = get_width_vertical();
layout->height = get_height_vertical();
break;
}
}
m_layout.dirty = false;
layout->dirty = false;
}
float GLToolbar::get_width_horizontal() const
@ -1196,19 +1184,17 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
if (tex_id == 0 || tex_width <= 0 || tex_height <= 0)
return;
// renders icons
for (const GLToolbarItem* item : m_items)
{
for (const GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
if (item->is_separator())
top -= separator_stride;
else
{
else {
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
top -= icon_stride;
}
@ -1219,16 +1205,14 @@ bool GLToolbar::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
std::vector<std::string> filenames;
for (GLToolbarItem* item : m_items)
{
for (GLToolbarItem* item : m_items) {
const std::string& icon_filename = item->get_icon_filename();
if (!icon_filename.empty())
filenames.push_back(path + icon_filename);
}
std::vector<std::pair<int, bool>> states;
if (m_type == Normal)
{
if (m_type == Normal) {
states.push_back({ 1, false }); // Normal
states.push_back({ 0, false }); // Pressed
states.push_back({ 2, false }); // Disabled
@ -1236,8 +1220,7 @@ bool GLToolbar::generate_icons_texture() const
states.push_back({ 0, false }); // HoverPressed
states.push_back({ 2, false }); // HoverDisabled
}
else
{
else {
states.push_back({ 1, false }); // Normal
states.push_back({ 1, true }); // Pressed
states.push_back({ 1, false }); // Disabled
@ -1251,9 +1234,9 @@ bool GLToolbar::generate_icons_texture() const
// if (sprite_size_px % 2 != 0)
// sprite_size_px += 1;
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false);
bool res = const_cast<GLTexture*>(&m_icons_texture)->load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false);
if (res)
m_icons_texture_dirty = false;
*const_cast<bool*>(&m_icons_texture_dirty) = false;
return res;
}
@ -1262,8 +1245,7 @@ bool GLToolbar::update_items_visibility()
{
bool ret = false;
for (GLToolbarItem* item : m_items)
{
for (GLToolbarItem* item : m_items) {
ret |= item->update_visibility();
}
@ -1272,12 +1254,10 @@ bool GLToolbar::update_items_visibility()
// updates separators visibility to avoid having two of them consecutive
bool any_item_visible = false;
for (GLToolbarItem* item : m_items)
{
for (GLToolbarItem* item : m_items) {
if (!item->is_separator())
any_item_visible |= item->is_visible();
else
{
else {
item->set_visible(any_item_visible);
any_item_visible = false;
}

View file

@ -233,10 +233,10 @@ private:
EType m_type;
std::string m_name;
bool m_enabled;
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
GLTexture m_icons_texture;
bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
mutable Layout m_layout;
Layout m_layout;
ItemsList m_items;

View file

@ -1525,14 +1525,20 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
if (type == itUndef)
return;
wxDataViewItem parent = m_objects_model->GetParent(item);
if (type & itSettings)
del_settings_from_config(m_objects_model->GetParent(item));
del_settings_from_config(parent);
else if (type & itInstanceRoot && obj_idx != -1)
del_instances_from_object(obj_idx);
else if (type & itLayerRoot && obj_idx != -1)
del_layers_from_object(obj_idx);
else if (type & itLayer && obj_idx != -1)
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (type & itInfo && obj_idx != -1) {
Unselect(item);
Select(parent);
}
else if (idx == -1)
return;
else if (!del_subobject_from_object(obj_idx, idx, type))
@ -1540,9 +1546,10 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
// If last volume item with warning was deleted, unmark object item
if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
m_objects_model->DeleteWarningIcon(parent);
m_objects_model->Delete(item);
update_info_items(obj_idx);
}
void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
@ -2118,20 +2125,32 @@ void ObjectList::part_selection_changed()
{
if (item)
{
if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) {
obj_idx = m_objects_model->GetIdByItem(item);
const ItemType type = m_objects_model->GetItemType(item);
const wxDataViewItem parent = m_objects_model->GetParent(item);
const ItemType parent_type = m_objects_model->GetItemType(parent);
obj_idx = m_objects_model->GetObjectIdByItem(item);
if (parent == wxDataViewItem(nullptr)
|| type == itInfo) {
og_name = _(L("Object manipulation"));
m_config = &(*m_objects)[obj_idx]->config;
update_and_show_manipulations = true;
if (type == itInfo) {
InfoItemType info_type = m_objects_model->GetInfoItemType(item);
if (info_type != InfoItemType::VariableLayerHeight) {
GLGizmosManager::EType gizmo_type =
info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports
: GLGizmosManager::EType::Seam;
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos_mgr.get_current_type() != gizmo_type)
gizmos_mgr.open_gizmo(gizmo_type);
} else
wxGetApp().plater()->toggle_layers_editing(true);
}
}
else {
obj_idx = m_objects_model->GetObjectIdByItem(item);
const ItemType type = m_objects_model->GetItemType(item);
if (type & itSettings) {
const auto parent = m_objects_model->GetParent(item);
const ItemType parent_type = m_objects_model->GetItemType(parent);
if (parent_type & itObject) {
og_name = _(L("Object Settings to modify"));
m_config = &(*m_objects)[obj_idx]->config;
@ -2243,6 +2262,52 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D
return ret;
}
void ObjectList::update_info_items(size_t obj_idx)
{
const ModelObject* model_object = (*m_objects)[obj_idx];
wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx);
assert(item_obj.IsOk());
for (InfoItemType type : {InfoItemType::CustomSupports,
InfoItemType::CustomSeam,
InfoItemType::VariableLayerHeight}) {
wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type);
bool shows = item.IsOk();
bool should_show = false;
switch (type) {
case InfoItemType::CustomSupports :
case InfoItemType::CustomSeam :
should_show = printer_technology() == ptFFF
&& std::any_of(model_object->volumes.begin(), model_object->volumes.end(),
[type](const ModelVolume* mv) {
return ! (type == InfoItemType::CustomSupports
? mv->supported_facets.empty()
: mv->seam_facets.empty());
});
break;
case InfoItemType::VariableLayerHeight :
should_show = printer_technology() == ptFFF
&& ! model_object->layer_height_profile.empty();
break;
}
if (! shows && should_show) {
m_objects_model->AddInfoChild(item_obj, type);
Expand(item_obj);
}
else if (shows && ! should_show) {
Unselect(item);
m_objects_model->Delete(item);
Select(item_obj);
}
}
}
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
{
auto model_object = (*m_objects)[obj_idx];
@ -2251,6 +2316,8 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
get_mesh_errors_count(obj_idx) > 0);
update_info_items(obj_idx);
// add volumes to the object
if (model_object->volumes.size() > 1) {
for (const ModelVolume* volume : model_object->volumes) {
@ -3029,7 +3096,7 @@ void ObjectList::update_selections_on_canvas()
if (sel_cnt == 1) {
wxDataViewItem item = GetSelection();
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo))
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode);
else
add_to_selection(item, selection, instance_idx, mode);
@ -3442,6 +3509,9 @@ void ObjectList::update_object_list_by_printer_technology()
m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items);
for (auto& object_item : object_items) {
// update custom supports info
update_info_items(m_objects_model->GetObjectIdByItem(object_item));
// Update Settings Item for object
update_settings_item_and_selection(object_item, sel);

View file

@ -18,7 +18,6 @@
class wxBoxSizer;
class wxBitmapComboBox;
class wxMenuItem;
class ObjectDataViewModel;
class MenuWithSeparators;
namespace Slic3r {
@ -347,6 +346,7 @@ public:
void update_and_show_object_settings_item();
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
void update_object_list_by_printer_technology();
void update_info_items(size_t obj_idx);
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
void instances_to_separated_objects(const int obj_idx);

View file

@ -7,6 +7,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/ImGuiWrapper.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include <GL/glew.h>
@ -316,8 +317,12 @@ void GLGizmoFdmSupports::update_model_object() const
updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get());
}
if (updated)
if (updated) {
const ModelObjectPtrs& mos = wxGetApp().model().objects;
wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
}

View file

@ -7,7 +7,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/ImGuiWrapper.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include <GL/glew.h>
@ -222,8 +222,12 @@ void GLGizmoSeam::update_model_object() const
updated |= mv->seam_facets.set(*m_triangle_selectors[idx].get());
}
if (updated)
if (updated) {
const ModelObjectPtrs& mos = wxGetApp().model().objects;
wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
}

View file

@ -163,6 +163,17 @@ void GLGizmosManager::reset_all_states()
m_hover = Undefined;
}
bool GLGizmosManager::open_gizmo(EType type)
{
int idx = int(type);
if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) {
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
update_data();
return true;
}
return false;
}
void GLGizmosManager::set_hover_id(int id)
{
if (!m_enabled || m_current == Undefined)
@ -266,24 +277,21 @@ bool GLGizmosManager::is_running() const
bool GLGizmosManager::handle_shortcut(int key)
{
if (!m_enabled)
if (!m_enabled || m_parent.get_selection().is_empty())
return false;
if (m_parent.get_selection().is_empty())
auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(),
[key](const std::unique_ptr<GLGizmoBase>& gizmo) {
int gizmo_key = gizmo->get_shortcut_key();
return gizmo->is_selectable()
&& ((gizmo_key == key - 64) || (gizmo_key == key - 96));
});
if (it == m_gizmos.end())
return false;
bool handled = false;
for (size_t idx : get_selectable_idxs()) {
int it_key = m_gizmos[idx]->get_shortcut_key();
if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) {
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
handled = true;
}
}
return handled;
EType gizmo_type = EType(it - m_gizmos.begin());
return open_gizmo(gizmo_type);
}
bool GLGizmosManager::is_dragging() const
@ -814,10 +822,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
if (!processed && !evt.HasModifiers())
{
if (handle_shortcut(keyCode))
{
update_data();
processed = true;
}
}
if (processed)
@ -1156,5 +1161,11 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const
return true;
}
int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const
{
return m_gizmos[type]->get_shortcut_key();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -171,6 +171,7 @@ public:
void refresh_on_off_state();
void reset_all_states();
bool is_serializing() const { return m_serializing; }
bool open_gizmo(EType type);
void set_hover_id(int id);
void enable_grabber(EType type, unsigned int id, bool enable);
@ -228,6 +229,7 @@ public:
void update_after_undo_redo(const UndoRedo::Snapshot& snapshot);
int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); }
int get_shortcut_key(GLGizmosManager::EType) const;
private:
void render_background(float left, float top, float right, float bottom, float border) const;

View file

@ -522,8 +522,10 @@ void MainFrame::init_tabpanel()
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font());
#endif
#if wxCHECK_VERSION(3,1,3)
if (wxSystemSettings::GetAppearance().IsDark())
m_tabpanel->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
m_tabpanel->Hide();
m_settings_dialog.set_tabpanel(m_tabpanel);

View file

@ -169,13 +169,16 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
// find if hovered
if (m_state == EState::Hovered)
m_state = EState::Shown;
// find if hovered FIXME: do it only in update state?
if (m_state == EState::Hovered) {
m_state = EState::Unknown;
init();
}
if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) {
ImGui::SetNextWindowFocus();
m_state = EState::Hovered;
set_hovered();
}
// color change based on fading out
@ -185,22 +188,8 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
fading_pop = true;
}
// background color
if (m_is_gray) {
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
}
else if (m_data.level == NotificationLevel::ErrorNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
}
else if (m_data.level == NotificationLevel::WarningNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
backcolor.y += 0.15f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
}
bool bgrnd_color_pop = push_background_color();
// name of window indentifies window - has to be unique string
if (m_id == 0)
@ -219,13 +208,34 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
}
imgui.end();
if (m_is_gray || m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification)
if (bgrnd_color_pop)
ImGui::PopStyleColor();
if (fading_pop)
ImGui::PopStyleColor(2);
}
bool NotificationManager::PopNotification::push_background_color()
{
if (m_is_gray) {
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::ErrorNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::WarningNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
backcolor.y += 0.15f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
}
void NotificationManager::PopNotification::count_spaces()
{
//determine line width
@ -242,34 +252,28 @@ void NotificationManager::PopNotification::count_spaces()
m_window_width = m_line_height * 25;
}
void NotificationManager::PopNotification::init()
void NotificationManager::PopNotification::count_lines()
{
// Do not init closing notification
if (is_finished())
return;
std::string text = m_text1 + " " + m_hypertext;
size_t last_end = 0;
m_lines_count = 0;
std::string text = m_text1 + " " + m_hypertext;
size_t last_end = 0;
m_lines_count = 0;
count_spaces();
// count lines
m_endlines.clear();
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
//next line is ended by '/n'
m_endlines.push_back(next_hard_end);
last_end = next_hard_end + 1;
} else {
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
@ -283,7 +287,8 @@ void NotificationManager::PopNotification::init()
}
m_endlines.push_back(last_end + letter_count);
last_end += letter_count;
} else {
}
else {
m_endlines.push_back(next_space);
last_end = next_space + 1;
}
@ -297,11 +302,22 @@ void NotificationManager::PopNotification::init()
}
m_lines_count++;
}
}
void NotificationManager::PopNotification::init()
{
// Do not init closing notification
if (is_finished())
return;
count_spaces();
count_lines();
if (m_lines_count == 3)
m_multiline = true;
m_notification_start = GLCanvas3D::timestamp_now();
//if (m_state != EState::Hidden)
// m_state = EState::Shown;
if (m_state == EState::Unknown)
m_state = EState::Shown;
}
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
{
@ -324,8 +340,8 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
if (m_multiline) {
int last_end = 0;
float starting_y = m_line_height/2;//10;
float shift_y = m_line_height;// -m_line_height / 20;
float starting_y = m_line_height/2;
float shift_y = m_line_height;
for (size_t i = 0; i < m_lines_count; i++) {
std::string line = m_text1.substr(last_end , m_endlines[i] - last_end);
if(i < m_lines_count - 1)
@ -549,7 +565,7 @@ void NotificationManager::PopNotification::update(const NotificationData& n)
m_text2 = n.text2;
init();
}
bool NotificationManager::PopNotification::compare_text(const std::string& text)
bool NotificationManager::PopNotification::compare_text(const std::string& text) const
{
std::wstring wt1 = boost::nowide::widen(m_text1);
std::wstring wt2 = boost::nowide::widen(text);
@ -579,9 +595,10 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
// reset timers - hovered state is set in render
if (m_state == EState::Hovered) {
m_current_fade_opacity = 1.0f;
m_notification_start = now;
m_state = EState::Unknown;
init();
// Timers when not fading
} else if (m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
} else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
int64_t up_time = now - m_notification_start;
if (up_time >= m_data.duration * 1000) {
m_state = EState::FadingOut;
@ -785,53 +802,154 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
void NotificationManager::ProgressBarNotification::init()
{
PopNotification::init();
m_lines_count++;
m_endlines.push_back(m_endlines.back());
}
void NotificationManager::ProgressBarNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
m_left_indentation = m_line_height;
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
std::string text;
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
//m_lines_count++;
if(m_lines_count >= 2) {
m_lines_count = 3;
m_multiline = true;
while (m_endlines.size() < 3)
m_endlines.push_back(m_endlines.back());
} else {
m_lines_count = 2;
m_endlines.push_back(m_endlines.back());
}
m_window_width_offset = m_line_height * (m_has_cancel_button ? 6 : 4);
m_window_width = m_line_height * 25;
if(m_state == EState::Shown)
m_state = EState::NotFading;
}
void NotificationManager::ProgressBarNotification::count_lines()
{
std::string text = m_text1 + " " + m_hypertext;
size_t last_end = 0;
m_lines_count = 0;
m_endlines.clear();
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
//next line is ended by '/n'
m_endlines.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
// when one word longer than line. Or the last space is too early.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
letter_count++;
}
m_endlines.push_back(last_end + letter_count);
last_end += letter_count;
}
else {
m_endlines.push_back(next_space);
last_end = next_space + 1;
}
}
}
else {
m_endlines.push_back(text.length());
last_end = text.length();
}
}
m_lines_count++;
}
}
void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
// line1 - we do not print any more text than what fits on line 1. Line 2 is bar.
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
if (m_has_cancel_button)
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
if (m_multiline) {
// two lines text, one line bar
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(m_line_height / 4);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(m_line_height + m_line_height / 4);
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
imgui.text(line.c_str());
if (m_has_cancel_button)
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
} else {
//one line text, one line bar
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(/*win_size_y / 2 - win_size_y / 6 -*/ m_line_height / 4);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
if (m_has_cancel_button)
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
}
}
void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f);
ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 4);
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 4);
ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y);
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
}
//------PrintHostUploadNotification----------------
void NotificationManager::PrintHostUploadNotification::init()
{
ProgressBarNotification::init();
if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED)
m_state = EState::Shown;
}
void NotificationManager::PrintHostUploadNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
m_left_indentation = m_line_height;
if (m_uj_state == UploadJobState::PB_ERROR) {
std::string text;
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
}
m_window_width_offset = m_line_height * 6; //(m_has_cancel_button ? 6 : 4);
m_window_width = m_line_height * 25;
}
bool NotificationManager::PrintHostUploadNotification::push_background_color()
{
if (m_uj_state == UploadJobState::PB_ERROR) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
}
void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
{
m_percentage = percent;
if (percent >= 1.0f) {
m_uj_state = UploadJobState::PB_COMPLETED;
m_has_cancel_button = false;
init();
} else if (percent < 0.0f) {
error();
} else {
@ -846,34 +964,44 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_PROGRESS:
{
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
float uploaded = m_file_size / 100 * m_percentage;
float uploaded = m_file_size * m_percentage;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded";
text = stream.str();
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
break;
}
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR:
text = _u8L("ERROR");
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
break;
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_CANCELLED:
text = _u8L("CANCELED");
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
break;
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED:
text = _u8L("COMPLETED");
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
break;
}
imgui.text(text.c_str());
}
void NotificationManager::PrintHostUploadNotification::render_left_sign(ImGuiWrapper& imgui)
{
if (m_uj_state == UploadJobState::PB_ERROR) {
std::string text;
text = ImGui::ErrorMarker;
ImGui::SetCursorPosX(m_line_height / 3);
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
imgui.text(text.c_str());
}
}
void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
@ -1059,6 +1187,14 @@ void NotificationManager::close_slicing_errors_and_warnings()
}
}
}
void NotificationManager::close_slicing_error_notification(const std::string& text)
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) {
notification->close();
}
}
}
void NotificationManager::push_slicing_complete_notification(int timestamp, bool large)
{
std::string hypertext;
@ -1122,39 +1258,53 @@ void NotificationManager::push_exporting_finished_notification(const std::string
void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
{
// find if upload with same id was not already in notification
// done by compare_jon_id not compare_text thus has to be performed here
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PrintHostUpload && dynamic_cast<PrintHostUploadNotification*>(notification.get())->compare_job_id(id)) {
return;
}
}
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text };
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text };
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
}
void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
{
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
dynamic_cast<PrintHostUploadNotification*>(notification.get())->set_percentage(percentage);
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
if (notification->get_type() == NotificationType::PrintHostUpload) {
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
if (phun->compare_job_id(id)) {
phun->set_percentage(percentage);
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
break;
}
}
}
}
void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host)
{
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
dynamic_cast<PrintHostUploadNotification*>(notification.get())->cancel();
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
break;
if (notification->get_type() == NotificationType::PrintHostUpload) {
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
if (phun->compare_job_id(id)) {
phun->cancel();
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
break;
}
}
}
}
void NotificationManager::upload_job_notification_show_error(int id, const std::string& filename, const std::string& host)
{
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
dynamic_cast<PrintHostUploadNotification*>(notification.get())->error();
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
break;
if (notification->get_type() == NotificationType::PrintHostUpload) {
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
if(phun->compare_job_id(id)) {
phun->error();
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
break;
}
}
}
}

View file

@ -125,6 +125,7 @@ public:
// void set_slicing_warning_gray(const std::string& text, bool g);
// immediately stops showing slicing errors
void close_slicing_errors_and_warnings();
void close_slicing_error_notification(const std::string& text);
// Release those slicing warnings, which refer to an ObjectID, which is not in the list.
// living_oids is expected to be sorted.
void remove_slicing_warnings_of_released_objects(const std::vector<ObjectID>& living_oids);
@ -207,6 +208,7 @@ private:
Unknown, // NOT initialized
Hidden,
Shown, // Requesting Render at some time if duration != 0
NotFading, // Never jumps to state Fading out even if duration says so
FadingOut, // Requesting Render at some time
ClosePending, // Requesting Render
Finished, // Requesting Render
@ -230,18 +232,17 @@ private:
const NotificationData get_data() const { return m_data; }
const bool is_gray() const { return m_is_gray; }
void set_gray(bool g) { m_is_gray = g; }
bool compare_text(const std::string& text);
virtual bool compare_text(const std::string& text) const;
void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; }
// sets m_next_render with time of next mandatory rendering. Delta is time since last render.
bool update_state(bool paused, const int64_t delta);
int64_t next_render() const { return is_finished() ? 0 : m_next_render; }
EState get_state() const { return m_state; }
bool is_hovered() const { return m_state == EState::Hovered; }
void set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; }
protected:
// Call after every size change
virtual void init();
// Part of init()
virtual void count_spaces();
// Calculetes correct size but not se it in imgui!
virtual void set_next_window_size(ImGuiWrapper& imgui);
virtual void render_text(ImGuiWrapper& imgui,
@ -255,13 +256,20 @@ private:
const std::string text,
bool more = false);
// Left sign could be error or warning sign
void render_left_sign(ImGuiWrapper& imgui);
virtual void render_left_sign(ImGuiWrapper& imgui);
virtual void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y);
// Hypertext action, returns true if notification should close.
// Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)>
virtual bool on_text_click();
protected:
// Part of init(), counts horizontal spacing like left indentation
virtual void count_spaces();
// Part of init(), counts end lines
virtual void count_lines();
// returns true if PopStyleColor should be called later to pop this push
virtual bool push_background_color();
const NotificationData m_data;
// For reusing ImGUI windows.
NotificationIDProvider &m_id_provider;
@ -364,7 +372,8 @@ private:
virtual void set_percentage(float percent) { m_percentage = percent; }
protected:
virtual void init() override;
virtual void count_spaces() override;
virtual void count_lines() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
@ -375,6 +384,8 @@ private:
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
{}
virtual void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y) override {}
float m_percentage;
bool m_has_cancel_button {false};
@ -401,17 +412,23 @@ private:
{
m_has_cancel_button = true;
}
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; }
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
virtual void set_percentage(float percent) override;
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; }
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); }
bool compare_job_id(const int other_id) const { return m_job_id == other_id; }
virtual bool compare_text(const std::string& text) const override { return false; }
protected:
virtual void init() override;
virtual void count_spaces() override;
virtual bool push_background_color() override;
virtual void render_bar(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_cancel_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_left_sign(ImGuiWrapper& imgui) override;
// Identifies job in cancel callback
int m_job_id;
// Size of uploaded size to be displayed in MB

View file

@ -47,6 +47,19 @@ void ObjectDataViewModelNode::init_container()
static constexpr char LayerRootIcon[] = "edit_layers_all";
static constexpr char LayerIcon[] = "edit_layers_some";
static constexpr char WarningIcon[] = "exclamation";
static constexpr char InfoIcon[] = "info";
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) :
m_parent(parent),
m_type(itInfo),
m_extruder(wxEmptyString)
{
m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports")
: info_type == InfoItemType::CustomSeam ? _L("Paint-on seam")
: _L("Variable layer height");
m_info_item_type = info_type;
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) :
m_parent(parent),
@ -69,6 +82,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr
m_name = _(L("Layers"));
}
else if (type == itInfo)
assert(false);
if (type & (itInstanceRoot | itLayerRoot))
init_container();
@ -250,6 +265,7 @@ ObjectDataViewModel::ObjectDataViewModel()
m_volume_bmps = MenuFactory::get_volume_bitmaps();
m_warning_bmp = create_scaled_bitmap(WarningIcon);
m_info_bmp = create_scaled_bitmap(InfoIcon);
}
ObjectDataViewModel::~ObjectDataViewModel()
@ -330,12 +346,37 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
return child;
}
wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
const auto node = new ObjectDataViewModelNode(root, info_type);
// The new item should be added according to its order in InfoItemType.
// Find last info item with lower index and append after it.
const auto& children = root->GetChildren();
int idx = -1;
for (int i=0; i<int(children.size()); ++i) {
if (children[i]->GetType() == itInfo && int(children[i]->GetInfoItemType()) < int(info_type) )
idx = i;
}
root->Insert(node, idx+1);
node->SetBitmap(m_info_bmp);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
return child;
}
wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
const auto node = new ObjectDataViewModelNode(root, itSettings);
root->Insert(node, 0);
// notify control
const wxDataViewItem child((void*)node);
@ -1379,6 +1420,14 @@ ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const
return node->m_type < 0 ? itUndef : node->m_type;
}
InfoItemType ObjectDataViewModel::GetInfoItemType(const wxDataViewItem &item) const
{
if (!item.IsOk())
return InfoItemType::Undef;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->m_info_item_type;
}
wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const
{
if (!parent_item.IsOk())
@ -1411,6 +1460,21 @@ wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item)
return GetItemByType(item, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const
{
if (! parent_item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
for (size_t i = 0; i < node->GetChildCount(); i++) {
const ObjectDataViewModelNode* child_node = node->GetNthChild(i);
if (child_node->m_type == itInfo && child_node->m_info_item_type == type)
return wxDataViewItem((void*)child_node);
}
return wxDataViewItem(0); // not found
}
bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
{
if (!item.IsOk())

View file

@ -27,6 +27,7 @@ enum ItemType {
itSettings = 16,
itLayerRoot = 32,
itLayer = 64,
itInfo = 128
};
enum ColumnNumber
@ -44,6 +45,14 @@ enum PrintIndicator
piUnprintable , // unprintable
};
enum class InfoItemType
{
Undef,
CustomSupports,
CustomSeam,
VariableLayerHeight
};
class ObjectDataViewModelNode;
WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
@ -69,6 +78,7 @@ class ObjectDataViewModelNode
std::string m_action_icon_name = "";
ModelVolumeType m_volume_type;
InfoItemType m_info_item_type {InfoItemType::Undef};
public:
ObjectDataViewModelNode(const wxString& name,
@ -104,6 +114,7 @@ public:
const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType type);
~ObjectDataViewModelNode()
{
@ -176,6 +187,7 @@ public:
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
InfoItemType GetInfoItemType() const { return m_info_item_type; }
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
ModelVolumeType GetVolumeType() { return m_volume_type; }
@ -244,6 +256,7 @@ class ObjectDataViewModel :public wxDataViewModel
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap> m_volume_bmps;
wxBitmap m_warning_bmp;
wxBitmap m_info_bmp;
wxDataViewCtrl* m_ctrl { nullptr };
@ -261,6 +274,7 @@ public:
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
@ -335,12 +349,15 @@ public:
// In our case it is an item with all columns
bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
ItemType GetItemType(const wxDataViewItem &item) const ;
ItemType GetItemType(const wxDataViewItem &item) const;
InfoItemType GetInfoItemType(const wxDataViewItem &item) const;
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);

View file

@ -90,22 +90,24 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const
void OpenGLManager::GLInfo::detect() const
{
m_version = gl_get_string_safe(GL_VERSION, "N/A");
m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A");
m_vendor = gl_get_string_safe(GL_VENDOR, "N/A");
m_renderer = gl_get_string_safe(GL_RENDERER, "N/A");
*const_cast<std::string*>(&m_version) = gl_get_string_safe(GL_VERSION, "N/A");
*const_cast<std::string*>(&m_glsl_version) = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A");
*const_cast<std::string*>(&m_vendor) = gl_get_string_safe(GL_VENDOR, "N/A");
*const_cast<std::string*>(&m_renderer) = gl_get_string_safe(GL_RENDERER, "N/A");
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
int* max_tex_size = const_cast<int*>(&m_max_tex_size);
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, max_tex_size));
m_max_tex_size /= 2;
*max_tex_size /= 2;
if (Slic3r::total_physical_memory() / (1024 * 1024 * 1024) < 6)
m_max_tex_size /= 2;
*max_tex_size /= 2;
if (GLEW_EXT_texture_filter_anisotropic)
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy));
m_detected = true;
if (GLEW_EXT_texture_filter_anisotropic) {
float* max_anisotropy = const_cast<float*>(&m_max_anisotropy);
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
}
*const_cast<bool*>(&m_detected) = true;
}
static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor)
@ -174,19 +176,16 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension
out << b_start << "Renderer: " << b_end << m_renderer << line_end;
out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end;
if (extensions)
{
if (extensions) {
std::vector<std::string> extensions_list;
std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, "");
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off);
if (!extensions_list.empty())
{
if (!extensions_list.empty()) {
out << h2_start << "Installed extensions:" << h2_end << line_end;
std::sort(extensions_list.begin(), extensions_list.end());
for (const std::string& ext : extensions_list)
{
for (const std::string& ext : extensions_list) {
out << ext << line_end;
}
}
@ -252,7 +251,7 @@ bool OpenGLManager::init_gl()
message += _L("You may need to update your graphics card driver.");
#ifdef _WIN32
message += "\n";
message += _L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter.");
message += _L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw-renderer parameter.");
#endif
wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR);
}
@ -304,8 +303,7 @@ wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent)
0
};
if (s_multisample == EMultisampleState::Unknown)
{
if (s_multisample == EMultisampleState::Unknown) {
detect_multisample(attribList);
// // debug output
// std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;

View file

@ -22,14 +22,14 @@ public:
class GLInfo
{
mutable bool m_detected{ false };
mutable int m_max_tex_size{ 0 };
mutable float m_max_anisotropy{ 0.0f };
bool m_detected{ false };
int m_max_tex_size{ 0 };
float m_max_anisotropy{ 0.0f };
mutable std::string m_version;
mutable std::string m_glsl_version;
mutable std::string m_vendor;
mutable std::string m_renderer;
std::string m_version;
std::string m_glsl_version;
std::string m_vendor;
std::string m_renderer;
public:
GLInfo() = default;

View file

@ -2407,6 +2407,29 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
#endif /* AUTOPLACEMENT_ON_LOAD */
}
#if ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
for (size_t i = 0; i < object->instances.size(); ++i) {
ModelInstance* instance = object->instances[i];
const Vec3d size = object->instance_bounding_box(i).size();
const Vec3d ratio = size.cwiseQuotient(bed_size);
const double max_ratio = std::max(ratio(0), ratio(1));
if (max_ratio > 10000) {
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
// so scale down the mesh
double inv = 1. / max_ratio;
object->scale_mesh_after_creation(inv * Vec3d::Ones());
object->origin_translation = Vec3d::Zero();
object->center_around_origin();
scaled_down = true;
break;
}
else if (max_ratio > 5) {
const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones();
instance->set_scaling_factor(inverse.cwiseProduct(instance->get_scaling_factor()));
scaled_down = true;
}
}
#else
const Vec3d size = object->bounding_box().size();
const Vec3d ratio = size.cwiseQuotient(bed_size);
const double max_ratio = std::max(ratio(0), ratio(1));
@ -2425,6 +2448,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
}
scaled_down = true;
}
#endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
object->ensure_on_bed();
}
@ -4943,6 +4967,12 @@ void Plater::convert_unit(ConversionType conv_type)
}
}
void Plater::toggle_layers_editing(bool enable)
{
if (canvas3D()->is_layers_editing_enabled() != enable)
wxPostEvent(canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING));
}
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
{
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");

View file

@ -191,6 +191,7 @@ public:
bool is_selection_empty() const;
void scale_selection_to_fit_print_volume();
void convert_unit(ConversionType conv_type);
void toggle_layers_editing(bool enable);
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);

View file

@ -13,6 +13,7 @@
#include <wx/dataview.h>
#include <wx/wupdlock.h>
#include <wx/debug.h>
#include <wx/msgdlg.h>
#include <boost/log/trivial.hpp>
#include <boost/filesystem.hpp>
@ -68,8 +69,10 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
combo_groups->SetValue(recent_group);
}
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
auto* szr = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
auto* btn_ok = szr->GetAffirmativeButton();
btn_sizer->Add(szr);
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
recent_path += '/';
@ -82,6 +85,20 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
txt_filename->SetValue(recent_path);
txt_filename->SetFocus();
wxString suffix = recent_path.substr(recent_path.find_last_of('.'));
btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) {
wxString path = txt_filename->GetValue();
// .gcode suffix control
if (!path.Lower().EndsWith(suffix.Lower()))
{
wxMessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO);
if (msg_wingow.ShowModal() == wxID_NO)
return;
}
EndDialog(wxID_OK);
});
Fit();
CenterOnParent();

View file

@ -80,7 +80,9 @@ static void unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *con
NSLog(@"-%@",(CFStringRef)deviceModelKey);
*/
if (mediaEjectableKey != nullptr) {
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")) || CFEqual(deviceProtocolName, CFSTR("Secure Digital")));
BOOL op = ejectable &&
( (deviceProtocolName != nullptr && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceProtocolName, CFSTR("Secure Digital")))) ||
(deviceModelKey != nullptr && CFEqual(deviceModelKey, CFSTR("SD Card Reader"))) );
//!CFEqual(deviceModelKey, CFSTR("Disk Image"));
if (op)
[result addObject:volURL.path];

View file

@ -1102,39 +1102,32 @@ void Selection::erase()
if (is_single_full_object())
wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itObject, get_object_idx(), 0);
else if (is_multiple_full_object())
{
else if (is_multiple_full_object()) {
std::vector<ItemForDelete> items;
items.reserve(m_cache.content.size());
for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it)
{
for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) {
items.emplace_back(ItemType::itObject, it->first, 0);
}
wxGetApp().obj_list()->delete_from_model_and_list(items);
}
else if (is_multiple_full_instance())
{
else if (is_multiple_full_instance()) {
std::set<std::pair<int, int>> instances_idxs;
for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it)
{
for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it)
{
for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) {
for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) {
instances_idxs.insert(std::make_pair(obj_it->first, *inst_it));
}
}
std::vector<ItemForDelete> items;
items.reserve(instances_idxs.size());
for (const std::pair<int, int>& i : instances_idxs)
{
for (const std::pair<int, int>& i : instances_idxs) {
items.emplace_back(ItemType::itInstance, i.first, i.second);
}
wxGetApp().obj_list()->delete_from_model_and_list(items);
}
else if (is_single_full_instance())
wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx());
else if (is_mixed())
{
else if (is_mixed()) {
std::set<ItemForDelete> items_set;
std::map<int, int> volumes_in_obj;
@ -1186,11 +1179,9 @@ void Selection::erase()
wxGetApp().obj_list()->delete_from_model_and_list(items);
}
else
{
else {
std::set<std::pair<int, int>> volumes_idxs;
for (unsigned int i : m_list)
{
for (unsigned int i : m_list) {
const GLVolume* v = (*m_volumes)[i];
// Only remove volumes associated with ModelVolumes from the object list.
// Temporary meshes (SLA supports or pads) are not managed by the object list.
@ -1200,8 +1191,7 @@ void Selection::erase()
std::vector<ItemForDelete> items;
items.reserve(volumes_idxs.size());
for (const std::pair<int, int>& v : volumes_idxs)
{
for (const std::pair<int, int>& v : volumes_idxs) {
items.emplace_back(ItemType::itVolume, v.first, v.second);
}
@ -1214,7 +1204,7 @@ void Selection::render(float scale_factor) const
if (!m_valid || is_empty())
return;
m_scale_factor = scale_factor;
*const_cast<float*>(&m_scale_factor) = scale_factor;
// render cumulative bounding box of selected volumes
render_selected_volumes();
@ -1224,7 +1214,7 @@ void Selection::render(float scale_factor) const
#if ENABLE_RENDER_SELECTION_CENTER
void Selection::render_center(bool gizmo_is_dragging) const
{
if (!m_valid || is_empty() || (m_quadric == nullptr))
if (!m_valid || is_empty() || m_quadric == nullptr)
return;
Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center();
@ -1250,8 +1240,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
GLShaderProgram* shader = nullptr;
if (!boost::starts_with(sidebar_field, "layer"))
{
if (!boost::starts_with(sidebar_field, "layer")) {
shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
@ -1735,18 +1724,16 @@ void Selection::do_remove_volume(unsigned int volume_idx)
void Selection::do_remove_instance(unsigned int object_idx, unsigned int instance_idx)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
GLVolume* v = (*m_volumes)[i];
if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx))
if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx)
do_remove_volume(i);
}
}
void Selection::do_remove_object(unsigned int object_idx)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
GLVolume* v = (*m_volumes)[i];
if (v->object_idx() == (int)object_idx)
do_remove_volume(i);
@ -1755,47 +1742,48 @@ void Selection::do_remove_object(unsigned int object_idx)
void Selection::calc_bounding_box() const
{
m_bounding_box = BoundingBoxf3();
if (m_valid)
{
for (unsigned int i : m_list)
{
m_bounding_box.merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
*bounding_box = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
bounding_box->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
}
}
m_bounding_box_dirty = false;
*const_cast<bool*>(&m_bounding_box_dirty) = false;
}
void Selection::calc_unscaled_instance_bounding_box() const
{
m_unscaled_instance_bounding_box = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume &volume = *(*m_volumes)[i];
BoundingBoxf3* unscaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_unscaled_instance_bounding_box);
*unscaled_instance_bounding_box = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume& volume = *(*m_volumes)[i];
if (volume.is_modifier)
continue;
Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
trafo.translation()(2) += volume.get_sla_shift_z();
m_unscaled_instance_bounding_box.merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
m_unscaled_instance_bounding_box_dirty = false;
Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
trafo.translation()(2) += volume.get_sla_shift_z();
unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
*const_cast<bool*>(&m_unscaled_instance_bounding_box_dirty) = false;
}
void Selection::calc_scaled_instance_bounding_box() const
{
m_scaled_instance_bounding_box = BoundingBoxf3();
BoundingBoxf3* scaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_scaled_instance_bounding_box);
*scaled_instance_bounding_box = BoundingBoxf3();
if (m_valid) {
for (unsigned int i : m_list) {
const GLVolume &volume = *(*m_volumes)[i];
const GLVolume& volume = *(*m_volumes)[i];
if (volume.is_modifier)
continue;
Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
trafo.translation()(2) += volume.get_sla_shift_z();
m_scaled_instance_bounding_box.merge(volume.transformed_convex_hull_bounding_box(trafo));
scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
m_scaled_instance_bounding_box_dirty = false;
*const_cast<bool*>(&m_scaled_instance_bounding_box_dirty) = false;
}
void Selection::render_selected_volumes() const

View file

@ -206,14 +206,14 @@ private:
IndicesList m_list;
Cache m_cache;
Clipboard m_clipboard;
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_dirty;
BoundingBoxf3 m_bounding_box;
bool m_bounding_box_dirty;
// Bounding box of a selection, with no instance scaling applied. This bounding box
// is useful for absolute scaling of tilted objects in world coordinate space.
mutable BoundingBoxf3 m_unscaled_instance_bounding_box;
mutable bool m_unscaled_instance_bounding_box_dirty;
mutable BoundingBoxf3 m_scaled_instance_bounding_box;
mutable bool m_scaled_instance_bounding_box_dirty;
BoundingBoxf3 m_unscaled_instance_bounding_box;
bool m_unscaled_instance_bounding_box_dirty;
BoundingBoxf3 m_scaled_instance_bounding_box;
bool m_scaled_instance_bounding_box_dirty;
#if ENABLE_RENDER_SELECTION_CENTER
GLUquadricObj* m_quadric;
@ -222,7 +222,7 @@ private:
GLModel m_arrow;
GLModel m_curved_arrow;
mutable float m_scale_factor;
float m_scale_factor;
public:
Selection();

View file

@ -1515,6 +1515,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_material_with_sheath", category_path + "with-sheath-around-the-support");
optgroup->append_single_option_line("support_material_spacing", category_path + "pattern-spacing-0-inf");
optgroup->append_single_option_line("support_material_angle", category_path + "pattern-angle");
optgroup->append_single_option_line("support_material_closing_radius", category_path + "pattern-angle");
optgroup->append_single_option_line("support_material_interface_layers", category_path + "interface-layers");
optgroup->append_single_option_line("support_material_bottom_interface_layers", category_path + "interface-layers");
optgroup->append_single_option_line("support_material_interface_pattern", category_path + "interface-pattern");