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

This commit is contained in:
enricoturri1966 2021-01-07 08:24:29 +01:00
commit 676540279d
111 changed files with 12989 additions and 9870 deletions

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

@ -233,6 +233,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coFloats: return new ConfigOptionFloatsNullable();
case coInts: return new ConfigOptionIntsNullable();
case coPercents: return new ConfigOptionPercentsNullable();
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
case coBools: return new ConfigOptionBoolsNullable();
default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label);
}
@ -247,6 +248,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coPercent: return new ConfigOptionPercent();
case coPercents: return new ConfigOptionPercents();
case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercents();
case coPoint: return new ConfigOptionPoint();
case coPoints: return new ConfigOptionPoints();
case coPoint3: return new ConfigOptionPoint3();
@ -587,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);
@ -950,6 +952,8 @@ CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercentsNullable)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercents)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercentsNullable)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3)
@ -984,6 +988,8 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOp
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercentsNullable)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::FloatOrPercent>, Slic3r::ConfigOptionFloatsOrPercents)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::FloatOrPercent>, Slic3r::ConfigOptionFloatsOrPercentsNullable)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>, Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)

View file

@ -86,6 +86,8 @@ enum ConfigOptionType {
coPercents = coPercent + coVectorType,
// a fraction or an absolute value
coFloatOrPercent = 5,
// vector of the above
coFloatsOrPercents = coFloatOrPercent + coVectorType,
// single 2d point (Point2f). Currently not used.
coPoint = 6,
// vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
@ -889,6 +891,143 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
};
struct FloatOrPercent
{
double value;
bool percent;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
{
return l.value == r.value && l.percent == r.percent;
}
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
{
return !(l == r);
}
template<bool NULLABLE>
class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
{
public:
ConfigOptionFloatsOrPercentsTempl() : ConfigOptionVector<FloatOrPercent>() {}
explicit ConfigOptionFloatsOrPercentsTempl(size_t n, FloatOrPercent value) : ConfigOptionVector<FloatOrPercent>(n, value) {}
explicit ConfigOptionFloatsOrPercentsTempl(std::initializer_list<FloatOrPercent> il) : ConfigOptionVector<FloatOrPercent>(std::move(il)) {}
explicit ConfigOptionFloatsOrPercentsTempl(const std::vector<FloatOrPercent> &vec) : ConfigOptionVector<FloatOrPercent>(vec) {}
explicit ConfigOptionFloatsOrPercentsTempl(std::vector<FloatOrPercent> &&vec) : ConfigOptionVector<FloatOrPercent>(std::move(vec)) {}
static ConfigOptionType static_type() { return coFloatsOrPercents; }
ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
}
// Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil().
static FloatOrPercent nil_value() { return { std::numeric_limits<double>::quiet_NaN(), false }; }
// A scalar is nil, or all values of a vector are nil.
bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v.value)) return false; return true; }
bool is_nil(size_t idx) const override { return std::isnan(this->values[idx].value); }
std::string serialize() const override
{
std::ostringstream ss;
for (const FloatOrPercent &v : this->values) {
if (&v != &this->values.front())
ss << ",";
serialize_single_value(ss, v);
}
return ss.str();
}
std::vector<std::string> vserialize() const override
{
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (const FloatOrPercent &v : this->values) {
std::ostringstream ss;
serialize_single_value(ss, v);
vv.push_back(ss.str());
}
return vv;
}
bool deserialize(const std::string &str, bool append = false) override
{
if (! append)
this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
boost::trim(item_str);
if (item_str == "nil") {
if (NULLABLE)
this->values.push_back(nil_value());
else
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
} else {
bool percent = item_str.find_first_of("%") != std::string::npos;
std::istringstream iss(item_str);
double value;
iss >> value;
this->values.push_back({ value, percent });
}
}
return true;
}
ConfigOptionFloatsOrPercentsTempl& operator=(const ConfigOption *opt)
{
this->set(opt);
return *this;
}
protected:
void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const {
if (std::isfinite(v.value)) {
ss << v.value;
if (v.percent)
ss << "%";
} else if (std::isnan(v.value)) {
if (NULLABLE)
ss << "nil";
else
throw Slic3r::RuntimeError("Serializing NaN");
} else
throw Slic3r::RuntimeError("Serializing invalid number");
}
static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
if (NULLABLE) {
if (v1.size() != v2.size())
return false;
for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2)
if (! ((std::isnan(it1->value) && std::isnan(it2->value)) || *it1 == *it2))
return false;
return true;
} else
// Not supporting nullable values, the default vector compare is cheaper.
return v1 == v2;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<FloatOrPercent>>(this)); }
};
using ConfigOptionFloatsOrPercents = ConfigOptionFloatsOrPercentsTempl<false>;
using ConfigOptionFloatsOrPercentsNullable = ConfigOptionFloatsOrPercentsTempl<true>;
class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
{
public:

View file

@ -297,15 +297,16 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
for (const PrintRegion *region : print_object.print()->regions()) {
const PrintRegionConfig &config = region->config();
bool nonempty = config.fill_density > 0;
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
const PrintRegionConfig &config = region->config();
bool nonempty = config.fill_density > 0;
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
double infill_extrusion_width = config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * config.infill_extrusion_width : config.infill_extrusion_width;
region_fill_data.push_back(RegionFillData({
has_adaptive_infill ? Tristate::Maybe : Tristate::No,
has_support_infill ? Tristate::Maybe : Tristate::No,
config.fill_density,
config.infill_extrusion_width != 0. ? config.infill_extrusion_width : default_infill_extrusion_width
infill_extrusion_width != 0. ? infill_extrusion_width : default_infill_extrusion_width
}));
build_octree |= has_adaptive_infill || has_support_infill;
}

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,58 +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";
const char* MODEL_TAG = "model";
const char* RESOURCES_TAG = "resources";
const char* OBJECT_TAG = "object";
const char* MESH_TAG = "mesh";
const char* VERTICES_TAG = "vertices";
const char* VERTEX_TAG = "vertex";
const char* TRIANGLES_TAG = "triangles";
const char* TRIANGLE_TAG = "triangle";
const char* COMPONENTS_TAG = "components";
const char* COMPONENT_TAG = "component";
const char* BUILD_TAG = "build";
const char* ITEM_TAG = "item";
const 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";
const char* CONFIG_TAG = "config";
const char* VOLUME_TAG = "volume";
static constexpr const char* CONFIG_TAG = "config";
static constexpr const char* VOLUME_TAG = "volume";
const char* UNIT_ATTR = "unit";
const char* NAME_ATTR = "name";
const char* TYPE_ATTR = "type";
const char* ID_ATTR = "id";
const char* X_ATTR = "x";
const char* Y_ATTR = "y";
const char* Z_ATTR = "z";
const char* V1_ATTR = "v1";
const char* V2_ATTR = "v2";
const char* V3_ATTR = "v3";
const char* OBJECTID_ATTR = "objectid";
const char* TRANSFORM_ATTR = "transform";
const char* PRINTABLE_ATTR = "printable";
const char* INSTANCESCOUNT_ATTR = "instances_count";
const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
const 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";
const char* KEY_ATTR = "key";
const char* VALUE_ATTR = "value";
const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
const 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";
const char* OBJECT_TYPE = "object";
const char* VOLUME_TYPE = "volume";
static constexpr const char* OBJECT_TYPE = "object";
static constexpr const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
const char* VOLUME_TYPE_KEY = "volume_type";
const char* MATRIX_KEY = "matrix";
const char* SOURCE_FILE_KEY = "source_file";
const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
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[] =
@ -1926,6 +1927,8 @@ namespace Slic3r {
volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_Z_KEY)
volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_IN_INCHES)
volume->source.is_converted_from_inches = metadata.value == "1";
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@ -2248,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";
@ -2749,15 +2752,20 @@ namespace Slic3r {
stream << "\"/>\n";
// stores volume's source data
if (!volume->source.input_file.empty())
{
std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\"";
if (! volume->source.input_file.empty()) {
stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
}
if (volume->source.is_converted_from_inches)
stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
}
// stores volume's config data

View file

@ -778,6 +778,9 @@ void AMFParserContext::endElement(const char * /* name */)
else if (strcmp(opt_key, "source_offset_z") == 0) {
m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str());
}
else if (strcmp(opt_key, "source_in_inches") == 0) {
m_volume->source.is_converted_from_inches = m_value[1] == "1";
}
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@ -1017,6 +1020,12 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
#endif // forward compatibility
close_zip_reader(&archive);
for (ModelObject *o : model->objects)
for (ModelVolume *v : o->volumes)
if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART))
v->source.input_file = path;
return true;
}
@ -1205,6 +1214,8 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
stream << " <metadata type=\"slic3r.source_offset_y\">" << volume->source.mesh_offset(1) << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_z\">" << volume->source.mesh_offset(2) << "</metadata>\n";
}
if (volume->source.is_converted_from_inches)
stream << " <metadata type=\"slic3r.source_in_inches\">1</metadata>\n";
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < its.indices.size(); ++i) {

View file

@ -7,6 +7,8 @@
#include <string>
#include <boost/log/trivial.hpp>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#else
@ -22,7 +24,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
// Parse the OBJ file.
ObjParser::ObjData data;
if (! ObjParser::objparse(path, data)) {
// die "Failed to parse $file\n" if !-e $path;
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path;
return false;
}
@ -70,7 +72,8 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
++ num_normals;
}
}
if (data.vertices[i].coordIdx != -1) {
// Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
// This is a quad. Produce the other triangle.
stl_facet &facet2 = stl.facet_start[i_face++];
facet2.vertex[0] = facet.vertex[0];
@ -102,7 +105,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
stl_get_size(&stl);
mesh.repair();
if (mesh.facets_count() == 0) {
// die "This OBJ file couldn't be read because it's empty.\n"
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false;
}

View file

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp>
#include "objparser.hpp"
@ -312,7 +313,7 @@ static bool obj_parseline(const char *line, ObjData &data)
break;
}
default:
printf("ObjParser: Unknown command: %c\r\n", c1);
BOOST_LOG_TRIVIAL(error) << "ObjParser: Unknown command: " << c1;
break;
}
@ -338,15 +339,22 @@ bool objparse(const char *path, ObjData &data)
char *c = buf + lastLine;
while (*c == ' ' || *c == '\t')
++ c;
//FIXME check the return value and exit on error?
// Will it break parsing of some obj files?
obj_parseline(c, data);
lastLine = i + 1;
}
lenPrev = len - lastLine;
if (lenPrev > 65536) {
BOOST_LOG_TRIVIAL(error) << "ObjParser: Excessive line length";
::fclose(pFile);
return false;
}
memmove(buf, buf + lastLine, lenPrev);
}
}
catch (std::bad_alloc&) {
printf("Out of memory\r\n");
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
}
::fclose(pFile);
@ -378,7 +386,8 @@ bool objparse(std::istream &stream, ObjData &data)
}
}
catch (std::bad_alloc&) {
printf("Out of memory\r\n");
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
return false;
}
return true;

View file

@ -413,14 +413,11 @@ namespace Slic3r {
std::string WipeTowerIntegration::prime(GCode& gcodegen)
{
assert(m_layer_idx == 0);
std::string gcode;
for (const WipeTower::ToolChangeResult& tcr : m_priming) {
if (! tcr.extrusions.empty())
gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
}
return gcode;
}
@ -717,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 ||
@ -1243,18 +1253,30 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
bbox_prime.offset(0.5f);
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
_write(file, this->retract());
_write(file, "M300 S800 P500\n");
if (bbox_prime.overlap(bbox_print)) {
// Wait for the user to remove the priming extrusions, otherwise they would
// get covered by the print.
_write(file, "M1 Remove priming towers and click button.\n");
}
else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
_write(file, "M1 S10\n");
bool overlap = bbox_prime.overlap(bbox_print);
if (print.config().gcode_flavor == gcfMarlin) {
_write(file, this->retract());
_write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
if (overlap) {
// Wait for the user to remove the priming extrusions.
_write(file, "M1 Remove priming towers and click button.\n");
} else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
_write(file, "M1 S10\n");
}
} else {
// This is not Marlin, M1 command is probably not supported.
// (See https://github.com/prusa3d/PrusaSlicer/issues/5441.)
if (overlap) {
print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
_(L("Your print is very close to the priming regions. "
"Make sure there is no collision.")));
} else {
// Just continue printing, no action necessary.
}
}
}
print.throw_if_canceled();
@ -1700,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
@ -2087,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

@ -269,10 +269,10 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
}
// Called by avoid_perimeters() and by simplify_travel_heuristics().
static size_t avoid_perimeters_inner(const GCode &gcodegen, const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
const Point &end,
std::vector<TravelPoint> &result_out)
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
const Point &end,
std::vector<TravelPoint> &result_out)
{
const Polygons &boundaries = boundary.boundaries;
const EdgeGrid::Grid &edge_grid = boundary.grid;
@ -372,14 +372,14 @@ static size_t avoid_perimeters_inner(const GCode &gcodegen, const AvoidCrossingP
}
// Called by AvoidCrossingPerimeters::travel_to()
static size_t avoid_perimeters(const GCode &gcodegen, const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
const Point &end,
Polyline &result_out)
static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
const Point &end,
Polyline &result_out)
{
// Travel line is completely or partially inside the bounding box.
std::vector<TravelPoint> path;
size_t num_intersections = avoid_perimeters_inner(gcodegen, boundary, start, end, path);
size_t num_intersections = avoid_perimeters_inner(boundary, start, end, path);
result_out = to_polyline(path);
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
@ -726,6 +726,7 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
}
// Polygon offset which ensures that if a polygon breaks up into several separate parts, the original polygon will be used in these places.
// ExPolygons are handled one by one so returned ExPolygons could intersect.
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset, double min_contour_width = scale_(0.001))
{
double search_radius = 2. * (offset + min_contour_width);
@ -781,12 +782,25 @@ static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset, dou
return ex_poly_result;
}
//#define INCLUDE_SUPPORTS_IN_BOUNDARY
// called by AvoidCrossingPerimeters::travel_to()
static ExPolygons get_boundary(const Layer &layer)
{
const float perimeter_spacing = get_perimeter_spacing(layer);
const float perimeter_offset = perimeter_spacing / 2.f;
auto const *support_layer = dynamic_cast<const SupportLayer *>(&layer);
ExPolygons boundary = union_ex(inner_offset(layer.lslices, perimeter_offset));
if(support_layer) {
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(boundary, inner_offset(support_layer->support_islands.expolygons, perimeter_offset));
#endif
auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON);
if (layer_below)
append(boundary, inner_offset(layer_below->lslices, perimeter_offset));
// After calling inner_offset it is necessary to call union_ex because of the possibility of intersection ExPolygons
boundary = union_ex(boundary);
}
// Collect all top layers that will not be crossed.
size_t polygons_count = 0;
for (const LayerRegion *layer_region : layer.regions())
@ -810,20 +824,41 @@ static ExPolygons get_boundary(const Layer &layer)
// called by AvoidCrossingPerimeters::travel_to()
static Polygons get_boundary_external(const Layer &layer)
{
const float perimeter_spacing = get_perimeter_spacing(layer);
const float perimeter_spacing = get_perimeter_spacing_external(layer);
const float perimeter_offset = perimeter_spacing / 2.f;
auto const *support_layer = dynamic_cast<const SupportLayer *>(&layer);
Polygons boundary;
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
ExPolygons supports_boundary;
#endif
// Collect all holes for all printed objects and their instances, which will be printed at the same time as passed "layer".
for (const PrintObject *object : layer.object()->print()->objects()) {
Polygons polygons_per_obj;
Polygons polygons_per_obj;
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
ExPolygons supports_per_obj;
#endif
if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l)
for (const ExPolygon &island : l->lslices) append(polygons_per_obj, island.holes);
if (support_layer) {
auto *layer_below = object->get_first_layer_bellow_printz(layer.print_z, EPSILON);
if (layer_below)
for (const ExPolygon &island : layer_below->lslices) append(polygons_per_obj, island.holes);
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(supports_per_obj, support_layer->support_islands.expolygons);
#endif
}
for (const PrintInstance &instance : object->instances()) {
size_t boundary_idx = boundary.size();
append(boundary, polygons_per_obj);
for (; boundary_idx < boundary.size(); ++boundary_idx)
boundary[boundary_idx].translate(instance.shift);
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
size_t support_idx = supports_boundary.size();
append(supports_boundary, supports_per_obj);
for (; support_idx < supports_boundary.size(); ++support_idx)
supports_boundary[support_idx].translate(instance.shift);
#endif
}
}
@ -832,7 +867,9 @@ static Polygons get_boundary_external(const Layer &layer)
// Reverse all polygons for making normals point from the polygon out.
for (Polygon &poly : boundary)
poly.reverse();
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(boundary, to_polygons(inner_offset(supports_boundary, perimeter_offset)));
#endif
return boundary;
}
@ -873,14 +910,17 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
Vec2d startf = start.cast<double>();
Vec2d endf = end .cast<double>();
if (!use_external && !gcodegen.layer()->lslices.empty() && !any_expolygon_contains(gcodegen.layer()->lslices, gcodegen.layer()->lslices_bboxes, m_grid_lslice, travel)) {
const ExPolygons &lslices = gcodegen.layer()->lslices;
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
bool is_support_layer = dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr;
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
// Initialize m_internal only when it is necessary.
if (m_internal.boundaries.empty())
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer())));
// Trim the travel line by the bounding box.
if (Geometry::liang_barsky_line_clipping(startf, endf, m_internal.bbox)) {
travel_intersection_count = avoid_perimeters(gcodegen, m_internal, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
if (!m_internal.boundaries.empty() && Geometry::liang_barsky_line_clipping(startf, endf, m_internal.bbox)) {
travel_intersection_count = avoid_perimeters(m_internal, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
result_pl.points.front() = start;
result_pl.points.back() = end;
}
@ -891,7 +931,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
// Trim the travel line by the bounding box.
if (!m_external.boundaries.empty() && Geometry::liang_barsky_line_clipping(startf, endf, m_external.bbox)) {
travel_intersection_count = avoid_perimeters(gcodegen, m_external, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
travel_intersection_count = avoid_perimeters(m_external, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
result_pl.points.front() = start;
result_pl.points.back() = end;
}
@ -903,13 +943,25 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
travel_intersection_count = 0;
}
double max_detour_length scale_(gcodegen.config().avoid_crossing_perimeters_max_detour);
if (max_detour_length > 0 && (result_pl.length() - travel.length()) > max_detour_length)
result_pl = {start, end};
const ConfigOptionFloatOrPercent &opt_max_detour = gcodegen.config().avoid_crossing_perimeters_max_detour;
bool max_detour_length_exceeded = false;
if (opt_max_detour.value > 0) {
double direct_length = travel.length();
double detour = result_pl.length() - direct_length;
double max_detour_length = opt_max_detour.percent ?
direct_length * 0.01 * opt_max_detour.value :
scale_(opt_max_detour.value);
if (detour > max_detour_length) {
result_pl = {start, end};
max_detour_length_exceeded = true;
}
}
if (use_external) {
result_pl.translate(-scaled_origin);
*could_be_wipe_disabled = false;
} else if (max_detour_length_exceeded) {
*could_be_wipe_disabled = false;
} else
*could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count);

View file

@ -540,7 +540,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
}
if (config.machine_limits_usage.value != MachineLimitsUsage::Ignore)
if (m_flavor == gcfMarlin && config.machine_limits_usage.value != MachineLimitsUsage::Ignore)
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
@ -562,6 +562,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
}
m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
m_use_volumetric_e = config.use_volumetric_e;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
}
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -620,7 +624,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
}
}
// replace missing values with default
std::string default_color = "#FF8000";
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
@ -649,75 +652,86 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
}
}
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
if (machine_max_acceleration_x != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values;
if (m_flavor == gcfMarlin) {
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
if (machine_max_acceleration_x != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values;
const ConfigOptionFloats* machine_max_acceleration_y = config.option<ConfigOptionFloats>("machine_max_acceleration_y");
if (machine_max_acceleration_y != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values;
const ConfigOptionFloats* machine_max_acceleration_y = config.option<ConfigOptionFloats>("machine_max_acceleration_y");
if (machine_max_acceleration_y != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values;
const ConfigOptionFloats* machine_max_acceleration_z = config.option<ConfigOptionFloats>("machine_max_acceleration_z");
if (machine_max_acceleration_z != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values;
const ConfigOptionFloats* machine_max_acceleration_z = config.option<ConfigOptionFloats>("machine_max_acceleration_z");
if (machine_max_acceleration_z != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values;
const ConfigOptionFloats* machine_max_acceleration_e = config.option<ConfigOptionFloats>("machine_max_acceleration_e");
if (machine_max_acceleration_e != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values;
const ConfigOptionFloats* machine_max_acceleration_e = config.option<ConfigOptionFloats>("machine_max_acceleration_e");
if (machine_max_acceleration_e != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values;
const ConfigOptionFloats* machine_max_feedrate_x = config.option<ConfigOptionFloats>("machine_max_feedrate_x");
if (machine_max_feedrate_x != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values;
const ConfigOptionFloats* machine_max_feedrate_x = config.option<ConfigOptionFloats>("machine_max_feedrate_x");
if (machine_max_feedrate_x != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values;
const ConfigOptionFloats* machine_max_feedrate_y = config.option<ConfigOptionFloats>("machine_max_feedrate_y");
if (machine_max_feedrate_y != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values;
const ConfigOptionFloats* machine_max_feedrate_y = config.option<ConfigOptionFloats>("machine_max_feedrate_y");
if (machine_max_feedrate_y != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values;
const ConfigOptionFloats* machine_max_feedrate_z = config.option<ConfigOptionFloats>("machine_max_feedrate_z");
if (machine_max_feedrate_z != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values;
const ConfigOptionFloats* machine_max_feedrate_z = config.option<ConfigOptionFloats>("machine_max_feedrate_z");
if (machine_max_feedrate_z != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values;
const ConfigOptionFloats* machine_max_feedrate_e = config.option<ConfigOptionFloats>("machine_max_feedrate_e");
if (machine_max_feedrate_e != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values;
const ConfigOptionFloats* machine_max_feedrate_e = config.option<ConfigOptionFloats>("machine_max_feedrate_e");
if (machine_max_feedrate_e != nullptr)
m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values;
const ConfigOptionFloats* machine_max_jerk_x = config.option<ConfigOptionFloats>("machine_max_jerk_x");
if (machine_max_jerk_x != nullptr)
m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values;
const ConfigOptionFloats* machine_max_jerk_x = config.option<ConfigOptionFloats>("machine_max_jerk_x");
if (machine_max_jerk_x != nullptr)
m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values;
const ConfigOptionFloats* machine_max_jerk_y = config.option<ConfigOptionFloats>("machine_max_jerk_y");
if (machine_max_jerk_y != nullptr)
m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values;
const ConfigOptionFloats* machine_max_jerk_y = config.option<ConfigOptionFloats>("machine_max_jerk_y");
if (machine_max_jerk_y != nullptr)
m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values;
const ConfigOptionFloats* machine_max_jerk_z = config.option<ConfigOptionFloats>("machine_max_jerkz");
if (machine_max_jerk_z != nullptr)
m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values;
const ConfigOptionFloats* machine_max_jerk_z = config.option<ConfigOptionFloats>("machine_max_jerkz");
if (machine_max_jerk_z != nullptr)
m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values;
const ConfigOptionFloats* machine_max_jerk_e = config.option<ConfigOptionFloats>("machine_max_jerk_e");
if (machine_max_jerk_e != nullptr)
m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values;
const ConfigOptionFloats* machine_max_jerk_e = config.option<ConfigOptionFloats>("machine_max_jerk_e");
if (machine_max_jerk_e != nullptr)
m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values;
const ConfigOptionFloats* machine_max_acceleration_extruding = config.option<ConfigOptionFloats>("machine_max_acceleration_extruding");
if (machine_max_acceleration_extruding != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values;
const ConfigOptionFloats* machine_max_acceleration_extruding = config.option<ConfigOptionFloats>("machine_max_acceleration_extruding");
if (machine_max_acceleration_extruding != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values;
const ConfigOptionFloats* machine_max_acceleration_retracting = config.option<ConfigOptionFloats>("machine_max_acceleration_retracting");
if (machine_max_acceleration_retracting != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values;
const ConfigOptionFloats* machine_max_acceleration_retracting = config.option<ConfigOptionFloats>("machine_max_acceleration_retracting");
if (machine_max_acceleration_retracting != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values;
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
if (machine_min_extruding_rate != nullptr)
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values;
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
if (machine_min_extruding_rate != nullptr)
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values;
const ConfigOptionFloats* machine_min_travel_rate = config.option<ConfigOptionFloats>("machine_min_travel_rate");
if (machine_min_travel_rate != nullptr)
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
const ConfigOptionFloats* machine_min_travel_rate = config.option<ConfigOptionFloats>("machine_min_travel_rate");
if (machine_min_travel_rate != nullptr)
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
}
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
}
if (m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1)
enable_stealth_time_estimator(true);
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
if (use_volumetric_e != nullptr)
m_use_volumetric_e = use_volumetric_e->value;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
}
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@ -772,6 +786,10 @@ void GCodeProcessor::reset()
m_result.reset();
m_result.id = ++s_result_id;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
m_use_volumetric_e = false;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset();
m_height_compare.reset();
@ -1652,8 +1670,14 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
{
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1)
{
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
#else
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) {
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
if (axis == E)
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
@ -1661,6 +1685,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
if (lineG1.has(Slic3r::Axis(axis))) {
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
if (axis == E && m_use_volumetric_e)
ret /= area_filament_cross_section;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
}
else
@ -1718,9 +1746,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
if (type == EMoveType::Extrude) {
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
#if !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
#endif // !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
@ -1772,7 +1802,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
// clamp width to avoid artifacts which may arise from wrong values of m_height
m_width = std::min(m_width, 1.0f);
m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_width_compare.update(m_width, m_extrusion_role);
@ -2113,32 +2143,29 @@ void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
{
if (!m_time_processor.machine_envelope_processing_enabled)
return;
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor);
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor);
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor);
}
}
}
void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
{
if (!m_time_processor.machine_envelope_processing_enabled)
return;
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
if (m_flavor == gcfRepetier)
return;
@ -2148,45 +2175,48 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
float factor = (m_flavor == gcfMarlin || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, i, line.y() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, i, line.y() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, i, line.z() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, i, line.z() * factor);
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, i, line.e() * factor);
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, i, line.e() * factor);
}
}
}
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
{
if (!m_time_processor.machine_envelope_processing_enabled)
return;
float value;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (line.has_value('S', value)) {
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
// and it is also generated by Slic3r to control acceleration per extrusion type
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_value('S', value)) {
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
// and it is also generated by Slic3r to control acceleration per extrusion type
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
if (line.has_value('R', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
if (line.has_value('T', value)) {
// Interpret the T value as the travel acceleration in the new Marlin format.
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
// set_travel_acceleration(value);
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
if (line.has_value('R', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
if (line.has_value('T', value)) {
// Interpret the T value as the travel acceleration in the new Marlin format.
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
// set_travel_acceleration(value);
}
}
}
}
@ -2194,31 +2224,31 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
{
if (!m_time_processor.machine_envelope_processing_enabled)
return;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (line.has_x()) {
float max_jerk = line.x();
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk);
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, max_jerk);
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) {
float max_jerk = line.x();
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk);
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, max_jerk);
}
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y());
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z());
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e());
float value;
if (line.has_value('S', value))
set_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, i, value);
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value);
}
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y());
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z());
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e());
float value;
if (line.has_value('S', value))
set_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, i, value);
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value);
}
}
@ -2315,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

@ -242,7 +242,7 @@ namespace Slic3r {
bool extruder_unloaded;
// whether or not to export post-process the gcode to export lines M73 in it
bool export_remaining_time_enabled;
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope()
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() for non-Normal time estimate mode
bool machine_envelope_processing_enabled;
MachineEnvelopeConfig machine_limits;
// Additional load / unload times for a filament exchange sequence.
@ -421,6 +421,9 @@ namespace Slic3r {
unsigned int m_g1_line_id;
unsigned int m_layer_id;
CpColor m_cp_color;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
bool m_use_volumetric_e;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
enum class EProducer
{

View file

@ -504,7 +504,7 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size
bool* saw_custom) const
{
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon.parameter_by_length();
const std::vector<float> lengths = polygon.parameter_by_length();
// Which of the points are inside enforcers/blockers?
std::vector<size_t> enforcers_idxs;
@ -516,23 +516,73 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size
if (saw_custom)
*saw_custom = has_enforcers || has_blockers;
// FIXME FIXME FIXME: This is just to test the outcome and whether it is
// reasonable. The algorithm should really sum the length of all available
// pieces, get a random length and find the respective point.
float rand_len = 0.f;
size_t pt_idx = 0;
do {
rand_len = lengths.back() * (rand()/float(RAND_MAX));
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
pt_idx = it == lengths.end() ? 0 : (it-lengths.begin()-1);
assert(std::is_sorted(enforcers_idxs.begin(), enforcers_idxs.end()));
assert(std::is_sorted(blockers_idxs.begin(), blockers_idxs.end()));
std::vector<float> edges;
// If there are blockers and the point is inside, repeat.
// If there are enforcers and the point is NOT inside, repeat.
} while ((has_blockers && std::binary_search(blockers_idxs.begin(), blockers_idxs.end(), pt_idx))
|| (has_enforcers && ! std::binary_search(enforcers_idxs.begin(), enforcers_idxs.end(), pt_idx)));
// Lambda to calculate lengths of all edges of interest. Last parameter
// decides whether to measure edges inside or outside idxs.
// Negative number = not an edge of interest.
auto get_valid_length = [&lengths](const std::vector<size_t>& idxs,
std::vector<float>& edges,
bool measure_inside_edges) -> float
{
// First mark edges we are interested in by assigning a positive number.
edges.assign(lengths.size()-1, measure_inside_edges ? -1.f : 1.f);
for (size_t i=0; i<idxs.size(); ++i) {
size_t this_pt_idx = idxs[i];
// Two concurrent indices in the list -> the edge between them is the enforcer/blocker.
bool inside_edge = ((i != idxs.size()-1 && idxs[i+1] == this_pt_idx + 1)
|| (i == idxs.size()-1 && idxs.back() == lengths.size()-2 && idxs[0] == 0));
if (inside_edge)
edges[this_pt_idx] = measure_inside_edges ? 1.f : -1.f;
}
// Now measure them.
float running_total = 0.f;
for (size_t i=0; i<edges.size(); ++i) {
if (edges[i] > 0.f) {
edges[i] = lengths[i+1] - lengths[i];
running_total += edges[i];
}
}
return running_total;
};
// Find all seam candidate edges and their lengths.
float valid_length = 0.f;
if (has_enforcers)
valid_length = get_valid_length(enforcers_idxs, edges, true);
if (! has_enforcers || valid_length == 0.f) {
// Second condition covers case with isolated enf points. Given how the painted
// triangles are projected, this should not happen. Stay on the safe side though.
if (has_blockers)
valid_length = get_valid_length(blockers_idxs, edges, false);
if (valid_length == 0.f) // No blockers or everything blocked - use the whole polygon.
valid_length = lengths.back();
}
assert(valid_length != 0.f);
// Now generate a random length and find the respective edge.
float rand_len = valid_length * (rand()/float(RAND_MAX));
size_t pt_idx = 0; // Index of the edge where to put the seam.
if (valid_length == lengths.back()) {
// Whole polygon is used for placing the seam.
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
pt_idx = it == lengths.begin() ? 0 : (it-lengths.begin()-1); // this takes care of a corner case where rand() returns 0
} else {
float running = 0.f;
for (size_t i=0; i<edges.size(); ++i) {
running += edges[i] > 0.f ? edges[i] : 0.f;
if (running >= rand_len) {
pt_idx = i;
break;
}
}
}
if (! has_enforcers && ! has_blockers) {
// The polygon may be too coarse, calculate the point exactly.
assert(valid_length == lengths.back());
bool last_seg = pt_idx == polygon.points.size()-1;
size_t next_idx = last_seg ? 0 : pt_idx+1;
const Point& prev = polygon.points[pt_idx];

View file

@ -461,11 +461,11 @@ bool Model::looks_like_imperial_units() const
return false;
}
void Model::convert_from_imperial_units()
void Model::convert_from_imperial_units(bool only_small_volumes)
{
double in_to_mm = 25.4;
for (ModelObject* obj : this->objects)
if (obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
if (! only_small_volumes || obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
for (ModelVolume* v : obj->volumes)
v->source.is_converted_from_inches = true;
@ -1042,8 +1042,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
int vol_idx = 0;
for (ModelVolume* volume : volumes)
{
volume->supported_facets.clear();
volume->seam_facets.clear();
if (!volume->mesh().empty()) {
TriangleMesh mesh(volume->mesh());
mesh.require_shared_vertices();
@ -1060,9 +1058,14 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
vol->source.object_idx = (int)new_objects.size();
vol->source.volume_idx = vol_idx;
// Perform conversion
if (volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end()) {
vol->supported_facets.assign(volume->supported_facets);
vol->seam_facets.assign(volume->seam_facets);
// Perform conversion only if the target "imperial" state is different from the current one.
// This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
vol->scale_geometry_after_creation(versor);
vol->set_offset(versor.cwiseProduct(volume->get_offset()));
vol->source.is_converted_from_inches = from_imperial;

View file

@ -1018,7 +1018,7 @@ public:
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
bool looks_like_imperial_units() const;
void convert_from_imperial_units();
void convert_from_imperial_units(bool only_small_volumes);
// Ensures that the min z of the model is not negative
void adjust_min_z();

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;
}
}
}
}
@ -1849,6 +1853,13 @@ void PhysicalPrinterCollection::select_printer(const std::string& full_name)
m_selected_preset = it->get_preset_name(full_name);
}
void PhysicalPrinterCollection::select_printer(const std::string& printer_name, const std::string& preset_name)
{
if (preset_name.empty())
return select_printer(printer_name);
return select_printer(printer_name + PhysicalPrinter::separator() + preset_name);
}
void PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer)
{
return select_printer(printer.name);

View file

@ -701,6 +701,7 @@ public:
// If full_name doesn't contain name of selected preset, then select first preset in the list for this printer
void select_printer(const std::string& full_name);
void select_printer(const PhysicalPrinter& printer);
void select_printer(const std::string& printer_name, const std::string& preset_name);
bool has_selection() const;
void unselect_printer() ;
bool is_selected(ConstIterator it, const std::string &preset_name) const;

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);
@ -876,7 +875,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// Activate the physical printer profile if possible.
PhysicalPrinter *pp = this->physical_printers.find_printer(physical_printer, true);
if (pp != nullptr && std::find(pp->preset_names.begin(), pp->preset_names.end(), this->printers.get_edited_preset().name) != pp->preset_names.end())
this->physical_printers.select_printer(*pp);
this->physical_printers.select_printer(pp->name, this->printers.get_edited_preset().name);
else
this->physical_printers.unselect_printer();
}
@ -1396,7 +1395,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
if (! active_printer.empty())
printers.select_preset_by_name(active_printer, true);
if (! active_physical_printer.empty())
physical_printers.select_printer(active_physical_printer +" * " + active_printer);
physical_printers.select_printer(active_physical_printer, active_printer);
// Activate the first filament preset.
if (! active_filaments.empty() && ! active_filaments.front().empty())
filaments.select_preset_by_name(active_filaments.front(), 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

@ -161,6 +161,8 @@ public:
// Get a layer approximately at print_z.
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon);
// Get the first layer approximately bellow print_z.
const Layer* get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const;
// print_z: top of the layer; slice_z: center of the layer.
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);

View file

@ -64,7 +64,7 @@ void PrintConfigDef::init_common_params()
def = this->add("thumbnails", coPoints);
def->label = L("G-code thumbnails");
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files");
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\"");
def->mode = comExpert;
def->gui_type = "one_string";
def->set_default_value(new ConfigOptionPoints());
@ -184,15 +184,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("avoid_crossing_perimeters_max_detour", coFloat);
def->label = L("Avoid crossing perimeters - The max detour lenght");
def = this->add("avoid_crossing_perimeters_max_detour", coFloatOrPercent);
def->label = L("Avoid crossing perimeters - Max detour length");
def->category = L("Layers and Perimeters");
def->tooltip = L("The maximum detour length for avoid crossing perimeters. "
"If the detour is longer than this value, avoid crossing perimeters is not applied for this path.");
def->sidetext = L("mm (zero to disable)");
"If the detour is longer than this value, avoid crossing perimeters is not applied for this travel path. "
"Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path.");
def->sidetext = L("mm or % (zero to disable)");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.));
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
def = this->add("bed_temperature", coInts);
def->label = L("Other layers");
@ -606,6 +607,7 @@ void PrintConfigDef::init_fff_params()
"this setting to get nice surface finish and correct single wall widths. "
"Usual values are between 0.9 and 1.1. If you think you need to change this more, "
"check filament diameter and your firmware E steps.");
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 1. });
@ -618,6 +620,7 @@ void PrintConfigDef::init_fff_params()
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
def->sidetext = L("mm or %");
def->min = 0;
def->max = 1000;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
@ -3375,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

@ -828,7 +828,7 @@ class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig
public:
ConfigOptionBool avoid_crossing_perimeters;
ConfigOptionFloat avoid_crossing_perimeters_max_detour;
ConfigOptionFloatOrPercent avoid_crossing_perimeters_max_detour;
ConfigOptionPoints bed_shape;
ConfigOptionInts bed_temperature;
ConfigOptionFloat bridge_acceleration;

View file

@ -567,12 +567,18 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "dont_support_bridges"
|| opt_key == "first_layer_extrusion_width") {
steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bottom_solid_layers") {
steps.emplace_back(posPrepareInfill);
if(m_print->config().spiral_vase) {
// Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
// Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
steps.emplace_back(posSlice);
}
} else if (
opt_key == "interface_shells"
|| opt_key == "infill_only_where_needed"
|| opt_key == "infill_every_layers"
|| opt_key == "solid_infill_every_layers"
|| opt_key == "bottom_solid_layers"
|| opt_key == "bottom_solid_min_thickness"
|| opt_key == "top_solid_layers"
|| opt_key == "top_solid_min_thickness"
@ -655,6 +661,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posPrepareInfill) {
invalidated |= this->invalidate_steps({ posInfill, posIroning });
} else if (step == posInfill) {
invalidated |= this->invalidate_steps({ posIroning });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
@ -2943,4 +2950,11 @@ const Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon
Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); }
const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const
{
coordf_t limit = print_z + epsilon;
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
return (it == m_layers.begin()) ? nullptr : *(--it);
}
} // namespace Slic3r

View file

@ -906,13 +906,15 @@ namespace SupportMaterialInternal {
polyline.extend_start(fw);
polyline.extend_end(fw);
// Is the straight perimeter segment supported at both sides?
for (size_t i = 0; i < lower_layer.lslices.size(); ++ i)
if (lower_layer.lslices_bboxes[i].contains(polyline.first_point()) && lower_layer.lslices_bboxes[i].contains(polyline.last_point()) &&
lower_layer.lslices[i].contains(polyline.first_point()) && lower_layer.lslices[i].contains(polyline.last_point())) {
// Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
break;
}
Point pts[2] = { polyline.first_point(), polyline.last_point() };
bool supported[2] = { false, false };
for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i)
for (int j = 0; j < 2; ++ j)
if (! supported[j] && lower_layer.lslices_bboxes[i].contains(pts[j]) && lower_layer.lslices[i].contains(pts[j]))
supported[j] = true;
if (supported[0] && supported[1])
// Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
}
bridges = union_(bridges);
}

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)
@ -99,4 +99,12 @@
#define ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE (1 && ENABLE_2_3_0_BETA3)
#define ENABLE_RENDER_PATH_REFRESH_AFTER_OPTIONS_CHANGE (1 && ENABLE_2_3_0_BETA3)
//=================
// 2.3.0.rc1 techs
//=================
#define ENABLE_2_3_0_RC1 1
#define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
#endif // _prusaslicer_technologies_h_

View file

@ -6,13 +6,14 @@
#include <pthread.h>
#endif
#include <atomic>
#include <condition_variable>
#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 {
@ -194,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

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