Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
YuSanka 2021-01-13 14:18:55 +01:00
commit af6ef2cb8a
83 changed files with 5953 additions and 3249 deletions

View file

@ -28,6 +28,7 @@
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include "unix/fhs.hpp" // Generated by CMake from ../platform/unix/fhs.hpp.in
@ -150,15 +151,11 @@ int CLI::run(int argc, char **argv)
}
// Read input file(s) if any.
for (const std::string& file : m_input_files) {
std::string ext = boost::filesystem::path(file).extension().string();
if (ext == ".gcode" || ext == ".g") {
if (boost::filesystem::exists(file)) {
start_as_gcodeviewer = true;
break;
}
for (const std::string& file : m_input_files)
if (is_gcode_file(file) && boost::filesystem::exists(file)) {
start_as_gcodeviewer = true;
break;
}
}
if (!start_as_gcodeviewer) {
for (const std::string& file : m_input_files) {
if (!boost::filesystem::exists(file)) {
@ -598,7 +595,9 @@ bool CLI::setup(int argc, char **argv)
}
}
boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
// See Invoking prusa-slicer from $PATH environment variable crashes #5542
// boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
boost::filesystem::path path_to_binary = boost::dll::program_location();
// Path from the Slic3r binary to its resources.
#ifdef __APPLE__

View file

@ -716,45 +716,33 @@ static void traverse_pt_noholes(const ClipperLib::PolyNodes &nodes, Polygons *ou
});
}
static void traverse_pt_old(ClipperLib::PolyNodes &nodes, Polygons* retval)
static void traverse_pt_outside_in(const ClipperLib::PolyNodes &nodes, Polygons *retval)
{
/* use a nearest neighbor search to order these children
TODO: supply start_near to chained_path() too? */
// collect ordering points
Points ordering_points;
ordering_points.reserve(nodes.size());
for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
Point p((*it)->Contour.front().X, (*it)->Contour.front().Y);
ordering_points.push_back(p);
}
// perform the ordering
ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes);
// push results recursively
for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
for (const ClipperLib::PolyNode *node : nodes)
ordering_points.emplace_back(node->Contour.front().X, node->Contour.front().Y);
// Perform the ordering, push results recursively.
//FIXME pass the last point to chain_clipper_polynodes?
for (const ClipperLib::PolyNode *node : chain_clipper_polynodes(ordering_points, nodes)) {
retval->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour));
if (node->IsHole())
// Orient a hole, which is clockwise oriented, to CCW.
retval->back().reverse();
// traverse the next depth
traverse_pt_old((*it)->Childs, retval);
retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
if ((*it)->IsHole()) retval->back().reverse(); // ccw
traverse_pt_outside_in(node->Childs, retval);
}
}
Polygons union_pt_chained(const Polygons &subject, bool safety_offset_)
Polygons union_pt_chained_outside_in(const Polygons &subject, bool safety_offset_)
{
ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_);
Polygons retval;
traverse_pt_old(polytree.Childs, &retval);
traverse_pt_outside_in(polytree.Childs, &retval);
return retval;
// TODO: This needs to be tested:
// ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_);
// Polygons retval;
// traverse_pt_noholes(polytree.Childs, &retval);
// return retval;
}
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)

View file

@ -219,7 +219,7 @@ ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_off
ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false);
ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false);
Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject, bool safety_offset_ = false);
ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);

View file

@ -589,7 +589,7 @@ void ConfigBase::setenv_() const
void ConfigBase::load(const std::string &file)
{
if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g"))
if (is_gcode_file(file))
this->load_from_gcode_file(file);
else
this->load_from_ini(file);

View file

@ -66,7 +66,11 @@ protected:
const std::pair<float, Point> &direction,
ExPolygon expolygon,
Polylines &polylines_out) override;
bool no_sort() const override { return true; }
// Let the G-code export reoder the infill lines.
//FIXME letting the G-code exporter to reorder infill lines of Adaptive Cubic Infill
// may not be optimal as the internal infill lines may get extruded before the long infill
// lines to which the short infill lines are supposed to anchor.
bool no_sort() const override { return false; }
};
}; // namespace FillAdaptive

View file

@ -96,10 +96,10 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
assert(width >= 0);
assert(distance > 0);
// floor(width / distance)
coord_t number_of_intervals = (width - EPSILON) / distance;
coord_t distance_new = (number_of_intervals == 0) ?
const auto number_of_intervals = coord_t((width - EPSILON) / distance);
coord_t distance_new = (number_of_intervals == 0) ?
distance :
((width - EPSILON) / number_of_intervals);
coord_t((width - EPSILON) / number_of_intervals);
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
assert(factor > 1. - 1e-5);
// How much could the extrusion width be increased? By 20%.
@ -143,7 +143,7 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
#ifdef SLIC3R_DEBUG
printf("Filling bridge with angle %f\n", surface->bridge_angle);
#endif /* SLIC3R_DEBUG */
out_angle = surface->bridge_angle;
out_angle = float(surface->bridge_angle);
} else if (this->layer_id != size_t(-1)) {
// alternate fill direction
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
@ -161,15 +161,15 @@ struct ContourIntersectionPoint {
size_t contour_idx;
size_t point_idx;
// Eucleidean parameter of point_idx along its contour.
float param;
double param;
// Other intersection points along the same contour. If there is only a single T-joint on a contour
// with an intersection line, then the prev_on_contour and next_on_contour remain nulls.
ContourIntersectionPoint* prev_on_contour { nullptr };
ContourIntersectionPoint* next_on_contour { nullptr };
// Length of the contour not yet allocated to some extrusion path going back (clockwise), or masked out by some overlapping infill line.
float contour_not_taken_length_prev { std::numeric_limits<float>::max() };
double contour_not_taken_length_prev { std::numeric_limits<double>::max() };
// Length of the contour not yet allocated to some extrusion path going forward (counter-clockwise), or masked out by some overlapping infill line.
float contour_not_taken_length_next { std::numeric_limits<float>::max() };
double contour_not_taken_length_next { std::numeric_limits<double>::max() };
// End point is consumed if an infill line connected to this T-joint was already connected left or right along the contour,
// or if the infill line was processed, but it was not possible to connect it left or right along the contour.
bool consumed { false };
@ -180,13 +180,13 @@ struct ContourIntersectionPoint {
void consume_prev() { this->contour_not_taken_length_prev = 0.; this->prev_trimmed = true; this->consumed = true; }
void consume_next() { this->contour_not_taken_length_next = 0.; this->next_trimmed = true; this->consumed = true; }
void trim_prev(const float new_len) {
void trim_prev(const double new_len) {
if (new_len < this->contour_not_taken_length_prev) {
this->contour_not_taken_length_prev = new_len;
this->prev_trimmed = true;
}
}
void trim_next(const float new_len) {
void trim_next(const double new_len) {
if (new_len < this->contour_not_taken_length_next) {
this->contour_not_taken_length_next = new_len;
this->next_trimmed = true;
@ -207,24 +207,24 @@ struct ContourIntersectionPoint {
};
// Distance from param1 to param2 when going counter-clockwise.
static inline float closed_contour_distance_ccw(float param1, float param2, float contour_length)
static inline double closed_contour_distance_ccw(double param1, double param2, double contour_length)
{
assert(param1 >= 0.f && param1 <= contour_length);
assert(param2 >= 0.f && param2 <= contour_length);
float d = param2 - param1;
if (d < 0.f)
assert(param1 >= 0. && param1 <= contour_length);
assert(param2 >= 0. && param2 <= contour_length);
double d = param2 - param1;
if (d < 0.)
d += contour_length;
return d;
}
// Distance from param1 to param2 when going clockwise.
static inline float closed_contour_distance_cw(float param1, float param2, float contour_length)
static inline double closed_contour_distance_cw(double param1, double param2, double contour_length)
{
return closed_contour_distance_ccw(param2, param1, contour_length);
}
// Length along the contour from cp1 to cp2 going counter-clockwise.
float path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, float contour_length)
double path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
{
assert(cp1 != nullptr);
assert(cp2 != nullptr);
@ -234,13 +234,13 @@ float path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const C
}
// Lengths along the contour from cp1 to cp2 going CCW and going CW.
std::pair<float, float> path_lengths_along_contour(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, float contour_length)
std::pair<double, double> path_lengths_along_contour(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
{
// Zero'th param is the length of the contour.
float param_lo = cp1->param;
float param_hi = cp2->param;
assert(param_lo >= 0.f && param_lo <= contour_length);
assert(param_hi >= 0.f && param_hi <= contour_length);
double param_lo = cp1->param;
double param_hi = cp2->param;
assert(param_lo >= 0. && param_lo <= contour_length);
assert(param_hi >= 0. && param_hi <= contour_length);
bool reversed = false;
if (param_lo > param_hi) {
std::swap(param_lo, param_hi);
@ -267,25 +267,25 @@ static inline void take_cw_full(Polyline &pl, const Points& contour, size_t idx_
}
// Add contour points from interval (idx_start, idx_end> to polyline, limited by the Eucleidean length taken.
static inline float take_cw_limited(Polyline &pl, const Points &contour, const std::vector<float> &params, size_t idx_start, size_t idx_end, float length_to_take)
static inline double take_cw_limited(Polyline &pl, const Points &contour, const std::vector<double> &params, size_t idx_start, size_t idx_end, double length_to_take)
{
// If appending to an infill line, then the start point of a perimeter line shall match the end point of an infill line.
assert(pl.empty() || pl.points.back() == contour[idx_start]);
assert(contour.size() + 1 == params.size());
assert(length_to_take > SCALED_EPSILON);
// Length of the contour.
float length = params.back();
double length = params.back();
// Parameter (length from contour.front()) for the first point.
float p0 = params[idx_start];
double p0 = params[idx_start];
// Current (2nd) point of the contour.
size_t i = (idx_start == 0) ? contour.size() - 1 : idx_start - 1;
// Previous point of the contour.
size_t iprev = idx_start;
// Length of the contour curve taken for iprev.
float lprev = 0.f;
double lprev = 0.;
for (;;) {
float l = closed_contour_distance_cw(p0, params[i], length);
double l = closed_contour_distance_cw(p0, params[i], length);
if (l >= length_to_take) {
// Trim the last segment.
double t = double(length_to_take - lprev) / (l - lprev);
@ -323,16 +323,16 @@ static inline void take_ccw_full(Polyline &pl, const Points &contour, size_t idx
// Add contour points from interval (idx_start, idx_end> to polyline, limited by the Eucleidean length taken.
// Returns length of the contour taken.
static inline float take_ccw_limited(Polyline &pl, const Points &contour, const std::vector<float> &params, size_t idx_start, size_t idx_end, float length_to_take)
static inline double take_ccw_limited(Polyline &pl, const Points &contour, const std::vector<double> &params, size_t idx_start, size_t idx_end, double length_to_take)
{
// If appending to an infill line, then the start point of a perimeter line shall match the end point of an infill line.
assert(pl.empty() || pl.points.back() == contour[idx_start]);
assert(contour.size() + 1 == params.size());
assert(length_to_take > SCALED_EPSILON);
// Length of the contour.
float length = params.back();
double length = params.back();
// Parameter (length from contour.front()) for the first point.
float p0 = params[idx_start];
double p0 = params[idx_start];
// Current (2nd) point of the contour.
size_t i = idx_start;
if (++ i == contour.size())
@ -340,9 +340,9 @@ static inline float take_ccw_limited(Polyline &pl, const Points &contour, const
// Previous point of the contour.
size_t iprev = idx_start;
// Length of the contour curve taken at iprev.
float lprev = 0.f;
double lprev = 0;
for (;;) {
float l = closed_contour_distance_ccw(p0, params[i], length);
double l = closed_contour_distance_ccw(p0, params[i], length);
if (l >= length_to_take) {
// Trim the last segment.
double t = double(length_to_take - lprev) / (l - lprev);
@ -411,8 +411,8 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, Cont
}
static void take_limited(
Polyline &pl1, const Points &contour, const std::vector<float> &params,
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
Polyline &pl1, const Points &contour, const std::vector<double> &params,
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
{
#ifndef NDEBUG
// This is a valid case, where a single infill line connect to two different contours (outer contour + hole or two holes).
@ -445,11 +445,11 @@ static void take_limited(
pl1.points.reserve(pl1.points.size() + pl_tmp.size() + size_t(new_points));
}
float length = params.back();
float length_to_go = take_max_length;
double length = params.back();
double length_to_go = take_max_length;
cp_start->consumed = true;
if (cp_start == cp_end) {
length_to_go = std::max(0.f, std::min(length_to_go, length - line_half_width));
length_to_go = std::max(0., std::min(length_to_go, length - line_half_width));
length_to_go = std::min(length_to_go, clockwise ? cp_start->contour_not_taken_length_prev : cp_start->contour_not_taken_length_next);
cp_start->consume_prev();
cp_start->consume_next();
@ -462,11 +462,11 @@ static void take_limited(
assert(cp_start != cp_end);
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
// Length of the segment from cp to cp->prev_on_contour.
float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
double l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_prev);
//if (cp->prev_on_contour->consumed)
// Don't overlap with an already extruded infill line.
length_to_go = std::max(0.f, std::min(length_to_go, l - line_half_width));
length_to_go = std::max(0., std::min(length_to_go, l - line_half_width));
cp->consume_prev();
if (l >= length_to_go) {
if (length_to_go > SCALED_EPSILON) {
@ -475,7 +475,7 @@ static void take_limited(
}
break;
} else {
cp->prev_on_contour->trim_next(0.f);
cp->prev_on_contour->trim_next(0.);
take_cw_full(pl1, contour, cp->point_idx, cp->prev_on_contour->point_idx);
length_to_go -= l;
}
@ -483,11 +483,11 @@ static void take_limited(
} else {
assert(cp_start != cp_end);
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
double l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
//if (cp->next_on_contour->consumed)
// Don't overlap with an already extruded infill line.
length_to_go = std::max(0.f, std::min(length_to_go, l - line_half_width));
length_to_go = std::max(0., std::min(length_to_go, l - line_half_width));
cp->consume_next();
if (l >= length_to_go) {
if (length_to_go > SCALED_EPSILON) {
@ -496,7 +496,7 @@ static void take_limited(
}
break;
} else {
cp->next_on_contour->trim_prev(0.f);
cp->next_on_contour->trim_prev(0.);
take_ccw_full(pl1, contour, cp->point_idx, cp->next_on_contour->point_idx);
length_to_go -= l;
}
@ -678,19 +678,19 @@ static inline bool line_rounded_thick_segment_collision(
return intersects;
}
static inline bool inside_interval(float low, float high, float p)
static inline bool inside_interval(double low, double high, double p)
{
return p >= low && p <= high;
}
static inline bool interval_inside_interval(float outer_low, float outer_high, float inner_low, float inner_high, float epsilon)
static inline bool interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double epsilon)
{
outer_low -= epsilon;
outer_high += epsilon;
return inside_interval(outer_low, outer_high, inner_low) && inside_interval(outer_low, outer_high, inner_high);
}
static inline bool cyclic_interval_inside_interval(float outer_low, float outer_high, float inner_low, float inner_high, float length)
static inline bool cyclic_interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double length)
{
if (outer_low > outer_high)
outer_high += length;
@ -700,7 +700,7 @@ static inline bool cyclic_interval_inside_interval(float outer_low, float outer_
inner_low += length;
inner_high += length;
}
return interval_inside_interval(outer_low, outer_high, inner_low, inner_high, float(SCALED_EPSILON));
return interval_inside_interval(outer_low, outer_high, inner_low, inner_high, double(SCALED_EPSILON));
}
// #define INFILL_DEBUG_OUTPUT
@ -710,7 +710,7 @@ static void export_infill_to_svg(
// Boundary contour, along which the perimeter extrusions will be drawn.
const std::vector<Points> &boundary,
// Parametrization of boundary with Euclidian length.
const std::vector<std::vector<float>> &boundary_parameters,
const std::vector<std::vector<double>> &boundary_parameters,
// Intersections (T-joints) of the infill lines with the boundary.
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
// Infill lines, either completely inside the boundary, or touching the boundary.
@ -739,7 +739,7 @@ static void export_infill_to_svg(
for (const std::vector<ContourIntersectionPoint*> &intersections : boundary_intersections) {
const size_t boundary_idx = &intersections - boundary_intersections.data();
const Points &contour = boundary[boundary_idx];
const std::vector<float> &contour_param = boundary_parameters[boundary_idx];
const std::vector<double> &contour_param = boundary_parameters[boundary_idx];
for (const ContourIntersectionPoint *ip : intersections) {
assert(ip->next_trimmed == ip->next_on_contour->prev_trimmed);
assert(ip->prev_trimmed == ip->prev_on_contour->next_trimmed);
@ -834,7 +834,7 @@ void mark_boundary_segments_touching_infill(
// Boundary contour, along which the perimeter extrusions will be drawn.
const std::vector<Points> &boundary,
// Parametrization of boundary with Euclidian length.
const std::vector<std::vector<float>> &boundary_parameters,
const std::vector<std::vector<double>> &boundary_parameters,
// Intersections (T-joints) of the infill lines with the boundary.
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
// Bounding box around the boundary.
@ -865,12 +865,12 @@ void mark_boundary_segments_touching_infill(
// Make sure that the the grid is big enough for queries against the thick segment.
grid.set_bbox(boundary_bbox.inflated(distance_colliding * 1.43));
// Inflate the bounding box by a thick line width.
grid.create(boundary, std::max(clip_distance, distance_colliding) + scale_(10.));
grid.create(boundary, coord_t(std::max(clip_distance, distance_colliding) + scale_(10.)));
// Visitor for the EdgeGrid to trim boundary_intersections with existing infill lines.
struct Visitor {
Visitor(const EdgeGrid::Grid &grid,
const std::vector<Points> &boundary, const std::vector<std::vector<float>> &boundary_parameters, std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
const std::vector<Points> &boundary, const std::vector<std::vector<double>> &boundary_parameters, std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
const double radius) :
grid(grid), boundary(boundary), boundary_parameters(boundary_parameters), boundary_intersections(boundary_intersections), radius(radius), trim_l_threshold(0.5 * radius) {}
@ -907,10 +907,10 @@ void mark_boundary_segments_touching_infill(
// The boundary segment intersects with the infill segment thickened by radius.
// Interval is specified in Euclidian length from seg_pt1 to seg_pt2.
// 1) Find the Euclidian parameters of seg_pt1 and seg_pt2 on its boundary contour.
const std::vector<float> &contour_parameters = boundary_parameters[it_contour_and_segment->first];
const float contour_length = contour_parameters.back();
const float param_seg_pt1 = contour_parameters[it_contour_and_segment->second];
const float param_seg_pt2 = contour_parameters[it_contour_and_segment->second + 1];
const std::vector<double> &contour_parameters = boundary_parameters[it_contour_and_segment->first];
const double contour_length = contour_parameters.back();
const double param_seg_pt1 = contour_parameters[it_contour_and_segment->second];
const double param_seg_pt2 = contour_parameters[it_contour_and_segment->second + 1];
#ifdef INFILL_DEBUG_OUTPUT
this->perimeter_overlaps.push_back({ Point((seg_pt1 + (seg_pt2 - seg_pt1).normalized() * interval.first).cast<coord_t>()),
Point((seg_pt1 + (seg_pt2 - seg_pt1).normalized() * interval.second).cast<coord_t>()) });
@ -918,8 +918,8 @@ void mark_boundary_segments_touching_infill(
assert(interval.first >= 0.);
assert(interval.second >= 0.);
assert(interval.first <= interval.second);
const auto param_overlap1 = std::min(param_seg_pt2, float(param_seg_pt1 + interval.first));
const auto param_overlap2 = std::min(param_seg_pt2, float(param_seg_pt1 + interval.second));
const auto param_overlap1 = std::min(param_seg_pt2, param_seg_pt1 + interval.first);
const auto param_overlap2 = std::min(param_seg_pt2, param_seg_pt1 + interval.second);
// 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
// Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
ContourIntersectionPoint *ip_low, *ip_high;
@ -946,7 +946,7 @@ void mark_boundary_segments_touching_infill(
ip->consume_next();
}
// Subtract the interval from the first and last segments.
float trim_l = closed_contour_distance_ccw(ip_low->param, param_overlap1, contour_length);
double trim_l = closed_contour_distance_ccw(ip_low->param, param_overlap1, contour_length);
//if (trim_l > trim_l_threshold)
ip_low->trim_next(trim_l);
trim_l = closed_contour_distance_ccw(param_overlap2, ip_high->param, contour_length);
@ -978,12 +978,12 @@ void mark_boundary_segments_touching_infill(
const EdgeGrid::Grid &grid;
const std::vector<Points> &boundary;
const std::vector<std::vector<float>> &boundary_parameters;
const std::vector<std::vector<double>> &boundary_parameters;
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections;
// Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line.
const double radius;
// Region around the contour / infill line intersection point, where the intersections are ignored.
const float trim_l_threshold;
const double trim_l_threshold;
const Vec2d *infill_pt1;
const Vec2d *infill_pt2;
@ -1100,11 +1100,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams &params)
{
assert(! infill_ordered.empty());
assert(params.anchor_length >= 0.f);
assert(params.anchor_length >= 0.);
assert(params.anchor_length_max >= 0.01f);
assert(params.anchor_length_max >= params.anchor_length);
const auto anchor_length = float(scale_(params.anchor_length));
const auto anchor_length_max = float(scale_(params.anchor_length_max));
const double anchor_length = scale_(params.anchor_length);
const double anchor_length_max = scale_(params.anchor_length_max);
#if 0
append(polylines_out, infill_ordered);
@ -1113,9 +1113,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
// 1) Add the end points of infill_ordered to boundary_src.
std::vector<Points> boundary;
std::vector<std::vector<float>> boundary_params;
std::vector<std::vector<double>> boundary_params;
boundary.assign(boundary_src.size(), Points());
boundary_params.assign(boundary_src.size(), std::vector<float>());
boundary_params.assign(boundary_src.size(), std::vector<double>());
// Mapping the infill_ordered end point to a (contour, point) of boundary.
static constexpr auto boundary_idx_unconnected = std::numeric_limits<size_t>::max();
std::vector<ContourIntersectionPoint> map_infill_end_point_to_boundary(infill_ordered.size() * 2, ContourIntersectionPoint{ boundary_idx_unconnected, boundary_idx_unconnected });
@ -1125,11 +1125,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
{
EdgeGrid::Grid grid;
grid.set_bbox(bbox.inflated(SCALED_EPSILON));
grid.create(boundary_src, scale_(10.));
grid.create(boundary_src, coord_t(scale_(10.)));
intersection_points.reserve(infill_ordered.size() * 2);
for (const Polyline &pl : infill_ordered)
for (const Point *pt : { &pl.points.front(), &pl.points.back() }) {
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, coord_t(SCALED_EPSILON));
if (cp.valid()) {
// The infill end point shall lie on the contour.
assert(cp.distance <= 3.);
@ -1163,21 +1163,29 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
contour_intersection_points.reserve(n_intersection_points);
}
for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++ idx_point) {
contour_dst.emplace_back(contour_src.points[idx_point]);
const Point &ipt = contour_src.points[idx_point];
if (contour_dst.empty() || contour_dst.back() != ipt)
contour_dst.emplace_back(ipt);
for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) {
// Add these points to the destination contour.
const Polyline &infill_line = infill_ordered[it->second / 2];
const Point &pt = (it->second & 1) ? infill_line.points.back() : infill_line.points.front();
#ifndef NDEBUG
{
const Vec2d pt1 = contour_src[idx_point].cast<double>();
const Vec2d pt1 = ipt.cast<double>();
const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast<double>();
const Vec2d ptx = lerp(pt1, pt2, it->first.t);
assert(std::abs(pt.x() - pt.x()) < SCALED_EPSILON);
assert(std::abs(pt.y() - pt.y()) < SCALED_EPSILON);
}
#endif // NDEBUG
map_infill_end_point_to_boundary[it->second] = ContourIntersectionPoint{ idx_contour, contour_dst.size() };
size_t idx_tjoint_pt = 0;
if (idx_point + 1 < contour_src.size() || pt != contour_dst.front()) {
if (pt != contour_dst.back())
contour_dst.emplace_back(pt);
idx_tjoint_pt = contour_dst.size() - 1;
}
map_infill_end_point_to_boundary[it->second] = ContourIntersectionPoint{ idx_contour, idx_tjoint_pt };
ContourIntersectionPoint *pthis = &map_infill_end_point_to_boundary[it->second];
if (pprev) {
pprev->next_on_contour = pthis;
@ -1186,8 +1194,6 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
pfirst = pthis;
contour_intersection_points.emplace_back(pthis);
pprev = pthis;
//add new point here
contour_dst.emplace_back(pt);
}
if (pfirst) {
pprev->next_on_contour = pfirst;
@ -1195,16 +1201,19 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
}
}
// Parametrize the new boundary with the intersection points inserted.
std::vector<float> &contour_params = boundary_params[idx_contour];
contour_params.assign(contour_dst.size() + 1, 0.f);
for (size_t i = 1; i < contour_dst.size(); ++ i)
contour_params[i] = contour_params[i - 1] + (contour_dst[i].cast<float>() - contour_dst[i - 1].cast<float>()).norm();
contour_params.back() = contour_params[contour_params.size() - 2] + (contour_dst.back().cast<float>() - contour_dst.front().cast<float>()).norm();
std::vector<double> &contour_params = boundary_params[idx_contour];
contour_params.assign(contour_dst.size() + 1, 0.);
for (size_t i = 1; i < contour_dst.size(); ++i) {
contour_params[i] = contour_params[i - 1] + (contour_dst[i].cast<double>() - contour_dst[i - 1].cast<double>()).norm();
assert(contour_params[i] > contour_params[i - 1]);
}
contour_params.back() = contour_params[contour_params.size() - 2] + (contour_dst.back().cast<double>() - contour_dst.front().cast<double>()).norm();
assert(contour_params.back() > contour_params[contour_params.size() - 2]);
// Map parameters from contour_params to boundary_intersection_points.
for (ContourIntersectionPoint *ip : contour_intersection_points)
ip->param = contour_params[ip->point_idx];
// and measure distance to the previous and next intersection point.
const float contour_length = contour_params.back();
const double contour_length = contour_params.back();
for (ContourIntersectionPoint *ip : contour_intersection_points)
if (ip->next_on_contour == ip) {
assert(ip->prev_on_contour == ip);
@ -1238,9 +1247,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
}
// Connection from end of one infill line to the start of another infill line.
//const float length_max = scale_(spacing);
// const auto length_max = float(scale_((2. / params.density) * spacing));
const auto length_max = float(scale_((1000. / params.density) * spacing));
//const double length_max = scale_(spacing);
// const auto length_max = double(scale_((2. / params.density) * spacing));
const auto length_max = double(scale_((1000. / params.density) * spacing));
std::vector<size_t> merged_with(infill_ordered.size());
std::iota(merged_with.begin(), merged_with.end(), 0);
struct ConnectionCost {
@ -1258,7 +1267,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
const ContourIntersectionPoint *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2];
if (cp1->contour_idx != boundary_idx_unconnected && cp1->contour_idx == cp2->contour_idx) {
// End points on the same contour. Try to connect them.
std::pair<float, float> len = path_lengths_along_contour(cp1, cp2, boundary_params[cp1->contour_idx].back());
std::pair<double, double> len = path_lengths_along_contour(cp1, cp2, boundary_params[cp1->contour_idx].back());
if (len.first < length_max)
connections_sorted.emplace_back(idx_chain - 1, len.first, false);
if (len.second < length_max)
@ -1281,7 +1290,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
return std::numeric_limits<size_t>::max();
};
const float line_half_width = 0.5f * scale_(spacing);
const double line_half_width = 0.5 * scale_(spacing);
#if 0
for (ConnectionCost &connection_cost : connections_sorted) {
@ -1291,7 +1300,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
assert(cp1->contour_idx == cp2->contour_idx && cp1->contour_idx != boundary_idx_unconnected);
if (cp1->consumed || cp2->consumed)
continue;
const float length = connection_cost.cost;
const double length = connection_cost.cost;
bool could_connect;
{
// cp1, cp2 sorted CCW.
@ -1334,7 +1343,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
struct Arc {
ContourIntersectionPoint *intersection;
float arc_length;
double arc_length;
};
std::vector<Arc> arches;
arches.reserve(map_infill_end_point_to_boundary.size());
@ -1352,7 +1361,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
size_t polyline_idx1 = get_and_update_merged_with(((cp1 - map_infill_end_point_to_boundary.data()) / 2));
size_t polyline_idx2 = get_and_update_merged_with(((cp2 - map_infill_end_point_to_boundary.data()) / 2));
const Points &contour = boundary[cp1->contour_idx];
const std::vector<float> &contour_params = boundary_params[cp1->contour_idx];
const std::vector<double> &contour_params = boundary_params[cp1->contour_idx];
if (polyline_idx1 != polyline_idx2) {
Polyline &polyline1 = infill_ordered[polyline_idx1];
Polyline &polyline2 = infill_ordered[polyline_idx2];
@ -1385,23 +1394,23 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
// Connect the remaining open infill lines to the perimeter lines if possible.
for (ContourIntersectionPoint &contour_point : map_infill_end_point_to_boundary)
if (! contour_point.consumed && contour_point.contour_idx != boundary_idx_unconnected) {
const Points &contour = boundary[contour_point.contour_idx];
const std::vector<float> &contour_params = boundary_params[contour_point.contour_idx];
const size_t contour_pt_idx = contour_point.point_idx;
const Points &contour = boundary[contour_point.contour_idx];
const std::vector<double> &contour_params = boundary_params[contour_point.contour_idx];
const size_t contour_pt_idx = contour_point.point_idx;
float lprev = contour_point.could_connect_prev() ?
double lprev = contour_point.could_connect_prev() ?
path_length_along_contour_ccw(contour_point.prev_on_contour, &contour_point, contour_params.back()) :
std::numeric_limits<float>::max();
float lnext = contour_point.could_connect_next() ?
std::numeric_limits<double>::max();
double lnext = contour_point.could_connect_next() ?
path_length_along_contour_ccw(&contour_point, contour_point.next_on_contour, contour_params.back()) :
std::numeric_limits<float>::max();
std::numeric_limits<double>::max();
size_t polyline_idx = get_and_update_merged_with(((&contour_point - map_infill_end_point_to_boundary.data()) / 2));
Polyline &polyline = infill_ordered[polyline_idx];
assert(! polyline.empty());
assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
bool connected = false;
for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
if (l == std::numeric_limits<float>::max() || l > anchor_length_max)
for (double l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
if (l == std::numeric_limits<double>::max() || l > anchor_length_max)
break;
// Take the complete contour.
bool reversed = l == lprev;
@ -1439,7 +1448,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
// 2) Hook length
// ...
// Let's take the longer now, as this improves the chance of another hook to be placed on the other side of this contour point.
float l = std::max(contour_point.contour_not_taken_length_prev, contour_point.contour_not_taken_length_next);
double l = std::max(contour_point.contour_not_taken_length_prev, contour_point.contour_not_taken_length_next);
if (l > SCALED_EPSILON) {
if (contour_point.contour_not_taken_length_prev > contour_point.contour_not_taken_length_next)
take_limited(polyline, contour, contour_params, &contour_point, contour_point.prev_on_contour, true, anchor_length, line_half_width);

View file

@ -24,22 +24,22 @@ void FillConcentric::_fill_surface_single(
this->spacing = unscale<double>(distance);
}
Polygons loops = (Polygons)expolygon;
Polygons loops = to_polygons(std::move(expolygon));
Polygons last = loops;
while (! last.empty()) {
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
loops.insert(loops.end(), last.begin(), last.end());
append(loops, last);
}
// generate paths from the outermost to the innermost, to avoid
// adhesion problems of the first central tiny loops
loops = union_pt_chained(loops, false);
loops = union_pt_chained_outside_in(loops, false);
// split paths using a nearest neighbor search
size_t iPathFirst = polylines_out.size();
Point last_pos(0, 0);
for (const Polygon &loop : loops) {
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
polylines_out.emplace_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
last_pos = polylines_out.back().last_point();
}

View file

@ -8,7 +8,7 @@ namespace Slic3r {
class FillConcentric : public Fill
{
public:
~FillConcentric() override {}
~FillConcentric() override = default;
protected:
Fill* clone() const override { return new FillConcentric(*this); };

View file

@ -37,7 +37,7 @@ protected:
bool _can_connect(coord_t dist_X, coord_t dist_Y)
{
coord_t TOLERANCE = 10 * SCALED_EPSILON;
const auto TOLERANCE = coord_t(10 * SCALED_EPSILON);
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
&& (dist_Y <= this->_diagonal_distance);

View file

@ -56,59 +56,59 @@ const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_poin
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
static constexpr char* MODEL_TAG = "model";
static constexpr char* RESOURCES_TAG = "resources";
static constexpr char* OBJECT_TAG = "object";
static constexpr char* MESH_TAG = "mesh";
static constexpr char* VERTICES_TAG = "vertices";
static constexpr char* VERTEX_TAG = "vertex";
static constexpr char* TRIANGLES_TAG = "triangles";
static constexpr char* TRIANGLE_TAG = "triangle";
static constexpr char* COMPONENTS_TAG = "components";
static constexpr char* COMPONENT_TAG = "component";
static constexpr char* BUILD_TAG = "build";
static constexpr char* ITEM_TAG = "item";
static constexpr char* METADATA_TAG = "metadata";
static constexpr const char* MODEL_TAG = "model";
static constexpr const char* RESOURCES_TAG = "resources";
static constexpr const char* OBJECT_TAG = "object";
static constexpr const char* MESH_TAG = "mesh";
static constexpr const char* VERTICES_TAG = "vertices";
static constexpr const char* VERTEX_TAG = "vertex";
static constexpr const char* TRIANGLES_TAG = "triangles";
static constexpr const char* TRIANGLE_TAG = "triangle";
static constexpr const char* COMPONENTS_TAG = "components";
static constexpr const char* COMPONENT_TAG = "component";
static constexpr const char* BUILD_TAG = "build";
static constexpr const char* ITEM_TAG = "item";
static constexpr const char* METADATA_TAG = "metadata";
static constexpr char* CONFIG_TAG = "config";
static constexpr char* VOLUME_TAG = "volume";
static constexpr const char* CONFIG_TAG = "config";
static constexpr const char* VOLUME_TAG = "volume";
static constexpr char* UNIT_ATTR = "unit";
static constexpr char* NAME_ATTR = "name";
static constexpr char* TYPE_ATTR = "type";
static constexpr char* ID_ATTR = "id";
static constexpr char* X_ATTR = "x";
static constexpr char* Y_ATTR = "y";
static constexpr char* Z_ATTR = "z";
static constexpr char* V1_ATTR = "v1";
static constexpr char* V2_ATTR = "v2";
static constexpr char* V3_ATTR = "v3";
static constexpr char* OBJECTID_ATTR = "objectid";
static constexpr char* TRANSFORM_ATTR = "transform";
static constexpr char* PRINTABLE_ATTR = "printable";
static constexpr char* INSTANCESCOUNT_ATTR = "instances_count";
static constexpr char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
static constexpr char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
static constexpr const char* UNIT_ATTR = "unit";
static constexpr const char* NAME_ATTR = "name";
static constexpr const char* TYPE_ATTR = "type";
static constexpr const char* ID_ATTR = "id";
static constexpr const char* X_ATTR = "x";
static constexpr const char* Y_ATTR = "y";
static constexpr const char* Z_ATTR = "z";
static constexpr const char* V1_ATTR = "v1";
static constexpr const char* V2_ATTR = "v2";
static constexpr const char* V3_ATTR = "v3";
static constexpr const char* OBJECTID_ATTR = "objectid";
static constexpr const char* TRANSFORM_ATTR = "transform";
static constexpr const char* PRINTABLE_ATTR = "printable";
static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
static constexpr const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
static constexpr const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
static constexpr char* KEY_ATTR = "key";
static constexpr char* VALUE_ATTR = "value";
static constexpr char* FIRST_TRIANGLE_ID_ATTR = "firstid";
static constexpr char* LAST_TRIANGLE_ID_ATTR = "lastid";
static constexpr const char* KEY_ATTR = "key";
static constexpr const char* VALUE_ATTR = "value";
static constexpr const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
static constexpr char* OBJECT_TYPE = "object";
static constexpr char* VOLUME_TYPE = "volume";
static constexpr const char* OBJECT_TYPE = "object";
static constexpr const char* VOLUME_TYPE = "volume";
static constexpr char* NAME_KEY = "name";
static constexpr char* MODIFIER_KEY = "modifier";
static constexpr char* VOLUME_TYPE_KEY = "volume_type";
static constexpr char* MATRIX_KEY = "matrix";
static constexpr char* SOURCE_FILE_KEY = "source_file";
static constexpr char* SOURCE_OBJECT_ID_KEY = "source_object_id";
static constexpr char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
static constexpr char* SOURCE_OFFSET_X_KEY = "source_offset_x";
static constexpr char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
static constexpr char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr char* SOURCE_IN_INCHES = "source_in_inches";
static constexpr const char* NAME_KEY = "name";
static constexpr const char* MODIFIER_KEY = "modifier";
static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
static constexpr const char* MATRIX_KEY = "matrix";
static constexpr const char* SOURCE_FILE_KEY = "source_file";
static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@ -2251,7 +2251,7 @@ namespace Slic3r {
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
std::string name = boost::filesystem::path(filename).stem().string();
std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n";

View file

@ -714,8 +714,21 @@ namespace DoExport {
if (region->config().get_abs_value("infill_speed") == 0 ||
region->config().get_abs_value("solid_infill_speed") == 0 ||
region->config().get_abs_value("top_solid_infill_speed") == 0 ||
region->config().get_abs_value("bridge_speed") == 0)
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
region->config().get_abs_value("bridge_speed") == 0)
{
// Minimal volumetric flow should not be calculated over ironing extrusions.
// Use following lambda instead of the built-it method.
// https://github.com/prusa3d/PrusaSlicer/issues/5082
auto min_mm3_per_mm_no_ironing = [](const ExtrusionEntityCollection& eec) -> double {
double min = std::numeric_limits<double>::max();
for (const ExtrusionEntity* ee : eec.entities)
if (ee->role() != erIroning)
min = std::min(min, ee->min_mm3_per_mm());
return min;
};
mm3_per_mm.push_back(min_mm3_per_mm_no_ironing(layerm->fills));
}
}
}
if (object->config().get_abs_value("support_material_speed") == 0 ||
@ -1709,7 +1722,9 @@ namespace Skirt {
//FIXME infinite or high skirt does not make sense for sequential print!
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet (sequential print)
skirt_done.back() < layer_tools.print_z - EPSILON &&
// FIXME: The skirt_done should not be empty at this point. The check is a workaround
// of https://github.com/prusa3d/PrusaSlicer/issues/5652, but it deserves a real fix.
(! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON) &&
// and this layer is an object layer, or it is a raft layer.
(layer_tools.has_object || support_layer->id() < (size_t)support_layer->object()->config().raft_layers.value)) {
#if 0
@ -2096,6 +2111,8 @@ void GCode::process_layer(
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
m_layer = layers[instance_to_print.layer_id].layer();
}
//FIXME order islands?
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast<unsigned int>(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region;
//FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.

View file

@ -2345,7 +2345,9 @@ void GCodeProcessor::process_T(const std::string_view command)
if (command.length() > 1) {
int eid;
if (! parse_number(command.substr(1), eid) || eid < 0 || eid > 255) {
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ").";
// T-1 is a valid gcode line for RepRap Firmwares (used to deselects all tools) see https://github.com/prusa3d/PrusaSlicer/issues/5677
if ((m_flavor != gcfRepRapFirmware && m_flavor != gcfRepRapSprinter) || eid != -1)
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ").";
} else {
unsigned char id = static_cast<unsigned char>(eid);
if (m_extruder_id != id) {

View file

@ -134,7 +134,7 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
speed_t newSpeed = baud_rate;
ioctl(handle, IOSSIOSPEED, &newSpeed);
::tcsetattr(handle, TCSANOW, &ios);
#elif __linux
#elif __linux__
termios2 ios;
if (ioctl(handle, TCGETS2, &ios))
printf("Error in TCGETS2: %s\n", strerror(errno));

View file

@ -1152,18 +1152,22 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
{
if (opt_key == "bed_shape" || opt_key == "thumbnails" || opt_key == "compatible_prints" || opt_key == "compatible_printers") {
// Scalar variable, or a vector variable, which is independent from number of extruders,
// thus the vector is presented to the user as a single input.
diff.emplace_back(opt_key);
continue;
}
switch (other_opt->type())
{
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
default: diff.emplace_back(opt_key); break;
} else if (opt_key == "default_filament_profile") {
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
// Also the length of this field may differ, which may lead to a crash if the block below is used.
} else {
switch (other_opt->type()) {
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
default: diff.emplace_back(opt_key); break;
}
}
}
}

View file

@ -86,9 +86,8 @@ PresetBundle::PresetBundle() :
preset.config.optptr(key, true);
if (i == 0) {
preset.config.optptr("default_print_profile", true);
preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
}
else {
preset.config.option<ConfigOptionStrings>("default_filament_profile", true);
} else {
preset.config.optptr("default_sla_print_profile", true);
preset.config.optptr("default_sla_material_profile", true);
}
@ -668,7 +667,7 @@ DynamicPrintConfig PresetBundle::full_sla_config() const
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
void PresetBundle::load_config_file(const std::string &path)
{
if (boost::iends_with(path, ".gcode") || boost::iends_with(path, ".g")) {
if (is_gcode_file(path)) {
DynamicPrintConfig config;
config.apply(FullPrintConfig::defaults());
config.load_from_gcode_file(path);
@ -752,7 +751,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
switch (printer_technology) {
case ptFFF:
config.option<ConfigOptionString>("default_print_profile", true);
config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
config.option<ConfigOptionStrings>("default_filament_profile", true);
break;
case ptSLA:
config.option<ConfigOptionString>("default_sla_print_profile", true);

View file

@ -1851,10 +1851,7 @@ void Print::_make_brim()
}
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
}
loops = union_pt_chained(loops, false);
// The function above produces ordering well suited for concentric infill (from outside to inside).
// For Brim, the ordering should be reversed (from inside to outside).
std::reverse(loops.begin(), loops.end());
loops = union_pt_chained_outside_in(loops, false);
// If there is a possibility that brim intersects skirt, go through loops and split those extrusions
// The result is either the original Polygon or a list of Polylines

View file

@ -3378,6 +3378,8 @@ void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders)
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : print_config_def.extruder_option_keys()) {
if (key == "default_filament_profile")
// Don't resize this field, as it is presented to the user at the "Dependencies" page of the Printer profile and we don't want to present
// empty fields there, if not defined by the system profile.
continue;
auto *opt = this->option(key, false);
assert(opt != nullptr);

View file

@ -2004,10 +2004,9 @@ end:
layer->make_slices();
}
});
if (elephant_foot_compensation_scaled > 0.f) {
if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) {
// The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value.
// Store the uncompensated value there.
assert(! m_layers.empty());
assert(m_layers.front()->id() == 0);
m_layers.front()->lslices = std::move(lslices_1st_layer);
}

View file

@ -12,7 +12,7 @@
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
#define ENABLE_RENDER_SELECTION_CENTER 0
// Shows an imgui dialog with render related data
#define ENABLE_RENDER_STATISTICS 1
#define ENABLE_RENDER_STATISTICS 0
// Shows an imgui dialog with camera related data
#define ENABLE_CAMERA_STATISTICS 0
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)

View file

@ -11,9 +11,9 @@
#include <mutex>
#include <tbb/parallel_for.h>
#include <tbb/tbb_thread.h>
#include <tbb/task_arena.h>
#include <tbb/task_scheduler_init.h>
#include "Thread.hpp"
namespace Slic3r {
@ -195,7 +195,10 @@ void name_tbb_thread_pool_threads()
return;
initialized = true;
const size_t nthreads_hw = std::thread::hardware_concurrency();
// see GH issue #5661 PrusaSlicer hangs on Linux when run with non standard task affinity
// TBB will respect the task affinity mask on Linux and spawn less threads than std::thread::hardware_concurrency().
// const size_t nthreads_hw = std::thread::hardware_concurrency();
const size_t nthreads_hw = tbb::this_task_arena::max_concurrency();
size_t nthreads = nthreads_hw;
#ifdef SLIC3R_PROFILE

View file

@ -45,9 +45,11 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
m_cursor = Cursor(hit, source, radius, cursor_type, trafo);
// In case user changed cursor size since last time, update triangle edge limit.
if (m_old_cursor_radius != radius) {
set_edge_limit(radius / 5.f);
m_old_cursor_radius = radius;
// It is necessary to compare the internal radius in m_cursor! radius is in
// world coords and does not change after scaling.
if (m_old_cursor_radius_sqr != m_cursor.radius_sqr) {
set_edge_limit(std::sqrt(m_cursor.radius_sqr) / 5.f);
m_old_cursor_radius_sqr = m_cursor.radius_sqr;
}
// Now start with the facet the pointer points to and check all adjacent facets.

View file

@ -146,7 +146,7 @@ protected:
};
Cursor m_cursor;
float m_old_cursor_radius;
float m_old_cursor_radius_sqr;
// Private functions:
bool select_triangle(int facet_idx, EnforcerBlockerType type,

View file

@ -90,6 +90,7 @@ extern CopyFileResult check_copy(const std::string& origin, const std::string& c
extern bool is_plain_file(const boost::filesystem::directory_entry &path);
extern bool is_ini_file(const boost::filesystem::directory_entry &path);
extern bool is_idx_file(const boost::filesystem::directory_entry &path);
extern bool is_gcode_file(const std::string &path);
// File path / name / extension splitting utilities, working with UTF-8,
// to be published to Perl.
@ -353,8 +354,12 @@ inline std::string get_time_dhm(float time_in_secs)
#if WIN32
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE)
//FIXME this is an inprecise hack. Add the hash table size and possibly some estimate of the linked list at each of the used bin.
#define SLIC3R_STDUNORDEREDSET_MEMSIZE(NAME, TYPE) NAME.size() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE)
#else
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE)
//FIXME this is an inprecise hack. Add the hash table size and possibly some estimate of the linked list at each of the used bin.
#define SLIC3R_STDUNORDEREDSET_MEMSIZE(NAME, TYPE) NAME.size() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE)
#endif
#endif // slic3r_Utils_hpp_

View file

@ -39,9 +39,9 @@
#include <tbb/task_scheduler_init.h>
#if defined(__linux) || defined(__GNUC__ )
#if defined(__linux__) || defined(__GNUC__ )
#include <strings.h>
#endif /* __linux */
#endif /* __linux__ */
#ifdef _MSC_VER
#define strcasecmp _stricmp
@ -522,6 +522,12 @@ bool is_idx_file(const boost::filesystem::directory_entry &dir_entry)
return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".idx") == 0;
}
bool is_gcode_file(const std::string &path)
{
return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") ||
boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc");
}
} // namespace Slic3r
#ifdef WIN32

View file

@ -255,3 +255,12 @@ endif ()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
endif ()
# We need to implement some hacks for wxWidgets and touch the underlying GTK
# layer and sub-libraries. This forces us to use the include locations and
# link these libraries.
if (UNIX AND NOT APPLE)
find_package(GTK${SLIC3R_GTK} REQUIRED)
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES})
endif ()

View file

@ -611,7 +611,7 @@ struct _3DScene
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
};
static constexpr float BedEpsilon = float(EPSILON);
static constexpr float BedEpsilon = 3.f * float(EPSILON);
}

View file

@ -147,9 +147,9 @@ void GCodeViewer::TBuffer::reset()
}
// release cpu memory
indices = std::vector<IBuffer>();
paths = std::vector<Path>();
render_paths = std::vector<RenderPath>();
indices.clear();
paths.clear();
render_paths.clear();
}
void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id)
@ -781,9 +781,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
unsigned int start_vertex_offset = buffer.start_segment_vertex_offset();
unsigned int end_vertex_offset = buffer.end_segment_vertex_offset();
for (size_t i = 0; i < buffer.render_paths.size(); ++i) {
size_t i = 0;
for (const RenderPath& render_path : buffer.render_paths) {
// get paths segments from buffer paths
const RenderPath& render_path = buffer.render_paths[i];
const IndexBuffer& ibuffer = indices[render_path.index_buffer_id];
const Path& path = buffer.paths[render_path.path_id];
float half_width = 0.5f * path.width;
@ -948,6 +948,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
for (const Triangle& t : out_triangles) {
fprintf(fp, "f %zu//%zu %zu//%zu %zu//%zu\n", t[0], t[0], t[1], t[1], t[2], t[2]);
}
++ i;
}
fclose(fp);
@ -1900,6 +1902,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
}
// second pass: filter paths by sequential data and collect them by color
RenderPath *render_path = nullptr;
for (const auto& [buffer, index_buffer_id, path_id] : paths) {
const Path& path = buffer->paths[path_id];
if (m_sequential_view.current.last <= path.first.s_id || path.last.s_id <= m_sequential_view.current.first)
@ -1930,16 +1933,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
default: { color = { 0.0f, 0.0f, 0.0f }; break; }
}
unsigned int ibuffer_id = index_buffer_id;
auto it = std::find_if(buffer->render_paths.begin(), buffer->render_paths.end(),
[color, ibuffer_id](const RenderPath& path) { return path.index_buffer_id == ibuffer_id && path.color == color; });
if (it == buffer->render_paths.end()) {
it = buffer->render_paths.insert(buffer->render_paths.end(), RenderPath());
it->color = color;
it->path_id = path_id;
it->index_buffer_id = index_buffer_id;
}
RenderPath key{ color, static_cast<unsigned int>(index_buffer_id), path_id };
if (render_path == nullptr || ! RenderPathPropertyEqual()(*render_path, key))
render_path = const_cast<RenderPath*>(&(*buffer->render_paths.emplace(key).first));
unsigned int segments_count = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1;
unsigned int size_in_indices = 0;
switch (buffer->render_primitive_type)
@ -1948,7 +1944,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
case TBuffer::ERenderPrimitiveType::Line:
case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (segments_count - 1); break; }
}
it->sizes.push_back(size_in_indices);
render_path->sizes.push_back(size_in_indices);
unsigned int delta_1st = 0;
if (path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= path.last.s_id)
@ -1957,7 +1953,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle)
delta_1st *= buffer->indices_per_segment();
it->offsets.push_back(static_cast<size_t>((path.first.i_id + delta_1st) * sizeof(unsigned int)));
render_path->offsets.push_back(static_cast<size_t>((path.first.i_id + delta_1st) * sizeof(unsigned int)));
}
// set sequential data to their final value
@ -2943,7 +2939,7 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional)
int64_t render_paths_size = 0;
for (const TBuffer& buffer : m_buffers) {
paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path);
render_paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.render_paths, RenderPath);
render_paths_size += SLIC3R_STDUNORDEREDSET_MEMSIZE(buffer.render_paths, RenderPath);
for (const RenderPath& path : buffer.render_paths) {
render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int);
render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t);

View file

@ -7,6 +7,8 @@
#include <cstdint>
#include <float.h>
#include <set>
#include <unordered_set>
namespace Slic3r {
@ -146,11 +148,35 @@ class GCodeViewer
// Used to batch the indices needed to render paths
struct RenderPath
{
Color color;
unsigned int path_id;
unsigned int index_buffer_id;
std::vector<unsigned int> sizes;
std::vector<size_t> offsets; // use size_t because we need an unsigned int whose size matches pointer's size (used in the call glMultiDrawElements())
// Render path property
Color color;
unsigned int index_buffer_id;
// Render path content
unsigned int path_id;
std::vector<unsigned int> sizes;
std::vector<size_t> offsets; // use size_t because we need an unsigned int whose size matches pointer's size (used in the call glMultiDrawElements())
};
struct RenderPathPropertyHash {
size_t operator() (const RenderPath &p) const {
// Conver the RGB value to an integer hash.
// return (size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) * 7919) ^ size_t(p.index_buffer_id);
return size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) ^ size_t(p.index_buffer_id);
}
};
struct RenderPathPropertyLower {
bool operator() (const RenderPath &l, const RenderPath &r) const {
for (int i = 0; i < 3; ++ i)
if (l.color[i] < r.color[i])
return true;
else if (l.color[i] > r.color[i])
return false;
return l.index_buffer_id < r.index_buffer_id;
}
};
struct RenderPathPropertyEqual {
bool operator() (const RenderPath &l, const RenderPath &r) const {
return l.color == r.color && l.index_buffer_id == r.index_buffer_id;
}
};
// buffer containing data for rendering a specific toolpath type
@ -169,7 +195,9 @@ class GCodeViewer
std::string shader;
std::vector<Path> paths;
std::vector<RenderPath> render_paths;
// std::set seems to perform singificantly better, at least on Windows.
// std::unordered_set<RenderPath, RenderPathPropertyHash, RenderPathPropertyEqual> render_paths;
std::set<RenderPath, RenderPathPropertyLower> render_paths;
bool visible{ false };
void reset();

View file

@ -1331,7 +1331,7 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
{
for (GLVolume* vol : m_volumes.volumes) {
if (vol->composite_id.object_id == 1000) { // wipe tower
vol->is_active = (visible && mo == nullptr);
vol->is_active = (visible && mo == nullptr);
}
else {
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
@ -2996,6 +2996,7 @@ void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
}
//render();
m_dirty = true;
wxWakeUpIdle();
}
void GLCanvas3D::request_extra_frame_delayed(int miliseconds)
@ -4151,9 +4152,13 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
shader->start_using();
shader->set_uniform("print_box.volume_detection", 0);
for (const GLVolume* vol : visible_volumes) {
for (GLVolume* vol : visible_volumes) {
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
// the volume may have been deactivated by an active gizmo
bool is_active = vol->is_active;
vol->is_active = true;
vol->render();
vol->is_active = is_active;
}
shader->stop_using();

View file

@ -652,7 +652,6 @@ public:
void set_toolpath_view_type(GCodeViewer::EViewType type);
void set_volumes_z_range(const std::array<double, 2>& range);
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
void set_toolpaths_range(double low, double high);
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(const Model& model, int obj_idx);

View file

@ -131,7 +131,7 @@ public:
memDC.SetFont(m_action_font);
memDC.SetTextForeground(wxColour(237, 107, 33));
memDC.DrawText(text, int(m_scale * 60), int(m_scale * 275));
memDC.DrawText(text, int(m_scale * 60), m_action_line_y_position);
memDC.SelectObject(wxNullBitmap);
set_bitmap(bitmap);
@ -206,14 +206,22 @@ public:
memDc.SetFont(m_constant_text.version_font);
memDc.DrawLabel(m_constant_text.version, banner_rect, wxALIGN_TOP | wxALIGN_LEFT);
int version_height = memDc.GetTextExtent(m_constant_text.version).GetY();
memDc.SetFont(m_constant_text.credits_font);
memDc.DrawLabel(m_constant_text.credits, banner_rect, wxALIGN_BOTTOM | wxALIGN_LEFT);
int credits_height = memDc.GetMultiLineTextExtent(m_constant_text.credits).GetY();
int text_height = memDc.GetTextExtent("text").GetY();
// calculate position for the dynamic text
int logo_and_header_height = margin + logo_size + title_height + version_height;
m_action_line_y_position = logo_and_header_height + 0.5 * (bmp.GetHeight() - margin - credits_height - logo_and_header_height - text_height);
}
private:
wxBitmap m_main_bitmap;
wxFont m_action_font;
int m_action_line_y_position;
float m_scale {1.0};
struct ConstantText
@ -258,7 +266,8 @@ private:
float title_font_scale = (float)text_banner_width / GetTextExtent(m_constant_text.title).GetX();
scale_font(m_constant_text.title_font, title_font_scale > 3.5f ? 3.5f : title_font_scale);
scale_font(m_constant_text.version_font, 2.f);
float version_font_scale = (float)text_banner_width / GetTextExtent(m_constant_text.version).GetX();
scale_font(m_constant_text.version_font, version_font_scale > 2.f ? 2.f : version_font_scale);
// The width of the credits information string doesn't respect to the banner width some times.
// So, scale credits_font in the respect to the longest string width
@ -753,7 +762,7 @@ bool GUI_App::on_init_inner()
#ifdef __linux__
if (! check_old_linux_datadir(GetAppName())) {
std::cerr << "Quitting, user chose to move his data to new location." << std::endl;
std::cerr << "Quitting, user chose to move their data to new location." << std::endl;
return false;
}
#endif
@ -1879,11 +1888,9 @@ bool GUI_App::OnExceptionInMainLoop()
void GUI_App::OSXStoreOpenFiles(const wxArrayString &fileNames)
{
size_t num_gcodes = 0;
for (const wxString &filename : fileNames) {
wxString fn = filename.Upper();
if (fn.EndsWith(".G") || fn.EndsWith(".GCODE"))
for (const wxString &filename : fileNames)
if (is_gcode_file(into_u8(filename)))
++ num_gcodes;
}
if (fileNames.size() == num_gcodes) {
// Opening PrusaSlicer by drag & dropping a G-Code onto PrusaSlicer icon in Finder,
// just G-codes were passed. Switch to G-code viewer mode.
@ -1903,8 +1910,7 @@ void GUI_App::MacOpenFiles(const wxArrayString &fileNames)
std::vector<wxString> gcode_files;
std::vector<wxString> non_gcode_files;
for (const auto& filename : fileNames) {
wxString fn = filename.Upper();
if (fn.EndsWith(".G") || fn.EndsWith(".GCODE"))
if (is_gcode_file(into_u8(filename)))
gcode_files.emplace_back(filename);
else {
files.emplace_back(into_u8(filename));

View file

@ -16,11 +16,26 @@
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/convert.hpp>
#if __APPLE__
#include <signal.h>
#endif // __APPLE__
namespace Slic3r {
namespace GUI {
int GUI_Run(GUI_InitParams &params)
{
#if __APPLE__
// On OSX, we use boost::process::spawn() to launch new instances of PrusaSlicer from another PrusaSlicer.
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
// (posix waitpid() call will always fail).
// https://jmmv.dev/2008/10/boostprocess-and-sigchld.html
// The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work.
// See GH issue #5507
signal(SIGCHLD, SIG_DFL);
#endif // __APPLE__
try {
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {

View file

@ -1211,7 +1211,7 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
**/
m_prevent_list_events = true;//it's needed for GTK
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value,
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid vaSome textlue,
* so set some nonempty string
*/
wxTextDataObject* obj = new wxTextDataObject;
@ -1243,8 +1243,10 @@ void ObjectList::OnDropPossible(wxDataViewEvent &event)
{
const wxDataViewItem& item = event.GetItem();
if (!can_drop(item))
if (!can_drop(item)) {
event.Veto();
m_prevent_list_events = false;
}
}
void ObjectList::OnDrop(wxDataViewEvent &event)
@ -1269,7 +1271,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
// It looks like a fixed in current version of the wxWidgets
// #ifdef __WXGTK__
// /* Under GTK, DnD moves an item between another two items.
// * And event.GetItem() return item, which is under "insertion line"
// * And event.GetItem() return item, which is under "insertion line"Some text
// * So, if we move item down we should to decrease the to_volume_id value
// **/
// if (to_volume_id > from_volume_id) to_volume_id--;
@ -2407,6 +2409,8 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
if (is_layer_settings)
m_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
changed_object();
}
void ObjectList::del_instances_from_object(const int obj_idx)

View file

@ -373,7 +373,7 @@ void Preview::reload_print(bool keep_volumes)
m_volumes_cleanup_required = !keep_volumes;
return;
}
#endif /* __linux __ */
#endif /* __linux__ */
if (
#ifdef __linux__
m_volumes_cleanup_required ||

View file

@ -252,14 +252,31 @@ namespace instance_check_internal
bool instance_check(int argc, char** argv, bool app_config_single_instance)
{
#ifndef _WIN32
boost::system::error_code ec;
#endif
std::size_t hashed_path =
std::size_t hashed_path;
#ifdef _WIN32
std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
#else
std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]), ec).string());
boost::system::error_code ec;
#ifdef __linux__
// If executed by an AppImage, start the AppImage, not the main process.
// see https://docs.appimage.org/packaging-guide/environment-variables.html#id2
const char *appimage_env = std::getenv("APPIMAGE");
bool appimage_env_valid = false;
if (appimage_env) {
try {
auto appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env));
if (boost::filesystem::exists(appimage_path)) {
hashed_path = std::hash<std::string>{}(appimage_path.string());
appimage_env_valid = true;
}
} catch (std::exception &) {
}
if (! appimage_env_valid)
BOOST_LOG_TRIVIAL(error) << "APPIMAGE environment variable was set, but it does not point to a valid file: " << appimage_env;
}
if (! appimage_env_valid)
#endif // __linux__
hashed_path = std::hash<std::string>{}(boost::filesystem::canonical(boost::filesystem::system_complete(argv[0]), ec).string());
if (ec.value() > 0) { // canonical was not able to find the executable (can happen with appimage on some systems. Does it fail on Fuse file systems?)
ec.clear();
// Compose path with boost canonical of folder and filename
@ -269,7 +286,7 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
hashed_path = std::hash<std::string>{}(boost::filesystem::system_complete(argv[0]).string());
}
}
#endif // win32
#endif // _WIN32
std::string lock_name = std::to_string(hashed_path);
GUI::wxGetApp().set_instance_hash(hashed_path);

View file

@ -73,9 +73,14 @@ void FillBedJob::prepare()
// This is the maximum number of items, the real number will always be close but less.
int needed_items = (bed_area - fixed_area) / poly_area;
ModelInstance *mi = model_object->instances[0];
int sel_id = m_plater->get_selection().get_instance_idx();
// if the selection is not a single instance, choose the first as template
sel_id = std::max(sel_id, 0);
ModelInstance *mi = model_object->instances[sel_id];
ArrangePolygon template_ap = get_arrange_poly(PtrWrapper{mi}, m_plater);
for (int i = 0; i < needed_items; ++i) {
ArrangePolygon ap;
ArrangePolygon ap = template_ap;
ap.poly = m_selected.front().poly;
ap.bed_idx = arrangement::UNARRANGED;
ap.setter = [this, mi](const ArrangePolygon &p) {

View file

@ -77,7 +77,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_
{
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), monospaced_font ? 30 * wxGetApp().em_unit() : -1));
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxFont monospace = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
wxFont monospace = wxGetApp().code_font();
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());

View file

@ -1233,11 +1233,11 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
if (this->activate_existing(notification.get())) {
m_pop_notifications.back()->update(notification->get_data());
canvas.request_extra_frame();
canvas.request_extra_frame_delayed(33);
return false;
} else {
m_pop_notifications.emplace_back(std::move(notification));
canvas.request_extra_frame();
canvas.request_extra_frame_delayed(33);
return true;
}
}
@ -1387,15 +1387,19 @@ void NotificationManager::update_notifications()
if (!top_level_wnd->IsActive())
return;
static size_t last_size = m_pop_notifications.size();
//static size_t last_size = m_pop_notifications.size();
//request frames
int64_t next_render = std::numeric_limits<int64_t>::max();
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
std::unique_ptr<PopNotification>& notification = *it;
notification->set_paused(m_hovered);
notification->update_state();
next_render = std::min<int64_t>(next_render, notification->next_render());
if (notification->get_state() == PopNotification::EState::Finished)
it = m_pop_notifications.erase(it);
else {
notification->set_paused(m_hovered);
notification->update_state();
++it;
}
}
@ -1436,16 +1440,11 @@ void NotificationManager::update_notifications()
if (m_requires_render)
m_requires_update = true;
*/
//request frames
int64_t next_render = std::numeric_limits<int64_t>::max();
const int64_t max = std::numeric_limits<int64_t>::max();
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
next_render = std::min<int64_t>(next_render, notification->next_render());
}
if (next_render == 0)
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
else if (next_render < max)
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render
else if (next_render < std::numeric_limits<int64_t>::max())
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
/*

View file

@ -544,7 +544,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
return;
}
if (printer_name == m_default_name) {
warning_catcher(this, _L("You should to change a name of your printer device. It can't be saved."));
warning_catcher(this, _L("You should change the name of your printer device."));
return;
}

View file

@ -3566,6 +3566,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
*/
wxGetApp().obj_list()->update_object_list_by_printer_technology();
}
#ifdef __WXMSW__
// From the Win 2004 preset combobox lose a focus after change the preset selection
// and that is why the up/down arrow doesn't work properly
// (see https://github.com/prusa3d/PrusaSlicer/issues/5531 ).
// So, set the focus to the combobox explicitly
combo->SetFocus();
#endif
}
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
@ -4847,9 +4855,7 @@ void Plater::load_gcode()
void Plater::load_gcode(const wxString& filename)
{
if (filename.empty() ||
(!filename.Lower().EndsWith(".gcode") && !filename.Lower().EndsWith(".g")) ||
m_last_loaded_gcode == filename)
if (! is_gcode_file(into_u8(filename)) || m_last_loaded_gcode == filename)
return;
m_last_loaded_gcode = filename;

View file

@ -32,6 +32,14 @@
#include "PhysicalPrinterDialog.hpp"
#include "SavePresetDialog.hpp"
// A workaround for a set of issues related to text fitting into gtk widgets:
// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584
#if defined(__WXGTK20__) || defined(__WXGTK3__)
#include <glib-2.0/glib-object.h>
#include <pango-1.0/pango/pango-layout.h>
#include <gtk/gtk.h>
#endif
using Slic3r::GUI::format_wxstr;
namespace Slic3r {
@ -179,6 +187,25 @@ void PresetComboBox::update_selection()
SetSelection(m_last_selected);
SetToolTip(GetString(m_last_selected));
// A workaround for a set of issues related to text fitting into gtk widgets:
// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584
#if defined(__WXGTK20__) || defined(__WXGTK3__)
GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_widget));
// 'cells' contains the GtkCellRendererPixBuf for the icon,
// 'cells->next' contains GtkCellRendererText for the text we need to ellipsize
if (!cells || !cells->next) return;
auto cell = static_cast<GtkCellRendererText *>(cells->next->data);
if (!cell) return;
g_object_set(G_OBJECT(cell), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
// Only the list of cells must be freed, the renderer isn't ours to free
g_list_free(cells);
#endif
}
void PresetComboBox::update(std::string select_preset_name)
@ -860,9 +887,13 @@ void PlaterPresetComboBox::update()
if (!tooltip.IsEmpty())
SetToolTip(tooltip);
#ifdef __WXMSW__
// Use this part of code just on Windows to avoid of some layout issues on Linux
// see https://github.com/prusa3d/PrusaSlicer/issues/5163 and https://github.com/prusa3d/PrusaSlicer/issues/5505
// Update control min size after rescale (changed Display DPI under MSW)
if (GetMinWidth() != 20 * m_em_unit)
SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight()));
#endif //__WXMSW__
}
void PlaterPresetComboBox::msw_rescale()
@ -905,6 +936,13 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type)
}
evt.StopPropagation();
#ifdef __WXMSW__
// From the Win 2004 preset combobox lose a focus after change the preset selection
// and that is why the up/down arrow doesn't work properly
// (see https://github.com/prusa3d/PrusaSlicer/issues/5531 ).
// So, set the focus to the combobox explicitly
this->SetFocus();
#endif
});
}

View file

@ -80,6 +80,17 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
Fit();
CenterOnParent();
#ifdef __linux__
// On Linux with GTK2 when text control lose the focus then selection (colored background) disappears but text color stay white
// and as a result the text is invisible with light mode
// see https://github.com/prusa3d/PrusaSlicer/issues/4532
// Workaround: Unselect text selection explicitly on kill focus
txt_filename->Bind(wxEVT_KILL_FOCUS, [this](wxEvent& e) {
e.Skip();
txt_filename->SetInsertionPoint(txt_filename->GetLastPosition());
}, txt_filename->GetId());
#endif /* __linux__ */
Bind(wxEVT_SHOW, [=](const wxShowEvent &) {
// Another similar case where the function only works with EVT_SHOW + CallAfter,
// this time on Mac.

View file

@ -261,19 +261,31 @@ void RemovableDriveManager::eject_drive()
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
this->update();
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
#if __APPLE__
// If eject is still pending on the eject thread, wait until it finishes.
//FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead
// to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally
// finishes quickly.
this->eject_thread_finish();
#endif
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
tbb::mutex::scoped_lock lock(m_drives_mutex);
auto it_drive_data = this->find_last_save_path_drive_data();
if (it_drive_data != m_current_drives.end()) {
std::string correct_path(m_last_save_path);
#ifndef __APPLE__
for (size_t i = 0; i < correct_path.size(); ++i)
if (correct_path[i]==' ') {
correct_path = correct_path.insert(i,1,'\\');
++ i;
}
DriveData drive_data;
{
tbb::mutex::scoped_lock lock(m_drives_mutex);
auto it_drive_data = this->find_last_save_path_drive_data();
if (it_drive_data == m_current_drives.end())
return;
drive_data = *it_drive_data;
}
std::string correct_path(m_last_save_path);
#if __APPLE__
// On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844.
m_eject_thread = new boost::thread([this, correct_path, drive_data]()
#endif
{
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
// there is no usable command in c++ so terminal command is used instead
// but neither triggers "succesful safe removal messege"
@ -296,31 +308,36 @@ void RemovableDriveManager::eject_drive()
// wait for command to finnish (blocks ui thread)
std::error_code ec;
child.wait(ec);
bool success = false;
if (ec) {
// The wait call can fail, as it did in https://github.com/prusa3d/PrusaSlicer/issues/5507
// It can happen even in cases where the eject is sucessful, but better report it as failed.
// We did not find a way to reliably retrieve the exit code of the process.
BOOST_LOG_TRIVIAL(error) << "boost::process::child::wait() failed during Ejection. State of Ejection is unknown. Error code: " << ec.value();
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
return;
} else {
int err = child.exit_code();
if (err) {
BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err;
} else {
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
success = true;
}
}
int err = child.exit_code();
if (err) {
BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err;
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
return;
}
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(*it_drive_data), true)));
m_current_drives.erase(it_drive_data);
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(drive_data, success)));
if (success) {
// Remove the drive_data from m_current drives, searching by value, not by pointer, as m_current_drives may get modified during
// asynchronous execution on m_eject_thread.
tbb::mutex::scoped_lock lock(m_drives_mutex);
auto it = std::find(m_current_drives.begin(), m_current_drives.end(), drive_data);
if (it != m_current_drives.end())
m_current_drives.erase(it);
}
}
#if __APPLE__
);
#endif // __APPLE__
}
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
@ -382,7 +399,11 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
void RemovableDriveManager::shutdown()
{
#if __APPLE__
this->unregister_window_osx();
// If eject is still pending on the eject thread, wait until it finishes.
//FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead
// to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally
// finishes quickly.
this->eject_thread_finish();
#endif
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
@ -493,4 +514,15 @@ std::vector<DriveData>::const_iterator RemovableDriveManager::find_last_save_pat
[this](const DriveData &data){ return data.path == m_last_save_path; });
}
#if __APPLE__
void RemovableDriveManager::eject_thread_finish()
{
if (m_eject_thread) {
m_eject_thread->join();
delete m_eject_thread;
m_eject_thread = nullptr;
}
}
#endif // __APPLE__
}} // namespace Slic3r::GUI

View file

@ -132,6 +132,8 @@ private:
void eject_device(const std::string &path);
// Opaque pointer to RemovableDriveManagerMM
void *m_impl_osx;
boost::thread *m_eject_thread { nullptr };
void eject_thread_finish();
#endif
};

View file

@ -302,6 +302,13 @@ void Tab::create_preset_tab()
// This helps to process all the cursor key events on Windows in the tree control,
// so that the cursor jumps to the last item.
m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, [this](wxTreeEvent&) {
#ifdef __linux__
// Events queue is opposite On Linux. wxEVT_SET_FOCUS invokes after wxEVT_TREE_SEL_CHANGED,
// and a result wxEVT_KILL_FOCUS doesn't invoke for the TextCtrls.
// see https://github.com/prusa3d/PrusaSlicer/issues/5720
// So, call SetFocus explicitly for this control before changing of the selection
m_treectrl->SetFocus();
#endif
if (!m_disable_tree_sel_changed_event && !m_pages.empty()) {
if (m_page_switch_running)
m_page_switch_planned = true;
@ -499,7 +506,7 @@ void Tab::update_label_colours()
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
opt.first == "compatible_prints" || opt.first == "compatible_printers" ) {
if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end())
*m_colored_Label_colors.at(opt.first) = *color;
m_colored_Label_colors.at(opt.first) = *color;
continue;
}
@ -540,7 +547,7 @@ void Tab::decorate()
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
opt.first == "compatible_prints" || opt.first == "compatible_printers")
colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : m_colored_Label_colors.at(opt.first);
colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first);
if (!colored_label_clr) {
field = get_field(opt.first);
@ -3551,8 +3558,8 @@ void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::strin
line.widget = widget;
line.label_path = path;
m_colored_Label_colors[opt_key] = &m_default_text_clr;
line.full_Label_color = m_colored_Label_colors[opt_key];
m_colored_Label_colors[opt_key] = m_default_text_clr;
line.full_Label_color = &m_colored_Label_colors[opt_key];
optgroup->append_line(line);
}

View file

@ -246,7 +246,7 @@ public:
// map of option name -> wxColour (color of the colored label, associated with option)
// Used for options which don't have corresponded field
std::map<std::string, wxColour*> m_colored_Label_colors;
std::map<std::string, wxColour> m_colored_Label_colors;
// Counter for the updating (because of an update() function can have a recursive behavior):
// 1. increase value from the very beginning of an update() function

View file

@ -78,6 +78,12 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
if (instance_type == NewSlicerInstanceType::Slicer && single_instance)
args.emplace_back("--single-instance");
boost::process::spawn(bin_path, args);
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
// (posix waitpid() call will always fail).
// https://jmmv.dev/2008/10/boostprocess-and-sigchld.html
// The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work.
// See GH issue #5507
}
catch (const std::exception& ex) {
BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what();
@ -87,7 +93,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
{
std::vector<const char*> args;
args.reserve(3);
#ifdef __linux
#ifdef __linux__
static const char* gcodeviewer_param = "--gcodeviewer";
{
// If executed by an AppImage, start the AppImage, not the main process.
@ -99,7 +105,7 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
args.emplace_back(gcodeviewer_param);
}
}
#endif // __linux
#endif // __linux__
std::string my_path;
if (args.empty()) {
// Binary path was not set to the AppImage in the Linux specific block above, call the application directly.

View file

@ -313,7 +313,7 @@ void Serial::set_baud_rate(unsigned baud_rate)
speed_t newSpeed = baud_rate;
handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
handle_errno(::tcsetattr(handle, TCSANOW, &ios));
#elif __linux
#elif __linux__
/* The following definitions are kindly borrowed from:
/usr/include/asm-generic/termbits.h