diff options
author | supermerill <merill@free.fr> | 2021-09-06 19:21:41 +0300 |
---|---|---|
committer | supermerill <merill@free.fr> | 2021-09-06 19:21:41 +0300 |
commit | 70d9ca80ff0f82928ac260a5106121316866767b (patch) | |
tree | acd4cc8266287b307218b425b22e1f1b84d3f41c | |
parent | c582f369f2066b337bc2e9adada60418da2a3748 (diff) | |
parent | 2795295ff0e13e12d1e36aeb504fe83d0899900e (diff) |
Merge branch 'merill-merge'2.3.57.0
70 files changed, 1944 insertions, 980 deletions
diff --git a/resources/icons/Slic3r-gcodeviewer.ico b/resources/icons/Slic3r-gcodeviewer.ico Binary files differnew file mode 100644 index 000000000..7183c99a6 --- /dev/null +++ b/resources/icons/Slic3r-gcodeviewer.ico diff --git a/resources/icons/Slic3r-gcodeviewer_128px.png b/resources/icons/Slic3r-gcodeviewer_128px.png Binary files differnew file mode 100644 index 000000000..da39950bf --- /dev/null +++ b/resources/icons/Slic3r-gcodeviewer_128px.png diff --git a/resources/icons/Slic3r-gcodeviewer_192px.png b/resources/icons/Slic3r-gcodeviewer_192px.png Binary files differnew file mode 100644 index 000000000..4991c74c0 --- /dev/null +++ b/resources/icons/Slic3r-gcodeviewer_192px.png diff --git a/resources/icons/Slic3r-gcodeviewer_32px.png b/resources/icons/Slic3r-gcodeviewer_32px.png Binary files differnew file mode 100644 index 000000000..730281354 --- /dev/null +++ b/resources/icons/Slic3r-gcodeviewer_32px.png diff --git a/resources/ui_layout/extruder.ui b/resources/ui_layout/extruder.ui index 458b92150..f3dee38f7 100644 --- a/resources/ui_layout/extruder.ui +++ b/resources/ui_layout/extruder.ui @@ -29,7 +29,9 @@ group:Retraction setting:idx:retract_restart_extra setting:idx:retract_before_travel setting:idx:retract_layer_change +group:Retraction wipe setting:idx:wipe + setting:idx:wipe_speed setting:idx:retract_before_wipe setting:idx:wipe_extra_perimeter group:Retraction when tool is disabled (advanced settings for multi-extruder setups) diff --git a/resources/ui_layout/filament.ui b/resources/ui_layout/filament.ui index acc8a3cbc..38ada3066 100644 --- a/resources/ui_layout/filament.ui +++ b/resources/ui_layout/filament.ui @@ -30,7 +30,10 @@ page:Cooling:time group:Fan speed - default setting:label$Run the fan at default speed when possible:fan_always_on setting:min_fan_speed - setting:bridge_fan_speed + line:Bridges fan speed + setting:label$:bridge_fan_speed + setting:label$Infill bridges:bridge_internal_fan_speed + end_line setting:top_fan_speed setting:external_perimeter_fan_speed line:Disable fan for the first diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index 981f69aad..6a7107bfc 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -106,6 +106,7 @@ group:Modifying slices line:Convert round vertical holes to polyholes setting:label$_:hole_to_polyhole setting:hole_to_polyhole_threshold + setting:hole_to_polyhole_twisted end_line group:Other setting:clip_multipart_objects @@ -257,6 +258,7 @@ group:Speed for non-print moves end_line group:sidetext_width$7:Modifiers line:First layer speed + setting:label_width$8:width$4:first_layer_min_speed setting:label_width$8:width$4:first_layer_speed setting:label_width$8:width$4:first_layer_infill_speed end_line diff --git a/resources/ui_layout/printer_fff.ui b/resources/ui_layout/printer_fff.ui index c5d4cd6d2..4cba9879d 100644 --- a/resources/ui_layout/printer_fff.ui +++ b/resources/ui_layout/printer_fff.ui @@ -19,6 +19,11 @@ group:silent_mode_event:Firmware setting:gcode_precision_xyz setting:gcode_precision_e end_line + line:Processing limit + setting:max_gcode_per_second + setting:min_length + end_line + setting:gcode_filename_illegal_char group:Cooling fan line:Speedup setting:label$Speedup time:fan_speedup_time @@ -40,7 +45,6 @@ group:Advanced setting:use_relative_e_distances setting:use_firmware_retraction setting:use_volumetric_e - setting:min_length setting:variable_layer_height page:Custom G-code:cog diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 24ba4a1b2..a02876484 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -471,8 +471,8 @@ int CLI::run(int argc, char **argv) sla_print.set_status_callback( [](const PrintBase::SlicingStatus& s) { - if(s.percent >= 0) // FIXME: is this sufficient? - printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str()); + if(s.percent >= 0 && s.args.empty()) // FIXME: is this sufficient? + printf("%3d%s %s\n", s.percent, "% =>", s.main_text.c_str()); }); PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print); diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 3824fb749..bd01b0892 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -69,6 +69,9 @@ void AppConfig::set_defaults() if (get("freecad_path").empty()) set("freecad_path", "."); + if (get("show_overwrite_dialog").empty()) + set("show_overwrite_dialog", "1"); + if (get("tab_icon_size").empty()) set("tab_icon_size", "32"); diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index 0a53a5330..c3f68abad 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -123,6 +123,8 @@ public: std::string get_last_output_dir(const std::string& alt, const bool removable = false) const; void update_last_output_dir(const std::string &dir, const bool removable = false); + bool get_show_overwrite_dialog() const { return get("show_overwrite_dialog") != "0"; } + // reset the current print / filament / printer selections, so that // the PresetBundle::load_selections(const AppConfig &config) call will select // the first non-default preset when called. diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 3800d49e3..46722d1d6 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -528,10 +528,10 @@ inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); } inline double area(const BoundingBox& box) { return double(width(box)) * height(box); } inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); } -inline double distance_to(const Point& p1, const Point& p2) +inline coordf_t distance_to(const Point& p1, const Point& p2) { - double dx = p2.x() - p1.x(); - double dy = p2.y() - p1.y(); + coordf_t dx = coordf_t(p2.x() - p1.x()); + coordf_t dy = coordf_t(p2.y() - p1.y()); return std::sqrt(dx*dx + dy*dy); } diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp index eb4e042a0..de02ac18b 100644 --- a/src/libslic3r/BoundingBox.cpp +++ b/src/libslic3r/BoundingBox.cpp @@ -161,11 +161,11 @@ BoundingBox3Base<PointClass>::size() const template Vec3f BoundingBox3Base<Vec3f>::size() const; template Vec3d BoundingBox3Base<Vec3d>::size() const; -template <class PointClass> double BoundingBoxBase<PointClass>::radius() const +template <class PointClass> coordf_t BoundingBoxBase<PointClass>::radius() const { assert(this->defined); - double x = this->max(0) - this->min(0); - double y = this->max(1) - this->min(1); + coordf_t x = coordf_t(this->max(0) - this->min(0)); + coordf_t y = coordf_t(this->max(1) - this->min(1)); return 0.5 * sqrt(x*x+y*y); } template double BoundingBoxBase<Point>::radius() const; diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 8de28af5c..5262c48d3 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -43,7 +43,7 @@ public: void merge(const BoundingBoxBase<PointClass> &bb); void scale(double factor); PointClass size() const; - double radius() const; + coordf_t radius() const; void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } void translate(const Vec2d &v) { this->min += v; this->max += v; } void offset(coordf_t delta); diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 0528c137a..45bb08599 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1230,7 +1230,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false)); //tiny holes can be reduced to giberish, get rid of them. for (auto it = holes.begin(); it != holes.end();) - if (ClipperLib::Area(*it) < CLIPPER_OFFSET_SCALE*CLIPPER_OFFSET_SCALE) { + if (ClipperLib::Area(*it) < double(CLIPPER_OFFSET_SCALE) * double(CLIPPER_OFFSET_SCALE)) { it = holes.erase(it); } else ++it; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 24f2a44af..8964d73b0 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -657,12 +657,15 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con opt->set_phony(false); else opt->set_phony(false); + + if (optdef->is_vector_extruder) + static_cast<ConfigOptionVectorBase*>(opt)->set_is_extruder_size(true); return success; } // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. -double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const +double ConfigBase::get_computed_value(const t_config_option_key &opt_key, int extruder_id) const { // Get stored option value. const ConfigOption *raw_opt = this->option(opt_key); @@ -670,52 +673,92 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const std::stringstream ss; ss << "You can't define an option that need " << opt_key << " without defining it!"; throw std::runtime_error(ss.str()); } - if (raw_opt->type() == coFloat) - return static_cast<const ConfigOptionFloat*>(raw_opt)->value; - if (raw_opt->type() == coInt) - return static_cast<const ConfigOptionInt*>(raw_opt)->value; - if (raw_opt->type() == coBool) - return static_cast<const ConfigOptionBool*>(raw_opt)->value?1:0; - const ConfigOptionDef* opt_def = nullptr; - const ConfigOptionPercent* cast_opt = nullptr; - if (raw_opt->type() == coFloatOrPercent) { - if(!static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->percent) - return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->value; - // Get option definition. - const ConfigDef *def = this->def(); - if (def == nullptr) - throw NoDefinitionException(opt_key); - opt_def = def->get(opt_key); - cast_opt = static_cast<const ConfigOptionFloatOrPercent*>(raw_opt); - assert(opt_def != nullptr); - } - if (raw_opt->type() == coPercent) { - // Get option definition. - const ConfigDef* def = this->def(); - if (def == nullptr) - throw NoDefinitionException(opt_key); - opt_def = def->get(opt_key); - assert(opt_def != nullptr); - cast_opt = static_cast<const ConfigOptionPercent*>(raw_opt); - } - if (opt_def != nullptr) { - //if over no other key, it's most probably a simple % - if (opt_def->ratio_over == "") - return cast_opt->get_abs_value(1); - if (opt_def->ratio_over == "nozzle_diameter") { - //use the first... i guess. - //TODO: find a better way, like a "current_extruder_idx" config option. - if (this->option(opt_def->ratio_over) == nullptr) { - std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " need nozzle_diameter but can't acess it. Please use get_abs_value(nozzle_diam)."; - throw std::runtime_error(ss.str()); + + if (!raw_opt->is_vector()) { + if (raw_opt->type() == coFloat) + return static_cast<const ConfigOptionFloat*>(raw_opt)->value; + if (raw_opt->type() == coInt) + return static_cast<const ConfigOptionInt*>(raw_opt)->value; + if (raw_opt->type() == coBool) + return static_cast<const ConfigOptionBool*>(raw_opt)->value ? 1 : 0; + const ConfigOptionDef* opt_def = nullptr; + const ConfigOptionPercent* cast_opt = nullptr; + if (raw_opt->type() == coFloatOrPercent) { + if (!static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->percent) + return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->value; + // Get option definition. + const ConfigDef* def = this->def(); + if (def == nullptr) + throw NoDefinitionException(opt_key); + opt_def = def->get(opt_key); + cast_opt = static_cast<const ConfigOptionFloatOrPercent*>(raw_opt); + assert(opt_def != nullptr); + } + if (raw_opt->type() == coPercent) { + // Get option definition. + const ConfigDef* def = this->def(); + if (def == nullptr) + throw NoDefinitionException(opt_key); + opt_def = def->get(opt_key); + assert(opt_def != nullptr); + cast_opt = static_cast<const ConfigOptionPercent*>(raw_opt); + } + if (opt_def != nullptr) { + //if over no other key, it's most probably a simple % + if (opt_def->ratio_over == "") + return cast_opt->get_abs_value(1); + // Compute absolute value over the absolute value of the base option. + //FIXME there are some ratio_over chains, which end with empty ratio_with. + // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return cast_opt->get_abs_value(this->get_computed_value(opt_def->ratio_over)); + + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); + } + } else { + // check if it's an extruder_id array + const ConfigOptionVectorBase* vector_opt = static_cast<const ConfigOptionVectorBase*>(raw_opt); + if (vector_opt->is_extruder_size()) { + if (extruder_id < 0) { + const ConfigOption* opt_extruder_id = nullptr; + if ((opt_extruder_id = this->option("extruder")) == nullptr) + if ((opt_extruder_id = this->option("current_extruder")) == nullptr + || opt_extruder_id->getInt() < 0 || opt_extruder_id->getInt() >= vector_opt->size()) { + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " need to has the extuder id to get the right value, but it's not available"; + throw ConfigurationError(ss.str()); + } + extruder_id = opt_extruder_id->getInt(); + } + + if (raw_opt->type() == coFloats || raw_opt->type() == coInts || raw_opt->type() == coBools) + return vector_opt->getFloat(extruder_id); + if (raw_opt->type() == coFloatsOrPercents) { + const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast<const ConfigOptionFloatsOrPercents*>(raw_opt); + if (!opt_fl_per->values[extruder_id].percent) + return opt_fl_per->values[extruder_id].value; + + const ConfigDef* def = this->def(); + if (def == nullptr) + throw NoDefinitionException(opt_key); + const ConfigOptionDef* opt_def = def->get(opt_key); + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return opt_fl_per->get_abs_value(extruder_id, this->get_computed_value(opt_def->ratio_over, extruder_id)); + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); + } + if (raw_opt->type() == coPercents) { + const ConfigOptionPercents* opt_per = static_cast<const ConfigOptionPercents*>(raw_opt); + const ConfigDef* def = this->def(); + if (def == nullptr) + throw NoDefinitionException(opt_key); + const ConfigOptionDef* opt_def = def->get(opt_key); + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return opt_per->get_abs_value(extruder_id, this->get_computed_value(opt_def->ratio_over, extruder_id)); + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); } - return cast_opt->get_abs_value(static_cast<const ConfigOptionFloats*>(this->option(opt_def->ratio_over))->values[0]); } - // Compute absolute value over the absolute value of the base option. - //FIXME there are some ratio_over chains, which end with empty ratio_with. - // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - return opt_def->ratio_over.empty() ? 0. : - cast_opt->get_abs_value(this->get_abs_value(opt_def->ratio_over)); } std::stringstream ss; ss << "ConfigBase::get_abs_value(): "<< opt_key<<" has not a valid option type for get_abs_value()"; throw ConfigurationError(ss.str()); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 736b24400..e257b4ade 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -296,16 +296,20 @@ struct ConfigSubstitutionContext ConfigSubstitutions substitutions; }; + // A generic value of a configuration option. class ConfigOption { public: // if true, this option doesn't need to be saved, it's a computed value from an other configOption. // uint32_t because macos crash if it's a bool. and it doesn't change the size of the object because of alignment. - uint32_t phony; - + uint32_t flags; + enum FlagsConfigOption : uint8_t { + FCO_PHONY = 1, + FCO_EXTRUDER_ARRAY = 1 << 1, + }; - ConfigOption() : phony(false) {} - ConfigOption(bool phony) : phony(uint32_t(phony)) {} + ConfigOption() : flags(false) {} + ConfigOption(bool phony) : flags(uint32_t(FlagsConfigOption::FCO_PHONY)) {} virtual ~ConfigOption() {} @@ -327,8 +331,8 @@ public: virtual bool nullable() const { return false; } // A scalar is nil, or all values of a vector are nil. virtual bool is_nil() const { return false; } - bool is_phony() const { return phony != 0; } - void set_phony(bool phony) { this->phony = phony ? 1 : 0; } + bool is_phony() const { return (flags & FCO_PHONY) != 0; } + void set_phony(bool phony) { if (phony) this->flags |= FCO_PHONY; else this->flags &= uint8_t(0xFF ^ FCO_PHONY); } // Is this option overridden by another option? // An option overrides another option if it is not nil and not equal. virtual bool overriden_by(const ConfigOption *rhs) const { @@ -344,7 +348,7 @@ public: } private: friend class cereal::access; - template<class Archive> void serialize(Archive& ar) { ar(this->phony); } + template<class Archive> void serialize(Archive& ar) { ar(this->flags); } }; typedef ConfigOption* ConfigOptionPtr; @@ -365,7 +369,7 @@ public: throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs)); this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value; - this->phony = rhs->phony; + this->flags = rhs->flags; } bool operator==(const ConfigOption &rhs) const override @@ -381,7 +385,7 @@ public: private: friend class cereal::access; - template<class Archive> void serialize(Archive & ar) { ar(this->phony); ar(this->value); } + template<class Archive> void serialize(Archive & ar) { ar(this->flags); ar(this->value); } }; // Value of a vector valued option (bools, ints, floats, strings, points) @@ -408,6 +412,11 @@ public: virtual bool empty() const = 0; // Is the value nil? That should only be possible if this->nullable(). virtual bool is_nil(size_t idx) const = 0; + // Get if the size of this vector is/should be the same as nozzle_diameter + bool is_extruder_size() const { return (flags & FCO_EXTRUDER_ARRAY) != 0; } + void set_is_extruder_size(bool is_extruder_size) { + if (is_extruder_size) this->flags |= FCO_EXTRUDER_ARRAY; else this->flags &= uint8_t(0xFF ^ FCO_EXTRUDER_ARRAY); } + virtual double getFloat(int idx) const { throw BadOptionTypeException("Calling ConfigOption::getFloat(idx) on a non-numeric arrray ConfigOptionVectorBase"); } // We just overloaded and hid two base class virtual methods. // Let's show it was intentional (warnings). @@ -440,7 +449,7 @@ public: throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs)); this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values; - this->phony = rhs->phony; + this->flags = rhs->flags; } // Set from a vector of ConfigOptions. @@ -597,7 +606,7 @@ public: private: friend class cereal::access; - template<class Archive> void serialize(Archive & ar) { ar(this->phony); ar(this->values); } + template<class Archive> void serialize(Archive & ar) { ar(this->flags); ar(this->values); } }; class ConfigOptionFloat : public ConfigOptionSingle<double> @@ -667,6 +676,7 @@ public: // 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)) return false; return true; } bool is_nil(size_t idx) const override { return std::isnan(this->values[idx]); } + virtual double getFloat(int idx) const override { return values[idx]; } std::string serialize() const override { @@ -813,6 +823,7 @@ public: // A scalar is nil, or all values of a vector are nil. bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); } + virtual double getFloat(int idx) const override { return values[idx]; } std::string serialize() const override { @@ -1142,6 +1153,12 @@ public: // 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); } + double get_abs_value(size_t i, double ratio_over) const { + if (this->is_nil(i)) return 0; + const FloatOrPercent& data = this->get_at(i); + if (data.percent) return ratio_over * data.value / 100; + return data.value; + } std::string serialize() const override { @@ -1436,6 +1453,7 @@ public: // A scalar is nil, or all values of a vector are nil. bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); } + virtual double getFloat(int idx) const override { return values[idx]?1:0; } bool& get_at(size_t i) { assert(! this->values.empty()); @@ -1553,7 +1571,7 @@ public: throw ConfigurationError("ConfigOptionEnum<T>: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T> this->value = (T)rhs->getInt(); - this->phony = rhs->phony; + this->flags = rhs->flags; } std::string serialize() const override @@ -1639,7 +1657,7 @@ public: throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T> this->value = rhs->getInt(); - this->phony = rhs->phony; + this->flags = rhs->flags; } std::string serialize() const override @@ -1677,7 +1695,13 @@ public: bool nullable = false; // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. Slic3r::clonable_ptr<const ConfigOption> default_value; - void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); } + void set_default_value(const ConfigOption* ptr) { + this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); + } + void set_default_value(ConfigOptionVectorBase* ptr) { + ptr->set_is_extruder_size(this->is_vector_extruder); + this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); + } template<typename T> const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); } // Create an empty option to be used as a base for deserialization of DynamicConfig. @@ -1686,34 +1710,40 @@ public: ConfigOption* create_default_option() const; template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const { - if (this->nullable) { + ConfigOption* opt; + ConfigOptionVectorBase* opt_vec = nullptr; + if (this->nullable) { switch (this->type) { - case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; } - case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } - case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } - case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; } + case coFloats: { opt = opt_vec = new ConfigOptionFloatsNullable(); break; } + case coInts: { opt = opt_vec = new ConfigOptionIntsNullable(); break; } + case coPercents: { opt = opt_vec = new ConfigOptionPercentsNullable(); break; } + case coBools: { opt = opt_vec = new ConfigOptionBoolsNullable(); break; } default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { - case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; } - case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; } - case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; } - case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; } - case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; } - case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; } - case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; } - case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; } - case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; } - case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; } - case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; } - case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; } - case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } - case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } - case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } + case coFloat: { opt = new ConfigOptionFloat(); break;} + case coFloats: { opt = opt_vec = new ConfigOptionFloats(); break; } + case coInt: { opt = new ConfigOptionInt(); break; } + case coInts: { opt = opt_vec = new ConfigOptionInts(); break; } + case coString: { opt = new ConfigOptionString(); break; } + case coStrings: { opt = opt_vec = new ConfigOptionStrings(); break; } + case coPercent: { opt = new ConfigOptionPercent(); break; } + case coPercents: { opt = opt_vec = new ConfigOptionPercents(); break; } + case coFloatOrPercent: { opt = new ConfigOptionFloatOrPercent(); break; } + case coPoint: { opt = new ConfigOptionPoint(); break; } + case coPoints: { opt = opt_vec = new ConfigOptionPoints(); break; } + case coPoint3: { opt = new ConfigOptionPoint3(); break; } + case coBool: { opt = new ConfigOptionBool(); break; } + case coBools: { opt = opt_vec = new ConfigOptionBools(); break; } + case coEnum: { opt = new ConfigOptionEnumGeneric(this->enum_keys_map); break; } default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); } } + if (opt_vec != nullptr) + opt_vec->set_is_extruder_size (this->is_vector_extruder); + archive(*opt); + return opt; } template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const { @@ -1785,7 +1815,9 @@ public: // For text input: If true, the GUI text box spans the complete page width. bool full_width = false; // For text input: If true, the GUI formats text as code (fixed-width) - bool is_code = false; + bool is_code = false; + // For array settign: If true, It has the same size as the number of extruders. + bool is_vector_extruder = false; // Not editable. Currently only used for the display of the number of threads. bool readonly = false; // Can be phony. if not present at laoding, mark it as phony. Also adapt the gui to look for phony status. @@ -2045,7 +2077,7 @@ public: void set_deserialize_strict(std::initializer_list<SetDeserializeItem> items) { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(items, ctxt); } - double get_abs_value(const t_config_option_key &opt_key) const; + double get_computed_value(const t_config_option_key &opt_key, int extruder_id = -1) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; void setenv_() const; ConfigSubstitutions load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index 2e1453736..d75acdfca 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -1213,8 +1213,8 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu // Signum of the distance field at pt. int sign_min = 0; bool on_segment = false; - for (int r = bbox.min(1); r <= bbox.max(1); ++ r) { - for (int c = bbox.min(0); c <= bbox.max(0); ++ c) { + for (coord_t r = bbox.min(1); r <= bbox.max(1); ++ r) { + for (coord_t c = bbox.min(0); c <= bbox.max(0); ++ c) { const Cell &cell = m_cells[r * m_cols + c]; for (size_t i = cell.begin; i < cell.end; ++ i) { const Slic3r::Points &pts = *m_contours[m_cell_data[i].first]; @@ -1301,7 +1301,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co std::vector<char> cell_inside2(cell_inside); for (int r = 1; r + 1 < int(cell_rows); ++ r) { for (int c = 1; c + 1 < int(cell_cols); ++ c) { - int addr = r * cell_cols + c; + int addr = r * int(cell_cols) + c; if ((cell_inside2[addr - 1] && cell_inside2[addr + 1]) || (cell_inside2[addr - cell_cols] && cell_inside2[addr + cell_cols])) cell_inside[addr] = true; @@ -1483,8 +1483,8 @@ bool EdgeGrid::Grid::has_intersecting_edges() const void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path, size_t scale) { - unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution; - unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution; + uint32_t w = uint32_t((bbox.max(0) - bbox.min(0) + resolution - 1) / resolution); + uint32_t h = uint32_t((bbox.max(1) - bbox.min(1) + resolution - 1) / resolution); std::vector<uint8_t> pixels(w * h * 3, 0); @@ -1501,7 +1501,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo #else if (grid.signed_distance(pt, search_radius, min_dist)) { #endif - float s = 255 * std::abs(min_dist) / float(display_blend_radius); + float s = float(255 * std::abs(min_dist)) / float(display_blend_radius); int is = std::max(0, std::min(255, int(floor(s + 0.5f)))); if (min_dist < 0) { if (on_segment) { @@ -1548,7 +1548,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo } } - float dgrid = fabs(min_dist) / float(grid.resolution()); + float dgrid = fabs(float(min_dist)) / float(grid.resolution()); float igrid = floor(dgrid + 0.5f); dgrid = std::abs(dgrid - igrid) * float(grid.resolution()) / float(resolution); if (dgrid < 1.f) { @@ -1559,7 +1559,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo pxl[2] = (unsigned char)(t * pxl[2]); if (igrid > 0.f) { // Other than zero iso contour. - int g = pxl[1] + 255.f * (1.f - t); + int g = int(pxl[1] + 255.f * (1.f - t)); pxl[1] = std::min(g, 255); } } @@ -1572,7 +1572,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo // Find all pairs of intersectiong edges from the set of polygons. std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons) { - double len = 0; + coordf_t len = 0; size_t cnt = 0; BoundingBox bbox; for (const Polygon &poly : polygons) { @@ -1592,7 +1592,7 @@ std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> bbox.offset(20); EdgeGrid::Grid grid; grid.set_bbox(bbox); - grid.create(polygons, len); + grid.create(polygons, coord_t(len)); out = grid.intersecting_edges(); } return out; @@ -1612,7 +1612,7 @@ void export_intersections_to_svg(const std::string &filename, const Polygons &po intersecting_contours.insert(ie.second.first); } // Highlight the contours with intersections. - coord_t line_width = coord_t(scale_(0.01)); + coord_t line_width = scale_t(0.01); for (const Points *ic : intersecting_contours) { svg.draw_outline(Polygon(*ic), "green"); svg.draw_outline(Polygon(*ic), "black", line_width); diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index c3bc869d4..3141b440f 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -88,10 +88,10 @@ public: assert(m_bbox.contains(p2)); p1 -= m_bbox.min; p2 -= m_bbox.min; - assert(p1.x() >= 0 && p1.x() < m_cols * m_resolution); - assert(p1.y() >= 0 && p1.y() < m_rows * m_resolution); - assert(p2.x() >= 0 && p2.x() < m_cols * m_resolution); - assert(p2.y() >= 0 && p2.y() < m_rows * m_resolution); + assert(p1.x() >= 0 && p1.x() < coord_t(m_cols) * m_resolution); + assert(p1.y() >= 0 && p1.y() < coord_t(m_rows) * m_resolution); + assert(p2.x() >= 0 && p2.x() < coord_t(m_cols) * m_resolution); + assert(p2.y() >= 0 && p2.y() < coord_t(m_rows) * m_resolution); // Get the cells of the end points. coord_t ix = p1(0) / m_resolution; coord_t iy = p1(1) / m_resolution; @@ -248,9 +248,9 @@ public: std::pair<std::vector<std::pair<size_t, size_t>>::const_iterator, std::vector<std::pair<size_t, size_t>>::const_iterator> cell_data_range(coord_t row, coord_t col) const { assert(row >= 0); - assert(row < m_rows); + assert(row < (coord_t)m_rows); assert(col >= 0); - assert(col < m_cols); + assert(col < (coord_t)m_cols); const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col]; return std::make_pair(m_cell_data.begin() + cell.begin, m_cell_data.begin() + cell.end); } diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index 83288929c..fac733c5b 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -536,14 +536,14 @@ static bool validate_expoly_orientation(const ExPolygon &expoly) #endif /* NDEBUG */ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_contour_width, const double compensation) -{ +{ assert(validate_expoly_orientation(input_expoly)); - double scaled_compensation = scale_(compensation); - min_contour_width = scale_(min_contour_width); - double min_contour_width_compensated = min_contour_width + 2. * scaled_compensation; + coordf_t scaled_compensation = scale_d(compensation); + coordf_t scaled_min_contour_width = scale_d(min_contour_width); + coordf_t min_contour_width_compensated = scaled_min_contour_width + 2. * scaled_compensation; // Make the search radius a bit larger for the averaging in contour_distance over a fan of rays to work. - double search_radius = min_contour_width_compensated + min_contour_width * 0.5; + coordf_t search_radius = min_contour_width_compensated + scaled_min_contour_width * 0.5; BoundingBox bbox = get_extents(input_expoly.contour); Point bbox_size = bbox.size(); @@ -578,12 +578,12 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c for (float &d : dists) { // printf("Point %d, Distance: %lf\n", int(&d - dists.data()), unscale<double>(d)); // Convert contour width to available compensation distance. - if (d < min_contour_width) + if (d < scaled_min_contour_width) d = 0.f; else if (d > min_contour_width_compensated) d = - float(scaled_compensation); else - d = - (d - float(min_contour_width)) / 2.f; + d = - (d - float(scaled_min_contour_width)) / 2.f; assert(d >= - float(scaled_compensation) && d <= 0.f); } // smooth_compensation(dists, 0.4f, 10); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index fbb5ed75a..c827025f9 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -202,10 +202,10 @@ inline Polylines to_polylines(ExPolygon &&src) Polyline &pl = polylines[idx ++]; pl.points = std::move(src.contour.points); pl.points.push_back(pl.points.front()); - for (Polygons::const_iterator ith = src.holes.begin(); ith != src.holes.end(); ++ith) { + for (Polygon& ith : src.holes) { Polyline &pl = polylines[idx ++]; - pl.points = std::move(ith->points); - pl.points.push_back(ith->points.front()); + pl.points = std::move(ith.points); + pl.points.push_back(pl.points.front()); } assert(idx == polylines.size()); return polylines; @@ -216,14 +216,14 @@ inline Polylines to_polylines(ExPolygons &&src) Polylines polylines; polylines.assign(number_polygons(src), Polyline()); size_t idx = 0; - for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) { + for (ExPolygon& ex_poly : src) { Polyline &pl = polylines[idx ++]; - pl.points = std::move(it->contour.points); + pl.points = std::move(ex_poly.contour.points); pl.points.push_back(pl.points.front()); - for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) { + for (Polygon& ith : ex_poly.holes) { Polyline &pl = polylines[idx ++]; - pl.points = std::move(ith->points); - pl.points.push_back(ith->points.front()); + pl.points = std::move(ith.points); + pl.points.push_back(pl.points.front()); } } assert(idx == polylines.size()); @@ -243,9 +243,9 @@ inline Polygons to_polygons(const ExPolygons &src) { Polygons polygons; polygons.reserve(number_polygons(src)); - for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) { - polygons.push_back(it->contour); - polygons.insert(polygons.end(), it->holes.begin(), it->holes.end()); + for (const ExPolygon& ex_poly : src) { + polygons.push_back(ex_poly.contour); + polygons.insert(polygons.end(), ex_poly.holes.begin(), ex_poly.holes.end()); } return polygons; } diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index e9783a75d..d6c0fc49f 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -221,12 +221,12 @@ double Extruder::retract_restart_extra_toolchange() const int16_t Extruder::temp_offset() const { - return m_config->extruder_temperature_offset.get_at(m_id); + return int16_t(m_config->extruder_temperature_offset.get_at(m_id)); } int8_t Extruder::fan_offset() const { - return m_config->extruder_fan_offset.get_at(m_id); + return int8_t(m_config->extruder_fan_offset.get_at(m_id)); } double Mill::retract_lift() const { diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index c062456de..a675ae9e7 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -435,7 +435,7 @@ public: ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) { assert(this->first_point() == this->paths.back().polyline.points.back()); } ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) { this->paths.push_back(path); } - ExtrusionLoop(const ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) + ExtrusionLoop(ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) { this->paths.emplace_back(std::move(path)); } virtual bool is_loop() const override{ return true; } virtual bool can_reverse() const override { return false; } diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 14515f511..fc49e9b88 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -638,11 +638,11 @@ static inline Intersection* get_nearest_intersection(std::vector<std::pair<Inter // Create a line representing the anchor aka hook extrusion based on line_to_offset // translated in the direction of the intersection line (intersection.intersect_line). -static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset) +static Line create_offset_line(Line offset_line, const Intersection &intersection, const coordf_t scaled_offset) { offset_line.translate((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>()); // Extend the line by a small value to guarantee a collision with adjacent lines - offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6) + offset_line.extend(coordf_t(coord_t(scaled_offset * 1.16))); // / cos(PI/6) return offset_line; } @@ -1385,8 +1385,8 @@ void Filler::_fill_surface_single( } #endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */ - const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length))); - const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max))); + const coordf_t hook_length = std::min<coordf_t>((coordf_t)std::numeric_limits<coord_t>::max(), scale_d(params.anchor_length)); + const coordf_t hook_length_max = std::min<coordf_t>((coordf_t)std::numeric_limits<coord_t>::max(), scale_d(params.anchor_length_max)); Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->get_spacing(), hook_length, hook_length_max) : std::move(all_polylines); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index d028e4510..619b41629 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -243,8 +243,8 @@ Fill::do_gap_fill(const ExPolygons& gapfill_areas, const FillParams& params, Ext // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), // true); ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); - double minarea = params.flow.scaled_width() * params.flow.scaled_width(); - if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width(); + double minarea = double(params.flow.scaled_width()) * double(params.flow.scaled_width()); + if (params.config != nullptr) minarea = scale_d(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * double(params.flow.scaled_width()); for (const ExPolygon& ex : gapfill_areas_collapsed) { //remove too small gaps that are too hard to fill. //ie one that are smaller than an extrusion with width of min and a length of max. @@ -2109,8 +2109,8 @@ void connect_infill(Polylines&& infill_ordered, const std::vector<const Polygon* assert(params.anchor_length >= 0.); assert(params.anchor_length_max >= 0.01f); assert(params.anchor_length_max >= params.anchor_length); - const double anchor_length = scale_(params.anchor_length); - const double anchor_length_max = scale_(params.anchor_length_max); + const coordf_t anchor_length = scale_d(params.anchor_length); + const coordf_t anchor_length_max = scale_d(params.anchor_length_max); #if 0 append(polylines_out, infill_ordered); diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 10b76c357..86de14193 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -174,8 +174,8 @@ FillConcentricWGapFill::fill_surface_extrusion( ExPolygons gapfill_areas = diff_ex({ surface->expolygon }, offset_ex(expp, double(scale_(0.5 * this->get_spacing())))); gapfill_areas = union_ex(gapfill_areas, true); if (gapfill_areas.size() > 0) { - double minarea = params.flow.scaled_width() * params.flow.scaled_width(); - if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width(); + double minarea = double(params.flow.scaled_width()) * double(params.flow.scaled_width()); + if (params.config != nullptr) minarea = scale_d(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * double(params.flow.scaled_width()); for (int i = 0; i < gapfill_areas.size(); i++) { if (gapfill_areas[i].area() < minarea) { gapfill_areas.erase(gapfill_areas.begin() + i); diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 8209bc3b0..c51cdf914 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -184,7 +184,7 @@ void FillGyroid::_fill_surface_single( if (! polylines.empty()) { // Remove very small bits, but be careful to not remove infill lines connecting thin walls! // The infill perimeter lines should be separated by around a single infill line width. - const double minlength = scale_(0.8 * this->get_spacing()); + const coordf_t minlength = scale_d(0.8 * this->get_spacing()); polylines.erase( std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }), polylines.end()); diff --git a/src/libslic3r/Fill/FillLine.cpp b/src/libslic3r/Fill/FillLine.cpp index de4df4893..eb2a17af5 100644 --- a/src/libslic3r/Fill/FillLine.cpp +++ b/src/libslic3r/Fill/FillLine.cpp @@ -94,7 +94,7 @@ void FillLine::_fill_surface_single( // offset the expolygon by max(min_spacing/2, extra) ExPolygon expolygon_off; { - ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2); + ExPolygons expolygons_off = offset_ex(expolygon, coordf_t(this->_min_spacing / 2)); if (! expolygons_off.empty()) { // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island. assert(expolygons_off.size() == 1); diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index e47e121f8..1dd8acd3d 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -2819,8 +2819,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa ExPolygonWithOffset poly_with_offset( surface->expolygon, - rotate_vector.first, - float(scale_(0 /*this->overlap*/ - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->get_spacing())), - float(scale_(0 /*this->overlap*/ - 0.5f * this->get_spacing()))); + (scale_t(0 /*this->overlap*/ - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->get_spacing())), + (scale_t(0 /*this->overlap*/ - 0.5f * this->get_spacing()))); if (poly_with_offset.n_contours_inner == 0) { // Not a single infill line fits. //Prusa: maybe one shall trigger the gap fill here? @@ -2956,24 +2956,24 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface* surface, FillPar { assert(sweep_params.size() > 1); assert(!params.full_infill()); - params.density /= double(sweep_params.size()); + params.density /= float(sweep_params.size()); assert(params.density > 0.0001f && params.density <= 1.f); - ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, float(scale_(this->overlap - 0.5 * this->get_spacing()))); + ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, scale_t(this->overlap - 0.5 * this->get_spacing())); if (poly_with_offset_base.n_contours == 0) // Not a single infill line fits. return true; Polylines fill_lines; - coord_t line_width = coord_t(scale_(this->get_spacing())); - coord_t line_spacing = coord_t(scale_(this->get_spacing()) / params.density); + coord_t line_width = scale_t(this->get_spacing()); + coord_t line_spacing = scale_t(this->get_spacing() / params.density); std::pair<float, Point> rotate_vector = this->_infill_direction(surface); for (const SweepParams& sweep : sweep_params) { size_t n_fill_lines_initial = fill_lines.size(); // Rotate polygons so that we can work with vertical lines here double angle = rotate_vector.first + sweep.angle_base; - ExPolygonWithOffset poly_with_offset(poly_with_offset_base, -angle); + ExPolygonWithOffset poly_with_offset(poly_with_offset_base, float(-angle)); BoundingBox bounding_box = poly_with_offset.bounding_box_src(); // Don't produce infill lines, which fully overlap with the infill perimeter. coord_t x_min = bounding_box.min.x() + line_width + coord_t(SCALED_EPSILON); @@ -3096,10 +3096,10 @@ FillRectilinearPeri::fill_surface_extrusion(const Surface *surface, const FillPa Polylines polylines_1; //generate perimeter: ExPolygons path_perimeter = offset2_ex(surface->expolygon, - scale_(-this->get_spacing()), scale_(this->get_spacing() / 2), - ClipperLib::jtMiter, scale_(this->get_spacing()) * 10); + scale_d(-this->get_spacing()), scale_d(this->get_spacing() / 2), + ClipperLib::jtMiter, scale_d(this->get_spacing()) * 10); //fix a bug that can happens when (positive) offsetting with a big miter limit and two island merge. See https://github.com/supermerill/SuperSlicer/issues/609 - path_perimeter = intersection_ex(path_perimeter, offset_ex(surface->expolygon, scale_(-this->get_spacing() / 2))); + path_perimeter = intersection_ex(path_perimeter, offset_ex(surface->expolygon, scale_d(-this->get_spacing() / 2))); for (ExPolygon &expolygon : path_perimeter) { expolygon.contour.make_counter_clockwise(); polylines_1.push_back(expolygon.contour.split_at_index(0)); @@ -3133,7 +3133,7 @@ FillRectilinearPeri::fill_surface_extrusion(const Surface *surface, const FillPa Polylines polylines_2; bool canFill = true; //50% overlap with the new perimeter - ExPolygons path_inner = offset2_ex(surface->expolygon, scale_(-this->get_spacing() * 1.5), scale_(this->get_spacing())); + ExPolygons path_inner = offset2_ex(surface->expolygon, scale_d(-this->get_spacing() * 1.5), scale_d(this->get_spacing())); for (ExPolygon &expolygon : path_inner) { Surface surfInner(*surface, expolygon); if (!fill_surface_by_lines(&surfInner, params, 0.f, 0.f, polylines_2)) { @@ -3203,7 +3203,7 @@ Polylines FillScatteredRectilinear::fill_surface(const Surface *surface, const F Polylines polylines_out; // Offset the pattern randomly using the current layer index as the generator - float offset = randomFloatFromSeed((uint32_t) layer_id) * 0.5f * this->get_spacing(); + float offset = (float)randomFloatFromSeed((uint32_t) layer_id) * 0.5f * this->get_spacing(); if (!fill_surface_by_lines(surface, params, 0.f, offset, polylines_out)) { printf("FillScatteredRectilinear::fill_surface() failed to fill a region.\n"); @@ -3307,7 +3307,7 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi current_extrusion->push_back(last, tooth_zhop); // add new extrusion that go down with no nozzle_flow / sqrt(2) - extrusions->paths.push_back(ExtrusionPath3D(good_role, params.flow.mm3_per_mm() / std::sqrt(2), params.flow.width / std::sqrt(2), params.flow.height)); + extrusions->paths.push_back(ExtrusionPath3D(good_role, params.flow.mm3_per_mm() / std::sqrt(2), float(params.flow.width / std::sqrt(2)), params.flow.height)); current_extrusion = &(extrusions->paths.back()); current_extrusion->push_back(last, tooth_zhop); //add next point @@ -3438,7 +3438,7 @@ FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const Fi eec->entities, polylines_rectilinear, good_role, params.flow.mm3_per_mm() * params.flow_mult * flow_mult_exact_volume, - params.flow.width * params.flow_mult * flow_mult_exact_volume, + params.flow.width * params.flow_mult * float(flow_mult_exact_volume), params.flow.height); coll_nosort->entities.push_back(eec); @@ -3452,7 +3452,7 @@ FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const Fi gapfill_areas.insert(gapfill_areas.end(), unextruded_areas.begin(), unextruded_areas.end()); gapfill_areas = union_ex(gapfill_areas, true); if (gapfill_areas.size() > 0) { - const double minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width(); + const double minarea = scale_d(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * double(params.flow.scaled_width()); for (int i = 0; i < gapfill_areas.size(); i++) { if (gapfill_areas[i].area() < minarea) { gapfill_areas.erase(gapfill_areas.begin() + i); diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 72e3ecb94..b13eb3f87 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -290,7 +290,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig const auto &width = (object->config().first_layer_extrusion_width.value > 0) ? object->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; float slice_height = layer_height; if (layer_height <= 0.f && !object->print()->config().nozzle_diameter.empty()){ - slice_height = (float)(object->config().first_layer_height.get_abs_value(object->print()->config().nozzle_diameter.get_at(0))); + slice_height = (float)object->get_first_layer_height(); } return Flow::new_from_config_width( frSupportMaterial, diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index fe98c0518..8db4a24d4 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2109,8 +2109,8 @@ namespace Slic3r { bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, const std::string &file_path); - bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data, const std::string &file_path); - bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); + bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const DynamicPrintConfig& print_config, const IdToObjectDataMap &objects_data, const std::string &file_path); + bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig& config); }; bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) @@ -2211,7 +2211,7 @@ namespace Slic3r { // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"). // All custom gcode per height of whole Model are stored here - if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) + if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, *config)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2238,21 +2238,21 @@ namespace Slic3r { // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides). // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data // is stored here as well. - if (!_add_model_config_file_to_archive(archive, model, objects_data, MODEL_CONFIG_FILE)) + if (!_add_model_config_file_to_archive(archive, model, *config, objects_data, MODEL_CONFIG_FILE)) { close_zip_writer(&archive); boost::filesystem::remove(filename); return false; } // also add prusa - if (!_add_model_config_file_to_archive(archive, model, objects_data, MODEL_PRUSA_CONFIG_FILE)) + if (!_add_model_config_file_to_archive(archive, model, *config, objects_data, MODEL_PRUSA_CONFIG_FILE)) { close_zip_writer(&archive); boost::filesystem::remove(filename); return false; } // also superslicer for backward comp, just for some version from 2.3.56 - if (!_add_model_config_file_to_archive(archive, model, objects_data, MODEL_SUPER_CONFIG_FILE)) + if (!_add_model_config_file_to_archive(archive, model, *config, objects_data, MODEL_SUPER_CONFIG_FILE)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2787,7 +2787,7 @@ namespace Slic3r { return true; } - bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data, const std::string &file_path) + bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const DynamicPrintConfig& print_config, const IdToObjectDataMap &objects_data, const std::string &file_path) { std::stringstream stream; // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back @@ -2802,6 +2802,7 @@ namespace Slic3r { const ModelObject* obj = obj_metadata.second.object; if (obj != nullptr) { + DynamicPrintConfig obj_config_wparent; // part of the chain of config, used as reference to convert configs to prusa config // Output of instances count added because of github #3435, currently not used by PrusaSlicer stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n"; @@ -2812,10 +2813,13 @@ namespace Slic3r { // stores object's config data if (file_path == MODEL_PRUSA_CONFIG_FILE) { + assert(obj->config.get().parent == nullptr); + obj_config_wparent = obj->config.get(); + obj_config_wparent.parent = &print_config; for (std::string key : obj->config.keys()) { // convert to prusa config std::string value = obj->config.opt_serialize(key); - obj->config.to_prusa(key, value); + obj_config_wparent.to_prusa(key, value); if (!key.empty()) stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << value << "\"/>\n"; } @@ -2884,10 +2888,13 @@ namespace Slic3r { // stores volume's config data if (file_path == MODEL_PRUSA_CONFIG_FILE) { + assert(volume->config.get().parent == nullptr); + DynamicPrintConfig copy_config = volume->config.get(); + copy_config.parent = &obj_config_wparent; for (std::string key : volume->config.keys()) { // convert to prusa config std::string value = volume->config.opt_serialize(key); - volume->config.to_prusa(key, value); + copy_config.to_prusa(key, value); if (!key.empty()) stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << value << "\"/>\n"; } @@ -2918,7 +2925,7 @@ namespace Slic3r { return true; } -bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config) +bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig& config) { std::string out = ""; @@ -2940,9 +2947,9 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv code_tree.put("<xmlattr>.extra" , code.extra ); // add gcode field data for the old version of the PrusaSlicer - std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") : - code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") : - code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : + std::string gcode = code.type == CustomGCode::ColorChange ? config.opt_string("color_change_gcode") : + code.type == CustomGCode::PausePrint ? config.opt_string("pause_print_gcode") : + code.type == CustomGCode::Template ? config.opt_string("template_custom_gcode") : code.type == CustomGCode::ToolChange ? "tool_change" : code.extra; code_tree.put("<xmlattr>.gcode" , gcode ); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8121e3f43..2ee128c92 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -158,6 +158,8 @@ std::string Wipe::wipe(GCode& gcodegen, bool toolchange) /* Reduce feedrate a bit; travel speed is often too high to move on existing material. Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; + if(gcodegen.writer().tool_is_extruder() && gcodegen.writer().config.wipe_speed.get_at(gcodegen.writer().tool()->id()) > 0) + wipe_speed = gcodegen.writer().config.wipe_speed.get_at(gcodegen.writer().tool()->id()); // get the retraction length double length = gcodegen.writer().tool()->retract_length(); @@ -173,7 +175,7 @@ std::string Wipe::wipe(GCode& gcodegen, bool toolchange) /* Calculate how long we need to travel in order to consume the required amount of retraction. In other words, how far do we move in XY at wipe_speed for the time needed to consume retract_length at retract_speed? */ - double wipe_dist = scale_(length / gcodegen.writer().tool()->retract_speed() * wipe_speed); + coordf_t wipe_dist = scale_d(length / gcodegen.writer().tool()->retract_speed() * wipe_speed); /* Take the stored wipe path and replace first point with the current actual position (they might be different, for example, in case of loop clipping). */ @@ -190,7 +192,7 @@ std::string Wipe::wipe(GCode& gcodegen, bool toolchange) if (!wipe_path.empty()) { // add tag for processor gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n"; - for (const Line& line : wipe_path.lines()) { + for (const Line& line : wipe_path.lines()) { double segment_length = line.length(); /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one due to rounding (TODO: test and/or better math for this) */ @@ -223,7 +225,7 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW //get biggest first layer height and set extra lift for first travel, to be safe. double extra_lift_value = 0; for (const PrintObject* obj : print.objects()) - extra_lift_value = std::max(extra_lift_value, obj->config().first_layer_height.get_abs_value(print.config().nozzle_diameter.get_at(0))); + extra_lift_value = std::max(extra_lift_value, print.get_object_first_layer_height(*obj)); writer.set_extra_lift(extra_lift_value * 2); } } @@ -235,13 +237,13 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const { - if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); - std::string gcode; + std::string gcode; - // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) - // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position + // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) + // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f { @@ -250,112 +252,112 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW return out; }; - Vec2f start_pos = tcr.start_pos; - Vec2f end_pos = tcr.end_pos; + Vec2f start_pos = tcr.start_pos; + Vec2f end_pos = tcr.end_pos; if (! tcr.priming) { start_pos = transform_wt_pt(start_pos); end_pos = transform_wt_pt(end_pos); - } + } - Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; - float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; - std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - //if needed, write the gcode_label_objects_end then priming tower - if (!gcodegen.m_gcode_label_objects_end.empty()) { - gcode += gcodegen.m_gcode_label_objects_end; - gcodegen.m_gcode_label_objects_end = ""; - } + //if needed, write the gcode_label_objects_end then priming tower + if (!gcodegen.m_gcode_label_objects_end.empty()) { + gcode += gcodegen.m_gcode_label_objects_end; + gcodegen.m_gcode_label_objects_end = ""; + } - if (! tcr.priming) { - // Move over the wipe tower. - // Retract for a tool change, using the toolchange retract value and setting the priming extra length. - gcode += gcodegen.retract(true); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - Polyline polyline = gcodegen.travel_to( - gcode, - wipe_tower_point_to_object_point(gcodegen, start_pos), - erMixed); - gcodegen.write_travel_to(gcode, polyline, "Travel to a Wipe Tower"); - gcode += gcodegen.unretract(); - } + if (! tcr.priming) { + // Move over the wipe tower. + // Retract for a tool change, using the toolchange retract value and setting the priming extra length. + gcode += gcodegen.retract(true); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + Polyline polyline = gcodegen.travel_to( + gcode, + wipe_tower_point_to_object_point(gcodegen, start_pos), + erMixed); + gcodegen.write_travel_to(gcode, polyline, "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + } - double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos - z = current_z; - if (! is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); - gcode += gcodegen.writer().unretract(); - } + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + if (! is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); + gcode += gcodegen.writer().unretract(); + } - // Process the end filament gcode. - std::string end_filament_gcode_str; - if (gcodegen.writer().tool() != nullptr && gcodegen.writer().tool_is_extruder()) { - // Process the custom end_filament_gcode in case of single_extruder_multi_material. - uint16_t old_extruder_id = gcodegen.writer().tool()->id(); - const std::string& end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); - if (gcodegen.writer().tool() != nullptr && ! end_filament_gcode.empty()) { - DynamicConfig config; - int previous_extruder_id = gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1; - config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); - config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); - end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id, &config); - check_add_eol(end_filament_gcode_str); + // Process the end filament gcode. + std::string end_filament_gcode_str; + if (gcodegen.writer().tool() != nullptr && gcodegen.writer().tool_is_extruder()) { + // Process the custom end_filament_gcode in case of single_extruder_multi_material. + uint16_t old_extruder_id = gcodegen.writer().tool()->id(); + const std::string& end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); + if (gcodegen.writer().tool() != nullptr && ! end_filament_gcode.empty()) { + DynamicConfig config; + int previous_extruder_id = gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1; + config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); + config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); + end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id, &config); + check_add_eol(end_filament_gcode_str); + } } - } - // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. - // Otherwise, leave control to the user completely. - std::string toolchange_gcode_str; + // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. + // Otherwise, leave control to the user completely. + std::string toolchange_gcode_str; - if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) - toolchange_gcode_str += gcodegen.toolchange(new_extruder_id, tcr.print_z); + if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) + toolchange_gcode_str += gcodegen.toolchange(new_extruder_id, tcr.print_z); - gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); + gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); - // Process the start filament gcode. - std::string start_filament_gcode_str; + // Process the start filament gcode. + std::string start_filament_gcode_str; const std::string& start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); if (!start_filament_gcode.empty()) { - // Process the start_filament_gcode for the active filament only. - DynamicConfig config; - config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); - config.set_key_value("previous_extruder", new ConfigOptionInt(gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1)); - config.set_key_value("next_extruder", new ConfigOptionInt(new_extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(0)); - config.set_key_value("layer_z", new ConfigOptionFloat(z)); - start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); - check_add_eol(start_filament_gcode_str); - } + // Process the start_filament_gcode for the active filament only. + DynamicConfig config; + config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); + config.set_key_value("previous_extruder", new ConfigOptionInt(gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1)); + config.set_key_value("next_extruder", new ConfigOptionInt(new_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(0)); + config.set_key_value("layer_z", new ConfigOptionFloat(z)); + start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); + check_add_eol(start_filament_gcode_str); + } - // Insert the end filament, toolchange, and start filament gcode into the generated gcode. - DynamicConfig config; - config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); - config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); - std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); - unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); - gcode += tcr_gcode; - check_add_eol(toolchange_gcode_str); - - if (gcodegen.writer().tool() && gcodegen.m_config.filament_enable_toolchange_part_fan.values[gcodegen.writer().tool()->id()]) { - //if the fan may have been changed silently by the wipetower, recover it. - gcode += gcodegen.m_writer.set_fan(gcodegen.m_writer.get_fan(), true); - } + // Insert the end filament, toolchange, and start filament gcode into the generated gcode. + DynamicConfig config; + config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); + config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); + config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); + std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); + unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); + gcode += tcr_gcode; + check_add_eol(toolchange_gcode_str); + + if (gcodegen.writer().tool() && gcodegen.m_config.filament_enable_toolchange_part_fan.values[gcodegen.writer().tool()->id()]) { + //if the fan may have been changed silently by the wipetower, recover it. + gcode += gcodegen.m_writer.set_fan(gcodegen.m_writer.get_fan(), true); + } - // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(end_pos.cast<double>()); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(end_pos.cast<double>()); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); if (!is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); - gcode += gcodegen.writer().unretract(); - } else { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); + gcode += gcodegen.writer().unretract(); + } else { // Prepare a future wipe. gcodegen.m_wipe.reset_path(); for (const Vec2f& wipe_pt : tcr.wipe_path) @@ -367,10 +369,10 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW return gcode; } - // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode - // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) - std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const - { +// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode +// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) +std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const +{ Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast<float>(); std::istringstream gcode_str(tcr.gcode); @@ -388,7 +390,7 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW // WT generator can override this by appending the never_skip_tag if (line.find("G1 ") == 0) { bool never_skip = false; - auto it = line.find(WipeTower::never_skip_tag()); + auto it = line.find(WipeTower::never_skip_tag()); if (it != std::string::npos) { // remove the tag and remember we saw it never_skip = true; @@ -399,7 +401,7 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW line_str >> std::noskipws; // don't skip whitespace char ch = 0; while (line_str >> ch) { - if (ch == 'X' || ch == 'Y') + if (ch == 'X' || ch == 'Y') line_str >> (ch == 'X' ? pos.x() : pos.y()); else line_out << ch; @@ -439,7 +441,7 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW } } return gcode_out; - } +} std::string WipeTowerIntegration::prime(GCode& gcodegen) @@ -454,33 +456,32 @@ static inline void set_extra_lift(const Layer& layer, const Print& print, GCodeW std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer) { - std::string gcode; - assert(m_layer_idx >= 0); + std::string gcode; + assert(m_layer_idx >= 0); if (!m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { - if (m_layer_idx < (int)m_tool_changes.size()) { + if (m_layer_idx < (int)m_tool_changes.size()) { if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); - - // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, - // resulting in a wipe tower with sparse layers. - double wipe_tower_z = -1; - bool ignore_sparse = false; - if (gcodegen.config().wipe_tower_no_sparse_layers.value) { - wipe_tower_z = m_last_wipe_tower_print_z; - ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); + // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, + // resulting in a wipe tower with sparse layers. + double wipe_tower_z = -1; + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + wipe_tower_z = m_last_wipe_tower_print_z; + ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); if (m_tool_change_idx == 0 && !ignore_sparse) - wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; + wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; } if (!ignore_sparse) { - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); - m_last_wipe_tower_print_z = wipe_tower_z; + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); + m_last_wipe_tower_print_z = wipe_tower_z; + } } - } - m_brim_done = true; - } - return gcode; + m_brim_done = true; + } + return gcode; } // Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. @@ -575,8 +576,8 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec // Remember last layer with extrusions. if (has_extrusions) - last_extrusion_layer = &layers_to_print.back(); - } + last_extrusion_layer = &layers_to_print.back(); + } } return layers_to_print; @@ -740,31 +741,34 @@ namespace DoExport { excluded.insert(erMixed); excluded.insert(erNone); excluded.insert(erWipeTower); - if (config->get_abs_value("perimeter_speed") != 0 && config->get_abs_value("small_perimeter_speed") != 0) { + if (config->option("perimeter_speed") != nullptr && config->option("perimeter_speed")->getFloat() != 0 + && config->option("small_perimeter_speed") != nullptr && config->option("small_perimeter_speed")->getFloat() != 0) { excluded.insert(erPerimeter); excluded.insert(erSkirt); } - if (config->get_abs_value("external_perimeter_speed") != 0 && config->get_abs_value("small_perimeter_speed") != 0) + if (config->option("external_perimeter_speed") != nullptr && config->option("external_perimeter_speed")->getFloat() != 0 + && config->option("small_perimeter_speed") != nullptr && config->option("small_perimeter_speed")->getFloat() != 0) excluded.insert(erExternalPerimeter); - if (config->get_abs_value("overhangs_speed") != 0 && config->get_abs_value("small_perimeter_speed") != 0) + if (config->option("overhangs_speed") != nullptr && config->option("overhangs_speed")->getFloat() != 0 + && config->option("small_perimeter_speed") != nullptr && config->option("small_perimeter_speed")->getFloat() != 0) excluded.insert(erOverhangPerimeter); - if (config->get_abs_value("gap_fill_speed") != 0) + if (config->option("gap_fill_speed") != nullptr && config->option("gap_fill_speed")->getFloat() != 0) excluded.insert(erGapFill); - if (config->get_abs_value("thin_walls_speed") != 0) + if (config->option("thin_walls_speed") != nullptr && config->option("thin_walls_speed")->getFloat() != 0) excluded.insert(erThinWall); - if (config->get_abs_value("infill_speed") != 0) + if (config->option("infill_speed") != nullptr && config->option("infill_speed")->getFloat() != 0) excluded.insert(erInternalInfill); - if (config->get_abs_value("solid_infill_speed") != 0) + if (config->option("solid_infill_speed") != nullptr && config->option("solid_infill_speed")->getFloat() != 0) excluded.insert(erSolidInfill); - if (config->get_abs_value("top_solid_infill_speed") != 0) + if (config->option("top_solid_infill_speed") != nullptr && config->option("top_solid_infill_speed")->getFloat() != 0) excluded.insert(erTopSolidInfill); - if (config->get_abs_value("bridge_speed") != 0) + if (config->option("bridge_speed") != nullptr && config->option("bridge_speed")->getFloat() != 0) excluded.insert(erBridgeInfill); - if (config->get_abs_value("bridge_speed_internal") != 0) + if (config->option("bridge_speed_internal") != nullptr && config->option("bridge_speed_internal")->getFloat() != 0) excluded.insert(erInternalBridgeInfill); - if (config->get_abs_value("support_material_speed") != 0) + if (config->option("support_material_speed") != nullptr && config->option("support_material_speed")->getFloat() != 0) excluded.insert(erSupportMaterial); - if (config->get_abs_value("support_material_interface_speed") != 0) + if (config->option("support_material_interface_speed") != nullptr && config->option("support_material_interface_speed")->getFloat() != 0) excluded.insert(erSupportMaterialInterface); } virtual void use(const ExtrusionPath& path) override { @@ -1087,6 +1091,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu { PROFILE_FUNC(); + m_last_status_update = std::chrono::system_clock::now(); + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -1185,7 +1191,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Write some terse information on the slicing parameters. const PrintObject *first_object = print.objects().front(); const double layer_height = first_object->config().layer_height.value; - const double first_layer_height = first_object->config().first_layer_height.get_abs_value(m_config.nozzle_diameter.empty()?0.:m_config.nozzle_diameter.get_at(0)); + const double first_layer_height = print.get_first_layer_height(); for (const PrintRegion* region : print.regions()) { _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); @@ -1290,7 +1296,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu } // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // Use the extruder IDs collected from Regions. - this->set_extruders(print.extruders()); + std::set<uint16_t> extruder_set = print.extruders(); + this->set_extruders(std::vector<uint16_t>(extruder_set.begin(), extruder_set.end())); if(has_milling) m_writer.set_mills(std::vector<uint16_t>() = { 0 }); } else { @@ -1811,11 +1818,11 @@ void GCode::print_machine_envelope(FILE *file, Print &print) int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); if (std::set<uint8_t>{gcfRepRap}.count(print.config().gcode_flavor.value) > 0) - _write_format(file, "M566 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", - print.config().machine_max_jerk_x.values.front(), - print.config().machine_max_jerk_y.values.front(), - print.config().machine_max_jerk_z.values.front(), - print.config().machine_max_jerk_e.values.front()); + _write_format(file, "M566 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/min\n", + print.config().machine_max_jerk_x.values.front() * 60, + print.config().machine_max_jerk_y.values.front() * 60, + print.config().machine_max_jerk_z.values.front() * 60, + print.config().machine_max_jerk_e.values.front() * 60); if (std::set<uint8_t>{gcfMarlin, gcfLerdge, gcfRepetier}.count(print.config().gcode_flavor.value) > 0) _write_format(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", print.config().machine_max_jerk_x.values.front(), @@ -2712,6 +2719,13 @@ void GCode::process_layer( _write(file, gcode); BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); + + + std::chrono::time_point<std::chrono::system_clock> end_export_layer = std::chrono::system_clock::now(); + if ((static_cast<std::chrono::duration<double>>(end_export_layer - m_last_status_update)).count() > 0.2) { + m_last_status_update = std::chrono::system_clock::now(); + print.set_status(int((layer.id() * 100) / layer_count()), std::string(L("Generating G-code layer %s / %s")), std::vector<std::string>{ std::to_string(layer.id()), std::to_string(layer_count()) }, PrintBase::SlicingStatus::DEFAULT); + } } void GCode::apply_print_config(const PrintConfig &print_config) @@ -2865,8 +2879,8 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // apply the small/external? perimeter speed if (speed == -1 && is_perimeter(paths.front().role()) && loop.length() <= scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)))) { - double min_length = scale_(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); - double max_length = scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + coordf_t min_length = scale_d(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + coordf_t max_length = scale_d(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); if (loop.length() <= min_length) { speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); } else { @@ -2913,7 +2927,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s Vec2d p1 = paths.front().polyline.points.front().cast<double>(); Vec2d p2 = paths.front().polyline.points[1].cast<double>(); Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); + double nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3061,7 +3075,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s Vec2d p1 = paths.front().polyline.points.front().cast<double>(); Vec2d p2 = paths.front().polyline.points[1].cast<double>(); Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); + coordf_t nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3182,8 +3196,8 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // apply the small perimeter speed if (speed == -1 && is_perimeter(paths.front().role()) && loop.length() <= scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)))) { - double min_length = scale_(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); - double max_length = scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + double min_length = scale_d(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + double max_length = scale_d(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); if (loop.length() <= min_length) { speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); } else { @@ -3218,7 +3232,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s //extra wipe before the little move. if (EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter, 0) > 0) { - coord_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter,0)); + coordf_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter,0)); ExtrusionPaths paths_wipe; for (int i = 0; i < paths.size(); i++) { ExtrusionPath& path = paths[i]; @@ -3276,7 +3290,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s Vec2d current_pos = current_point.cast<double>(); Vec2d next_pos = next_point.cast<double>(); Vec2d vec_dist = next_pos - current_pos; - double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); + double nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); double l2 = vec_dist.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3366,16 +3380,21 @@ void GCode::use(const ExtrusionEntityCollection &collection) { } } -std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &description, double speed) { +std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &description, double speed_mm_per_sec) { ExtrusionPath simplifed_path = path; - const double scaled_min_length = scale_(this->config().min_length.value); - if (scaled_min_length > 0 && !m_last_too_small.empty()) { + const coordf_t scaled_min_length = scale_d(this->config().min_length.value); + const double max_gcode_per_second = this->config().max_gcode_per_second.value; + double current_scaled_min_length = scaled_min_length; + if (max_gcode_per_second > 0) { + current_scaled_min_length = std::max(current_scaled_min_length, scale_(_compute_speed_mm_per_sec(path, speed_mm_per_sec)) / max_gcode_per_second); + } + if (current_scaled_min_length > 0 && !m_last_too_small.empty()) { //descr += " trys fusion " + std::to_string(unscaled(m_last_too_small.last_point().x())) + " , " + std::to_string(unscaled(path.first_point().x())); //ensure that it's a continous thing - if (m_last_too_small.last_point().distance_to_square(path.first_point()) < scaled_min_length*scaled_min_length /*&& m_last_too_small.first_point().distance_to_square(path.first_point()) > EPSILON*/) { + if (m_last_too_small.last_point().distance_to_square(path.first_point()) < current_scaled_min_length * current_scaled_min_length /*&& m_last_too_small.first_point().distance_to_square(path.first_point()) > EPSILON*/) { //descr += " ! fusion " + std::to_string(simplifed_path.polyline.points.size()); - simplifed_path.height = (m_last_too_small.height * m_last_too_small.length() + simplifed_path.height * simplifed_path.length()) / (m_last_too_small.length() + simplifed_path.length()); + simplifed_path.height = float(m_last_too_small.height * m_last_too_small.length() + simplifed_path.height * simplifed_path.length()) / float(m_last_too_small.length() + simplifed_path.length()); simplifed_path.mm3_per_mm = (m_last_too_small.mm3_per_mm * m_last_too_small.length() + simplifed_path.mm3_per_mm * simplifed_path.length()) / (m_last_too_small.length() + simplifed_path.length()); simplifed_path.polyline.points.insert(simplifed_path.polyline.points.begin(), m_last_too_small.polyline.points.begin(), m_last_too_small.polyline.points.end()-1); assert(simplifed_path.height == simplifed_path.height); @@ -3383,11 +3402,11 @@ std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &de } m_last_too_small.polyline.points.clear(); } - if (scaled_min_length > 0) { + if (current_scaled_min_length > 0) { // it's an alternative to simplifed_path.simplify(scale_(this->config().min_length)); with more enphasis ont he segment length that on the feature detail. // because tolerance = min_length /10, douglas_peucker will erase more points if angles are shallower than 6° and then the '_plus' will kick in to keep a bit more. // if angles are all bigger than 6°, then the douglas_peucker will do all the work. - simplifed_path.polyline.points = MultiPoint::_douglas_peucker_plus(simplifed_path.polyline.points, scaled_min_length / 10, scaled_min_length); + simplifed_path.polyline.points = MultiPoint::_douglas_peucker_plus(simplifed_path.polyline.points, current_scaled_min_length / 10, current_scaled_min_length); } //else simplifed_path.simplify(SCALED_RESOLUTION); //should already be simplified if (scaled_min_length > 0 && simplifed_path.length() < scaled_min_length) { @@ -3395,7 +3414,7 @@ std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &de return ""; } - std::string gcode = this->_extrude(simplifed_path, description, speed); + std::string gcode = this->_extrude(simplifed_path, description, speed_mm_per_sec); if (m_wipe.enable) { m_wipe.path = std::move(simplifed_path.polyline); @@ -3545,11 +3564,11 @@ void GCode::_post_process(std::string& what, bool flush) { if (this->m_fan_mover.get() == nullptr) this->m_fan_mover.reset(new Slic3r::FanMover( this->m_writer, - std::abs(this->config().fan_speedup_time.value), + std::abs((float)this->config().fan_speedup_time.value), this->config().fan_speedup_time.value > 0, this->config().use_relative_e_distances.value, this->config().fan_speedup_overhangs.value, - this->config().fan_kickstart.value)); + (float)this->config().fan_kickstart.value)); what = this->m_fan_mover->process_gcode(what, flush); } @@ -3680,7 +3699,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri double mult2 = 1 - coeff; double sum = 0; //Create a point - Point inter_point1 = line.point_at(scale_(length1)); + Point inter_point1 = line.point_at(scale_d(length1)); //extrude very reduced gcode += m_writer.extrude_to_xy( this->point_to_gcode(inter_point1), @@ -3689,7 +3708,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri sum += e_per_mm * (length1) * mult1; if (line_length - length1 > length2) { - Point inter_point2 = line.point_at(scale_(length2)); + Point inter_point2 = line.point_at(scale_d(length2)); //extrude reduced gcode += m_writer.extrude_to_xy( this->point_to_gcode(inter_point2), @@ -3737,6 +3756,102 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri return gcode; } +double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double speed) { + + // set speed + if (speed < 0) { + //if speed == -1, then it's means "choose yourself, but if it's -1 < speed <0 , then it's a scaling from small_periemter. + //it's a bit hacky, so if you want to rework it, help yourself. + float factor = float(-speed); + if (path.role() == erPerimeter) { + speed = m_config.get_computed_value("perimeter_speed"); + } else if (path.role() == erExternalPerimeter) { + speed = m_config.get_computed_value("external_perimeter_speed"); + } else if (path.role() == erBridgeInfill) { + speed = m_config.get_computed_value("bridge_speed"); + } else if (path.role() == erInternalBridgeInfill) { + speed = m_config.get_computed_value("bridge_speed_internal"); + } else if (path.role() == erOverhangPerimeter) { + speed = m_config.get_computed_value("overhangs_speed"); + } else if (path.role() == erInternalInfill) { + speed = m_config.get_computed_value("infill_speed"); + } else if (path.role() == erSolidInfill) { + speed = m_config.get_computed_value("solid_infill_speed"); + } else if (path.role() == erTopSolidInfill) { + speed = m_config.get_computed_value("top_solid_infill_speed"); + } else if (path.role() == erThinWall) { + speed = m_config.get_computed_value("thin_walls_speed"); + } else if (path.role() == erGapFill) { + speed = m_config.get_computed_value("gap_fill_speed"); + } else if (path.role() == erIroning) { + speed = m_config.get_computed_value("ironing_speed"); + } else if (path.role() == erNone) { + speed = m_config.get_computed_value("travel_speed"); + } else if (path.role() == erMilling) { + speed = m_config.get_computed_value("milling_speed"); + } else { + throw Slic3r::InvalidArgument("Invalid speed"); + } + //don't modify bridge speed + if (factor < 1 && !(is_bridge(path.role()))) { + float small_speed = (float)m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); + //apply factor between feature speed and small speed + speed = (speed * factor) + double((1.f - factor) * small_speed); + } + } + if (m_volumetric_speed != 0. && speed == 0) { + //if m_volumetric_speed, use the max size for thinwall & gapfill, to avoid variations + double vol_speed = m_volumetric_speed / path.mm3_per_mm; + if (vol_speed > m_config.max_print_speed.value) + vol_speed = m_config.max_print_speed.value; + // if using a % of an auto speed, use the % over the volumetric speed. + if (path.role() == erExternalPerimeter) { + speed = m_config.external_perimeter_speed.get_abs_value(vol_speed); + } else if (path.role() == erInternalBridgeInfill) { + speed = m_config.bridge_speed_internal.get_abs_value(vol_speed); + } else if (path.role() == erOverhangPerimeter) { + speed = m_config.overhangs_speed.get_abs_value(vol_speed); + } else if (path.role() == erSolidInfill) { + speed = m_config.solid_infill_speed.get_abs_value(vol_speed); + } else if (path.role() == erTopSolidInfill) { + speed = m_config.top_solid_infill_speed.get_abs_value(vol_speed); + } + if (speed == 0) { + speed = vol_speed; + } + } + if (speed == 0) // this code shouldn't trigger as if it's 0, you have to get a m_volumetric_speed + speed = m_config.max_print_speed.value; + if (this->on_first_layer()) { + const double base_speed = speed; + if (path.role() == erInternalInfill || path.role() == erSolidInfill) { + double first_layer_infill_speed = m_config.first_layer_infill_speed.get_abs_value(base_speed); + if (first_layer_infill_speed > 0) + speed = std::min(first_layer_infill_speed, speed); + } else { + double first_layer_speed = m_config.first_layer_speed.get_abs_value(base_speed); + if (first_layer_speed > 0) + speed = std::min(first_layer_speed, speed); + } + double first_layer_min_speed = m_config.first_layer_min_speed.get_abs_value(base_speed); + speed = std::max(first_layer_min_speed, speed); + } + // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) + if (m_config.max_volumetric_speed.value > 0 && path.mm3_per_mm > 0) { + speed = std::min(m_config.max_volumetric_speed.value / path.mm3_per_mm, speed); + } + double filament_max_volumetric_speed = EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_volumetric_speed, 0); + if (filament_max_volumetric_speed > 0) { + speed = std::min(filament_max_volumetric_speed / path.mm3_per_mm, speed); + } + double filament_max_speed = EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_speed, 0); + if (filament_max_speed > 0) { + speed = std::min(filament_max_speed, speed); + } + + return speed; +} + std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string &description_in, double speed) { std::string gcode; std::string description{ description_in }; @@ -3774,7 +3889,7 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string coordf_t length = poly_start.length(); if (length > SCALED_EPSILON) { Polyline poly_end; - coordf_t min_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.5)) * 20; + coordf_t min_length = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.5)) * 20; if (poly_start.size() > 2 && length > min_length * 3) { //if complex travel, try to deccelerate only at the end, unless it's less than ~ 20 nozzle if (poly_start.lines().back().length() < min_length) { @@ -3813,92 +3928,7 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string // compensate retraction gcode += this->unretract(); - // set speed - if (speed < 0) { - //if speed == -1, then it's means "choose yourself, but if it's -1 < speed <0 , then it's a scaling from small_periemter. - //it's a bit hacky, so if you want to rework it, help yourself. - float factor = (-speed); - if (path.role() == erPerimeter) { - speed = m_config.get_abs_value("perimeter_speed"); - } else if (path.role() == erExternalPerimeter) { - speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role() == erBridgeInfill) { - speed = m_config.get_abs_value("bridge_speed"); - } else if (path.role() == erInternalBridgeInfill) { - speed = m_config.get_abs_value("bridge_speed_internal"); - } else if (path.role() == erOverhangPerimeter) { - speed = m_config.get_abs_value("overhangs_speed"); - } else if (path.role() == erInternalInfill) { - speed = m_config.get_abs_value("infill_speed"); - } else if (path.role() == erSolidInfill) { - speed = m_config.get_abs_value("solid_infill_speed"); - } else if (path.role() == erTopSolidInfill) { - speed = m_config.get_abs_value("top_solid_infill_speed"); - } else if (path.role() == erThinWall) { - speed = m_config.get_abs_value("thin_walls_speed"); - } else if (path.role() == erGapFill) { - speed = m_config.get_abs_value("gap_fill_speed"); - } else if (path.role() == erIroning) { - speed = m_config.get_abs_value("ironing_speed"); - } else if (path.role() == erNone) { - speed = m_config.get_abs_value("travel_speed"); - } else if (path.role() == erMilling) { - speed = m_config.get_abs_value("milling_speed"); - } else { - throw Slic3r::InvalidArgument("Invalid speed"); - } - //don't modify bridge speed - if (factor < 1 && !(is_bridge(path.role()))) { - float small_speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); - //apply factor between feature speed and small speed - speed = (speed * factor) + double((1.f - factor) * small_speed); - } - } - if (m_volumetric_speed != 0. && speed == 0) { - //if m_volumetric_speed, use the max size for thinwall & gapfill, to avoid variations - double vol_speed = m_volumetric_speed / path.mm3_per_mm; - if (vol_speed > m_config.max_print_speed.value) - vol_speed = m_config.max_print_speed.value; - // if using a % of an auto speed, use the % over the volumetric speed. - if (path.role() == erExternalPerimeter) { - speed = m_config.get_abs_value("external_perimeter_speed", vol_speed); - } else if (path.role() == erInternalBridgeInfill) { - speed = m_config.get_abs_value("bridge_speed_internal", vol_speed); - } else if (path.role() == erOverhangPerimeter) { - speed = m_config.get_abs_value("overhangs_speed", vol_speed); - } else if (path.role() == erSolidInfill) { - speed = m_config.get_abs_value("solid_infill_speed", vol_speed); - } else if (path.role() == erTopSolidInfill) { - speed = m_config.get_abs_value("top_solid_infill_speed", vol_speed); - } - if(speed == 0){ - speed = vol_speed; - } - } - if (speed == 0) // this code shouldn't trigger as if it's 0, you have to get a m_volumetric_speed - speed = m_config.max_print_speed.value; - if (this->on_first_layer()) - if (path.role() == erInternalInfill || path.role() == erSolidInfill) { - double first_layer_infill_speed = m_config.get_abs_value("first_layer_infill_speed", speed); - if(first_layer_infill_speed > 0) - speed = std::min(first_layer_infill_speed, speed); - } else { - double first_layer_speed = m_config.get_abs_value("first_layer_speed", speed); - if (first_layer_speed > 0) - speed = std::min(first_layer_speed, speed); - } - // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) - if (m_config.max_volumetric_speed.value > 0 && path.mm3_per_mm > 0) { - speed = std::min(m_config.max_volumetric_speed.value / path.mm3_per_mm, speed); - } - double filament_max_volumetric_speed = EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_volumetric_speed, 0); - if (filament_max_volumetric_speed > 0) { - speed = std::min(filament_max_volumetric_speed / path.mm3_per_mm, speed); - } - double filament_max_speed = EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_speed, 0); - if (filament_max_speed > 0) { - speed = std::min(filament_max_speed, speed); - } + speed = _compute_speed_mm_per_sec(path, speed); double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line @@ -3965,7 +3995,9 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string std::string comment; if (m_enable_cooling_markers) { - if (is_bridge(path.role())) + if(path.role() == erInternalBridgeInfill) + gcode += ";_BRIDGE_INTERNAL_FAN_START\n"; + else if (is_bridge(path.role())) gcode += ";_BRIDGE_FAN_START\n"; else if (ExtrusionRole::erTopSolidInfill == path.role()) gcode += ";_TOP_FAN_START\n"; @@ -3984,7 +4016,9 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string std::string GCode::_after_extrude(const ExtrusionPath &path) { std::string gcode; if (m_enable_cooling_markers) - if (is_bridge(path.role())) + if (path.role() == erInternalBridgeInfill) + gcode += ";_BRIDGE_INTERNAL_FAN_END\n"; + else if (is_bridge(path.role())) gcode += ";_BRIDGE_FAN_END\n"; else if (ExtrusionRole::erTopSolidInfill == path.role()) gcode += ";_TOP_FAN_END\n"; @@ -4274,9 +4308,9 @@ Vec2d GCode::point_to_gcode(const Point &point) const // convert a model-space scaled point into G-code coordinates Vec3d GCode::point_to_gcode(const Point &point, const coord_t z_offset) const { Vec2d extruder_offset = EXTRUDER_CONFIG_WITH_DEFAULT(extruder_offset, Vec2d(0, 0)); //FIXME : mill ofsset - Vec3d ret_vec(unscale_(point.x()) + m_origin.x() - extruder_offset.x(), - unscale_(point.y()) + m_origin.y() - extruder_offset.y(), - unscale_(z_offset)); + Vec3d ret_vec(unscaled(point.x()) + m_origin.x() - extruder_offset.x(), + unscaled(point.y()) + m_origin.y() - extruder_offset.y(), + unscaled(z_offset)); return ret_vec; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index a116d5dfe..a5bfb881e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -23,6 +23,7 @@ #include <memory> #include <map> #include <string> +#include <chrono> #ifdef HAS_PRESSURE_EQUALIZER #include "GCode/PressureEqualizer.hpp" @@ -342,7 +343,7 @@ private: AvoidCrossingPerimeters m_avoid_crossing_perimeters; bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends - // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END + // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END, _BRIDGE_INTERNAL_FAN_START, _BRIDGE_INTERNAL_FAN_END // Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module. bool m_enable_cooling_markers; // Markers for the Pressure Equalizer to recognize the extrusion type. @@ -409,6 +410,9 @@ private: bool m_silent_time_estimator_enabled; + //for gui status update + std::chrono::time_point<std::chrono::system_clock> m_last_status_update; + // Processor GCodeProcessor m_processor; @@ -430,6 +434,7 @@ private: std::string _extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); std::string _before_extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); + double_t _compute_speed_mm_per_sec(const ExtrusionPath& path, double speed = -1); std::string _after_extrude(const ExtrusionPath &path); void print_machine_envelope(FILE *file, Print &print); void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, uint16_t first_printing_extruder_id, bool wait); diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index f9abd92d0..0a3a3eabb 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -483,7 +483,7 @@ static bool need_wipe(const GCode &gcodegen, // called by get_perimeter_spacing() / get_perimeter_spacing_external() static inline float get_default_perimeter_spacing(const PrintObject &print_object) { - std::vector<uint16_t> printing_extruders = print_object.object_extruders(); + std::set<uint16_t> printing_extruders = print_object.object_extruders(); assert(!printing_extruders.empty()); float avg_extruder = 0; for(uint16_t extruder_id : printing_extruders) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index b96fee5df..1cd36d8a3 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -34,22 +34,24 @@ void CoolingBuffer::reset() struct CoolingLine { - enum Type { + enum Type : uint32_t { TYPE_SET_TOOL = 1 << 0, TYPE_EXTRUDE_END = 1 << 1, TYPE_BRIDGE_FAN_START = 1 << 2, TYPE_BRIDGE_FAN_END = 1 << 3, - TYPE_TOP_FAN_START = 1 << 4, - TYPE_TOP_FAN_END = 1 << 5, - TYPE_G0 = 1 << 6, - TYPE_G1 = 1 << 7, - TYPE_ADJUSTABLE = 1 << 8, - TYPE_EXTERNAL_PERIMETER = 1 << 9, + TYPE_BRIDGE_INTERNAL_FAN_START = 1 << 4, + TYPE_BRIDGE_INTERNAL_FAN_END = 1 << 5, + TYPE_TOP_FAN_START = 1 << 6, + TYPE_TOP_FAN_END = 1 << 7, + TYPE_G0 = 1 << 8, + TYPE_G1 = 1 << 9, + TYPE_ADJUSTABLE = 1 << 10, + TYPE_EXTERNAL_PERIMETER = 1 << 11, // The line sets a feedrate. - TYPE_HAS_F = 1 << 10, - TYPE_WIPE = 1 << 11, - TYPE_G4 = 1 << 12, - TYPE_G92 = 1 << 13, + TYPE_HAS_F = 1 << 12, + TYPE_WIPE = 1 << 13, + TYPE_G4 = 1 << 14, + TYPE_G92 = 1 << 15, }; CoolingLine(unsigned int type, size_t line_start, size_t line_end) : @@ -487,6 +489,10 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_BRIDGE_FAN_START; } else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) { line.type = CoolingLine::TYPE_BRIDGE_FAN_END; + } else if (boost::starts_with(sline, ";_BRIDGE_INTERNAL_FAN_START")) { + line.type = CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START; + } else if (boost::starts_with(sline, ";_BRIDGE_INTERNAL_FAN_END")) { + line.type = CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_END; } else if (boost::starts_with(sline, ";_TOP_FAN_START")) { line.type = CoolingLine::TYPE_TOP_FAN_START; } else if (boost::starts_with(sline, ";_TOP_FAN_END")) { @@ -735,22 +741,27 @@ std::string CoolingBuffer::apply_layer_cooldown( int fan_speed = -1; bool bridge_fan_control = false; int bridge_fan_speed = 0; + bool bridge_internal_fan_control = false; + int bridge_internal_fan_speed = 0; bool top_fan_control = false; int top_fan_speed = 0; bool ext_peri_fan_control = false; int ext_peri_fan_speed = 0; - auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed, &top_fan_control, &top_fan_speed, &ext_peri_fan_control, &ext_peri_fan_speed]() { + auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed, &bridge_internal_fan_control, &bridge_internal_fan_speed, &top_fan_control, &top_fan_speed, &ext_peri_fan_control, &ext_peri_fan_speed]() { const FullPrintConfig &config = m_gcodegen.config(); #define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder) int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed); + bridge_internal_fan_speed = EXTRUDER_CONFIG(bridge_internal_fan_speed); top_fan_speed = EXTRUDER_CONFIG(top_fan_speed); ext_peri_fan_speed = EXTRUDER_CONFIG(external_perimeter_fan_speed); // 0 is deprecated for disable: take care of temp settings. if (bridge_fan_speed == 0) bridge_fan_speed = -1; + if (bridge_internal_fan_speed == 0) bridge_internal_fan_speed = -1; if (ext_peri_fan_speed == 0) ext_peri_fan_speed = -1; if (top_fan_speed == 0) top_fan_speed = -1; if (bridge_fan_speed == 1) bridge_fan_speed = 0; + if (bridge_internal_fan_speed == 1) bridge_internal_fan_speed = 0; if (ext_peri_fan_speed == 1) ext_peri_fan_speed = 0; if (top_fan_speed == 1) top_fan_speed = 0; // end deprecation @@ -771,6 +782,8 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5); if (bridge_fan_speed >= 0 && bridge_fan_speed < max_fan_speed) bridge_fan_speed = int(floor(t * bridge_fan_speed + (1. - t) * max_fan_speed) + 0.5); + if (bridge_internal_fan_speed >= 0 && bridge_internal_fan_speed < max_fan_speed) + bridge_internal_fan_speed = int(floor(t * bridge_internal_fan_speed + (1. - t) * max_fan_speed) + 0.5); if (top_fan_speed >= 0 && top_fan_speed < max_fan_speed) top_fan_speed = int(floor(t * top_fan_speed + (1. - t) * max_fan_speed) + 0.5); if (ext_peri_fan_speed >= 0 && ext_peri_fan_speed < max_fan_speed) @@ -787,16 +800,27 @@ std::string CoolingBuffer::apply_layer_cooldown( // Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer. float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); fan_speed_new = clamp(0, 255, int(float(fan_speed_new ) * factor + 0.5f)); - if(bridge_fan_speed >= 0) + if (bridge_fan_speed >= 0) bridge_fan_speed = clamp(0, 255, int(float(bridge_fan_speed) * factor + 0.5f)); + if (bridge_internal_fan_speed >= 0) + bridge_internal_fan_speed = clamp(0, 255, int(float(bridge_internal_fan_speed) * factor + 0.5f)); } #undef EXTRUDER_CONFIG bridge_fan_control = bridge_fan_speed > fan_speed_new && bridge_fan_speed >= 0; + bridge_internal_fan_control = bridge_internal_fan_speed > fan_speed_new && bridge_internal_fan_speed >= 0; top_fan_control = top_fan_speed != fan_speed_new && top_fan_speed >= 0; ext_peri_fan_control = ext_peri_fan_speed != fan_speed_new && ext_peri_fan_speed >= 0; + // if bridge_internal_fan is disabled, it takes teh value of bridge_fan_control + if (!bridge_internal_fan_control && bridge_fan_control) { + bridge_internal_fan_control = true; + bridge_internal_fan_speed = bridge_fan_speed; + } + } else { bridge_fan_control = false; bridge_fan_speed = 0; + bridge_internal_fan_control = false; + bridge_internal_fan_speed = 0; top_fan_control = false; top_fan_speed = 0; ext_peri_fan_control = false; @@ -837,6 +861,16 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_need_set = true; current_fan_sections.erase(CoolingLine::TYPE_BRIDGE_FAN_START); } + } else if (line->type & CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START) { + if (bridge_internal_fan_control && current_fan_sections.find(CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START) == current_fan_sections.end()) { + fan_need_set = true; + current_fan_sections.insert(CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START); + } + } else if (line->type & CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_END) { + if (bridge_internal_fan_control || current_fan_sections.find(CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START) != current_fan_sections.end()) { + fan_need_set = true; + current_fan_sections.erase(CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START); + } } else if (line->type & CoolingLine::TYPE_TOP_FAN_START) { if (top_fan_control && current_fan_sections.find(CoolingLine::TYPE_TOP_FAN_START) == current_fan_sections.end()) { fan_need_set = true; @@ -931,8 +965,10 @@ std::string CoolingBuffer::apply_layer_cooldown( } if (fan_need_set) { //choose the speed with highest priority - if(current_fan_sections.find(CoolingLine::TYPE_BRIDGE_FAN_START) != current_fan_sections.end()) + if (current_fan_sections.find(CoolingLine::TYPE_BRIDGE_FAN_START) != current_fan_sections.end()) new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed); + else if (current_fan_sections.find(CoolingLine::TYPE_BRIDGE_INTERNAL_FAN_START) != current_fan_sections.end()) + new_gcode += m_gcodegen.writer().set_fan(bridge_internal_fan_speed); else if (current_fan_sections.find(CoolingLine::TYPE_TOP_FAN_START) != current_fan_sections.end()) new_gcode += m_gcodegen.writer().set_fan(top_fan_speed); else if (current_fan_sections.find(CoolingLine::TYPE_EXTERNAL_PERIMETER) != current_fan_sections.end()) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 51e6fbb7a..9dcd96975 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -581,7 +581,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) #endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING if (m_flavor != gcfMarlin) { - double time_estimation_compensation = config.get_abs_value("time_estimation_compensation"); + double time_estimation_compensation = config.get_computed_value("time_estimation_compensation"); for (auto& machine : this->m_time_processor.machines) { machine.time_acceleration = float(time_estimation_compensation); } @@ -678,7 +678,8 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } - if (m_flavor == gcfMarlin) { + const ConfigOptionEnum<MachineLimitsUsage>* machine_limits_usage = config.option<ConfigOptionEnum<MachineLimitsUsage>>("machine_limits_usage"); + if (machine_limits_usage && machine_limits_usage->value != MachineLimitsUsage::Ignore) { 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; @@ -772,7 +773,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } if (m_flavor != gcfMarlin) { - double time_estimation_compensation = config.get_abs_value("time_estimation_compensation"); + double time_estimation_compensation = config.get_computed_value("time_estimation_compensation"); for (auto& machine : this->m_time_processor.machines) { machine.time_acceleration = float(time_estimation_compensation); } @@ -940,7 +941,6 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr m_result.moves[i].layer_duration = 0; } #if ENABLE_GCODE_VIEWER_DATA_CHECKING - std::cout << "\n"; m_mm3_per_mm_compare.output(); m_height_compare.output(); m_width_compare.output(); @@ -1050,7 +1050,6 @@ void GCodeProcessor::process_klipper_ACTIVATE_EXTRUDER(const GCodeReader::GCodeL void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { -/* std::cout << line.raw() << std::endl; */ // update start position m_start_position = m_end_position; @@ -1078,6 +1077,8 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) { case 0: { process_G0(line); break; } // Move case 1: { process_G1(line); break; } // Move + case 2: { process_G2_G3(line, false); break; } // Move + case 3: { process_G2_G3(line, true); break; } // Move case 10: { process_G10(line); break; } // Retract case 11: { process_G11(line); break; } // Unretract case 20: { process_G20(line); break; } // Set Units to Inches @@ -1876,6 +1877,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f) type = EMoveType::Travel; + float height_saved = -1; if (type == EMoveType::Extrude) { double delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); #if !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING @@ -1935,9 +1937,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // cross section: rectangle + 2 semicircles m_width = float(delta_pos[E] * (M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + (1.0 - 0.25 * M_PI) * m_height); - // if teh value seems wrong, fall back to circular extrusion from flow + // if the value seems wrong, fall back to circular extrusion from flow if (m_width > m_height * 10 || m_width < m_height) { m_width = 2 * std::sqrt(m_mm3_per_mm / float(PI)); + height_saved = m_height; m_height = m_width; } } @@ -1952,7 +1955,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // time estimate section auto move_length = [](const AxisCoords& delta_pos) { - float sq_xyz_length = (float)sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]); + float sq_xyz_length = (float)sqr(delta_pos[X]) + (float)sqr(delta_pos[Y]) + (float)sqr(delta_pos[Z]); return (sq_xyz_length > 0.0f) ? std::sqrt(sq_xyz_length) : std::abs(delta_pos[E]); }; @@ -2114,6 +2117,123 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // store move store_move_vertex(type); + + //restore + if(height_saved > 0) + m_height = height_saved; +} + +void GCodeProcessor::emit_G1_from_G2(Vec2d dest, float e, float f) { + GCodeReader::FakeGCodeLine line_fake; + line_fake.set_x(dest.x()); + line_fake.set_y(dest.y()); + //line_fake.set_z(dest.z()); + line_fake.set_e(e); + if( f > 0) + line_fake.set_f(f); + process_G1(line_fake); +} + +void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool direct) +{ + //check it has everything + float i = 0, j = 0; + bool has_i = line.has_value('I', i); + bool has_j = line.has_value('J', j); + if(!((line.has_x() || line.has_y()) && (has_i || has_j))) + return; + //compute points + // compute mult factor + float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; + Vec2d p_start = { m_end_position[Axis::X], m_end_position[Axis::Y] }; + Vec2d p_end = { line.x() * lengthsScaleFactor, line.y() * lengthsScaleFactor }; + Vec2d p_center = { i * lengthsScaleFactor, j * lengthsScaleFactor }; + p_center += p_start; + // if relative positioning + if ((m_global_positioning_type == EPositioningType::Relative)) { + Vec2d p_to_add = { m_start_position[Axis::X], m_start_position[Axis::Y] }; + p_end += p_to_add; + p_center += p_to_add; + } + // get missing values + if (!line.has_x()) + p_end.x() = p_start.x(); + if (!line.has_y()) + p_end.y() = p_start.y(); + if (!has_i) + p_center.x() = p_start.x(); + if (!has_j) + p_center.y() = p_start.y(); + + //compute angles + double min_dist = m_width == 0 ? 1 : m_width * 4; + const double pi2 = 2 * PI; + const double radius = (p_start - p_center).norm(); + const double radius2 = (p_end - p_center).norm(); + if (std::abs(radius - radius2) > min_dist*0.1) { + BOOST_LOG_TRIVIAL(error) << "error, radius from start & end are too different in command '" << line.raw() << "'."; + return; + } + Vec2d p_start_rel = p_start - p_center; + Vec2d p_end_rel = p_end - p_center; + const double a1 = atan2(p_start_rel.y(), p_start_rel.x()); + const double a2 = atan2(p_end_rel.y(), p_end_rel.x()); + double adiff = a2 - a1; + //if (a1 < 0) + // a1 += pi2; + //if (a2 < 0) + // a2 += pi2; + if (adiff > pi2) + adiff -= pi2; + if (adiff < -pi2) + adiff += pi2; + //check order + if (direct) { + if (adiff < 0) + adiff += pi2; + } else { + if (adiff > 0) + adiff -= pi2; + } + double distance = std::abs(adiff * radius); + //get E + float dE = 0; + float start_e = m_start_position[E]; + bool e_relative = true; + if (line.has_e()) { + e_relative = (m_e_local_positioning_type == EPositioningType::Relative); + double ret = line.e() * lengthsScaleFactor; +#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING + if (m_use_volumetric_e) { + 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; + double area_filament_cross_section = M_PI * sqr(filament_radius); + ret /= area_filament_cross_section; + } +#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING + dE = e_relative ? ret : m_origin[E] + ret - start_e; + } + + //compute how much sections we need (~1 per 4 * width/nozzle) + int nb_sections = std::min(30, 1 + int(distance / min_dist)); + Vec2d p_current = p_start; + float angle_incr = adiff / nb_sections; + float dE_incr = dE / nb_sections; + float current_angle = a1; + //create smaller sections + for (int i = 1; i < nb_sections;i++) { + current_angle += angle_incr; + p_current = { std::cos(current_angle) * radius, std::sin(current_angle) * radius }; + p_current += p_center; + emit_G1_from_G2(p_current, e_relative ? dE_incr : start_e + i * dE_incr, line.has_f() ? line.f() : -1); + // update start position for next fake G1 + m_start_position[X] = p_current.x(); + m_start_position[Y] = p_current.y(); + m_start_position[E] += dE_incr; + } + //emit last + emit_G1_from_G2(p_end, e_relative ? dE_incr : start_e + dE, line.has_f() ? line.f() : -1); + } void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) @@ -2375,6 +2495,7 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) { + // Note: Sprinter / Marlin gcode for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { @@ -2460,6 +2581,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line) { + // RepRapFirmware gcode 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_jerk_x, i, line.x() * MMMIN_TO_MMSEC); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 8c1dfaf7e..d672fc5ea 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -507,6 +507,8 @@ namespace Slic3r { // Move void process_G0(const GCodeReader::GCodeLine& line); void process_G1(const GCodeReader::GCodeLine& line); + void process_G2_G3(const GCodeReader::GCodeLine& line, bool direct); + void emit_G1_from_G2(Vec2d dest, float de, float f); // Retract void process_G10(const GCodeReader::GCodeLine& line); diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index d90a23160..cb20e2642 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -72,6 +72,14 @@ public: uint32_t m_mask; friend class GCodeReader; }; + class FakeGCodeLine : public GCodeLine { + public: + void set_x(float x) { m_axis[X] = x; m_mask = (m_mask | (1 << int(X))); } + void set_y(float y) { m_axis[Y] = y; m_mask = (m_mask | (1 << int(Y))); } + void set_z(float z) { m_axis[Z] = z; m_mask = (m_mask | (1 << int(Z))); } + void set_e(float e) { m_axis[E] = e; m_mask = (m_mask | (1 << int(E))); } + void set_f(float f) { m_axis[F] = f; m_mask = (m_mask | (1 << int(F))); } + }; typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index e9ababa33..2211e521a 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -27,7 +27,7 @@ std::string to_string_nozero(double value, int32_t max_precision) { //first, get the int part, to see how many digit it takes int long10 = 0; if (intpart > 9) - long10 = std::floor(std::log10(std::abs(intpart))); + long10 = (int)std::floor(std::log10(std::abs(intpart))); //set the usable precision: there is only 15-16 decimal digit in a double ss << std::fixed << std::setprecision(int(std::min(15 - long10, int(max_precision)))) << value; std::string ret = ss.str(); diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 9c2c9a21f..22a26950a 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -191,10 +191,14 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge, Lines &lines, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > &thickness) { // prevent overflows and detect almost-infinite edges - if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || - std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) || + if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || + std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) || std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || - std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED)) + std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) || + std::isnan(edge->vertex0()->x()) || + std::isnan(edge->vertex0()->y()) || + std::isnan(edge->vertex1()->x()) || + std::isnan(edge->vertex1()->y()) ) return false; // construct the line representing this edge of the Voronoi diagram @@ -350,9 +354,9 @@ remove_point_too_near(ThickPolyline* to_reduce) void add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify) { - const double to_modify_length = to_modify->length(); + const coordf_t to_modify_length = to_modify->length(); const double percent_epsilon = SCALED_EPSILON / to_modify_length; - const double pattern_length = pattern->length(); + const coordf_t pattern_length = pattern->length(); double percent_length = 0; for (size_t idx_point = 1; idx_point < pattern->points.size() - 1; ++idx_point) { @@ -390,10 +394,10 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify) /// return 1 for an angle of 90° and 0 for an angle of 0° or 180° double get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) { - double nearest_dist = point.distance_to(contour.contour.points.front()); + coordf_t nearest_dist = point.distance_to(contour.contour.points.front()); Point point_nearest = contour.contour.points.front(); size_t id_nearest = 0; - double near_dist = nearest_dist; + coordf_t near_dist = nearest_dist; Point point_near = point_nearest; size_t id_near = 0; for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) { @@ -581,8 +585,8 @@ MedialAxis::remove_bits(ThickPolylines &pp) if (polyline.width.back() > 0) continue; //check my length is small - coord_t length = (coord_t)polyline.length(); - if (length > max_width*1.5) { + coordf_t length = polyline.length(); + if (length > coordf_t(max_width) * 1.5) { continue; } @@ -609,11 +613,11 @@ MedialAxis::remove_bits(ThickPolylines &pp) if (nb_better_than_me < 2) continue; //check if the length of the polyline is small vs width of the other lines - double max_width = 0; + coord_t max_width = 0; for (int i = 0; i < crosspoint.size(); i++) { max_width = std::max(max_width, pp[crosspoint[i]].width[0]); } - if (length > max_width + min_width) + if (length > coordf_t(max_width + min_width)) continue; //delete the now unused polyline @@ -819,10 +823,10 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con }*/ // find anchor Point best_anchor; - double shortest_dist = (double)max_width; + coordf_t shortest_dist = (coordf_t)max_width; for (const ExPolygon& a : anchors) { Point p_maybe_inside = a.contour.centroid(); - double test_dist = new_bound.distance_to(p_maybe_inside) + new_back.distance_to(p_maybe_inside); + coordf_t test_dist = new_bound.distance_to(p_maybe_inside) + new_back.distance_to(p_maybe_inside); //if (test_dist < max_width / 2 && (test_dist < shortest_dist || shortest_dist < 0)) { double angle_test = new_back.ccw_angle(p_maybe_inside, line.a); if (angle_test > PI) angle_test = 2 * PI - angle_test; @@ -1092,12 +1096,12 @@ MedialAxis::main_fusion(ThickPolylines& pp) polyline.width[idx_point] = max_width; //failsafe: try to not go out of the radius of the section, take the width of the merging point for that. (and with some offset) coord_t main_branch_width = pp[biggest_main_branch_id].width.front(); - coord_t main_branch_dist = pp[biggest_main_branch_id].points.front().distance_to(polyline.points[idx_point]); - coord_t max_width_from_main = std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist); + coordf_t main_branch_dist = pp[biggest_main_branch_id].points.front().distance_to(polyline.points[idx_point]); + coord_t max_width_from_main = (coord_t)std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist); if (find_main_branch && polyline.width[idx_point] > max_width_from_main) polyline.width[idx_point] = max_width_from_main; if (find_main_branch && polyline.width[idx_point] > pp[biggest_main_branch_id].width.front() * 1.1) - polyline.width[idx_point] = pp[biggest_main_branch_id].width.front() * 1.1; + polyline.width[idx_point] = coord_t(pp[biggest_main_branch_id].width.front() * 1.1); //std::cout << "main fusion, max dist : " << max_width_from_main << "\n"; ++idx_point; @@ -1377,7 +1381,7 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz while (changes) { changes = false; - double shortest_size = min_size; + coordf_t shortest_size = (coordf_t) min_size; size_t shortest_idx = -1; for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; @@ -1403,19 +1407,19 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz } void -MedialAxis::check_width(ThickPolylines& pp, double max_width, std::string msg) +MedialAxis::check_width(ThickPolylines& pp, coord_t max_width, std::string msg) { //remove empty polyline int nb = 0; for (size_t i = 0; i < pp.size(); ++i) { for (size_t j = 0; j < pp[i].width.size(); ++j) { - if (pp[i].width[j] > max_width * 1.01) { - std::cout << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(max_width) << "\n"; + if (pp[i].width[j] > coord_t(max_width * 1.01)) { + BOOST_LOG_TRIVIAL(error) << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(max_width) << "\n"; nb++; } } } - if (nb > 0) std::cout << "== nbBig = " << nb << " ==\n"; + if (nb > 0) BOOST_LOG_TRIVIAL(error) << "== nbBig = " << nb << " ==\n"; } void @@ -1428,7 +1432,7 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp) for (ThickPolyline& polyline : pp) { for (ThickLine &l : polyline.thicklines()) { surface += l.length() * (l.a_width + l.b_width) / 2; - double width_mean = (l.a_width + l.b_width) / 2; + coord_t width_mean = (l.a_width + l.b_width) / 2; volume += height * (width_mean - height * (1. - 0.25 * PI)) * l.length(); } } @@ -1449,8 +1453,8 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp) //reduce width double reduce_by = boundsVolume / volume; for (ThickPolyline& polyline : pp) { - for (coordf_t &width : polyline.width) { - width *= reduce_by; + for (coord_t &width : polyline.width) { + width = coord_t( double(width) * reduce_by); } } } @@ -1511,9 +1515,10 @@ MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchor //compute the min width coord_t min_width = this->nozzle_diameter; if (this->height > 0) min_width = Flow::new_from_spacing( - float(unscale_(this->nozzle_diameter)), - float(unscale_(this->nozzle_diameter)), - float(unscale_(this->height)), false).scaled_width(); + float(unscaled(this->nozzle_diameter)), + float(unscaled(this->nozzle_diameter)), + float(unscaled(this->height)), + false).scaled_width(); //ensure the width is not lower than min_width. for (ThickPolyline& polyline : pp) { for (int i = 0; i < polyline.points.size(); ++i) { @@ -1534,8 +1539,8 @@ void MedialAxis::taper_ends(ThickPolylines& pp) { // minimum size of the taper: be sure to extrude at least the "round edges" of the extrusion (0-spacing extrusion). - const coord_t min_size = std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI)); - const coordf_t length = std::min(this->taper_size, (this->nozzle_diameter - min_size) / 2); + const coord_t min_size = (coord_t) std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI)); + const coordf_t length = (coordf_t) std::min(this->taper_size, (this->nozzle_diameter - min_size) / 2); if (length <= SCALED_RESOLUTION) return; //ensure the width is not lower than min_size. for (ThickPolyline& polyline : pp) { @@ -1589,9 +1594,9 @@ check_circular(ExPolygon& expolygon, coord_t max_variation) { if (expolygon.contour.concave_points().empty() && expolygon.contour.points.size() > 3) { // Computing circle center Point center = expolygon.contour.centroid(); - double radius_min = std::numeric_limits<float>::max(), radius_max = 0; + coordf_t radius_min = std::numeric_limits<float>::max(), radius_max = 0; for (int i = 0; i < expolygon.contour.points.size(); ++i) { - double dist = expolygon.contour.points[i].distance_to(center); + coordf_t dist = expolygon.contour.points[i].distance_to(center); radius_min = std::min(radius_min, dist); radius_max = std::max(radius_max, dist); } @@ -1630,7 +1635,7 @@ MedialAxis::build(ThickPolylines &polylines_out) if (this->expolygon.area() < this->min_width * this->min_width) return; //check for circular shape - double radius = check_circular(this->expolygon, this->min_width/4); + coordf_t radius = check_circular(this->expolygon, this->min_width/4); if (radius > 0 && this->expolygon.contour.points.size() > 4) { ExPolygons miniPeri = offset_ex(this->expolygon.contour, -radius / 2); if (miniPeri.size() == 1 && miniPeri[0].holes.size() == 0) { @@ -1738,9 +1743,9 @@ MedialAxis::build(ThickPolylines &polylines_out) /* Find the maximum width returned; we're going to use this for validating and filtering the output segments. */ - double max_w = 0; + coord_t max_w = 0; for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it) - max_w = std::max(max_w, *std::max_element(it->width.begin(), it->width.end())); + max_w = std::max(max_w, (coord_t)*std::max_element(it->width.begin(), it->width.end())); //for (auto &p : pp) { // std::cout << "Start polyline : "; @@ -1843,7 +1848,7 @@ MedialAxis::build(ThickPolylines &polylines_out) // svg.Close(); //} - remove_too_short_polylines(pp, (coord_t)max_w * 2); + remove_too_short_polylines(pp, max_w * 2); //{ // std::stringstream stri; // stri << "medial_axis_8_tooshort_" << id << ".svg"; @@ -1903,7 +1908,7 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl // this value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance - const double tolerance = 4*SCALED_RESOLUTION;//scale_(0.05); + const coord_t tolerance = 4 * SCALED_RESOLUTION;//scale_(0.05); ExtrusionEntityCollection coll; for (const ThickPolyline &p : polylines) { @@ -1919,16 +1924,16 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl assert(line.a_width >= 0); assert(line.b_width >= 0); - double thickness_delta = fabs(line.a_width - line.b_width); - if (thickness_delta > tolerance && ceil(thickness_delta / tolerance) > 2) { - const uint16_t segments = 1+(uint16_t) std::min(16000.0, ceil(thickness_delta / tolerance)); + coord_t thickness_delta = std::abs(line.a_width - line.b_width); + if (thickness_delta > tolerance && ceil(float(thickness_delta) / float(tolerance)) > 2) { + const uint16_t segments = 1 + (uint16_t) std::min((uint32_t)16000, (uint32_t)ceil(float(thickness_delta) / float(tolerance))); Points pp; std::vector<coordf_t> width; { for (size_t j = 0; j < segments; ++j) { pp.push_back(line.a.interpolate(((double)j) / segments, line.b)); double percent_width = ((double)j) / (segments-1); - width.push_back(line.a_width*(1 - percent_width) + line.b_width*percent_width); + width.push_back(line.a_width * (1 - percent_width) + line.b_width * percent_width); } pp.push_back(line.b); diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp index bc208e3f5..69bd94268 100644 --- a/src/libslic3r/MedialAxis.hpp +++ b/src/libslic3r/MedialAxis.hpp @@ -109,7 +109,7 @@ class MedialAxis { /// taper the ends of polylines (don't activate that for gapfill) void taper_ends(ThickPolylines& pp); //cleaning method - void check_width(ThickPolylines& pp, double max_width, std::string msg); + void check_width(ThickPolylines& pp, coord_t max_width, std::string msg); //removing small extrusion that won't be useful and will harm print. A bit like fusion_corners but more lenient and with just del. void remove_bits(ThickPolylines& pp); }; diff --git a/src/libslic3r/Milling/MillingPostProcess.cpp b/src/libslic3r/Milling/MillingPostProcess.cpp index 4e40ce553..50147de21 100644 --- a/src/libslic3r/Milling/MillingPostProcess.cpp +++ b/src/libslic3r/Milling/MillingPostProcess.cpp @@ -8,7 +8,7 @@ namespace Slic3r { void MillingPostProcess::getExtrusionLoop(const Layer* layer, Polygon& poly, Polylines& entrypoints, ExtrusionEntityCollection& out_coll) { - const double milling_diameter = scale_(this->print_config->milling_diameter.get_at(0)); + const coord_t milling_diameter = scale_t(this->print_config->milling_diameter.get_at(0)); //get the longest polyline Polyline best_polyline; @@ -22,8 +22,8 @@ namespace Slic3r { //get two pair of points that are at less than max_dist. int32_t first_point_extract_idx = 1; int32_t first_point_idx = -1; - const double dist_max_square = milling_diameter * milling_diameter / 4; - double best_dist = dist_max_square; + const coordf_t dist_max_square = coordf_t(milling_diameter) * coordf_t(milling_diameter / 4); + coordf_t best_dist = dist_max_square; for (int32_t idx = 0; idx < poly.points.size(); idx++) { if (poly.points[idx].distance_to_square(best_polyline.points[first_point_extract_idx]) < best_dist) { best_dist = poly.points[idx].distance_to_square(best_polyline.points[first_point_extract_idx]); @@ -91,7 +91,7 @@ namespace Slic3r { if (contour.polyline.points.size() > 3) contour.polyline.points.push_back(contour.polyline.points[1]); contour.mm3_per_mm = 0; - contour.width = this->print_config->milling_diameter.get_at(0); + contour.width = (float)this->print_config->milling_diameter.get_at(0); contour.height = (float)layer->height; out_coll.append(std::move(contour)); return; @@ -102,18 +102,18 @@ namespace Slic3r { { if (!can_be_milled(layer)) return ExtrusionEntityCollection(); - const double milling_diameter = scale_(this->print_config->milling_diameter.get_at(0)); + const coord_t milling_diameter = scale_t(this->print_config->milling_diameter.get_at(0)); ExPolygons milling_lines; for (const Surface& surf : slices->surfaces) { - ExPolygons surf_milling = offset_ex(surf.expolygon, milling_diameter/2, ClipperLib::jtRound); + ExPolygons surf_milling = offset_ex(surf.expolygon, double(milling_diameter / 2), ClipperLib::jtRound); for (const ExPolygon& expoly : surf_milling) // expoly.simplify(SCALED_RESOLUTION, &milling_lines); // should already be done milling_lines.push_back(expoly); } milling_lines = union_ex(milling_lines); - ExPolygons secured_points = offset_ex(milling_lines, milling_diameter / 3); + ExPolygons secured_points = offset_ex(milling_lines, double(milling_diameter / 3)); ExPolygons entrypoints; for (const ExPolygon& expoly : secured_points) // expoly.simplify(SCALED_RESOLUTION, &entrypoints); // should already be done @@ -137,8 +137,11 @@ namespace Slic3r { } bool MillingPostProcess::can_be_milled(const Layer* layer) { + double max_first_layer = 0; + for (double diam : this->print_config->nozzle_diameter.values) + max_first_layer = std::max(max_first_layer, config->milling_after_z.get_abs_value(this->object_config->first_layer_height.get_abs_value(diam))); return !print_config->milling_diameter.values.empty() && config->milling_post_process - && layer->bottom_z() >= config->milling_after_z.get_abs_value(this->object_config->first_layer_height.get_abs_value(this->print_config->nozzle_diameter.values.front())); + && layer->bottom_z() >= max_first_layer; } ExPolygons MillingPostProcess::get_unmillable_areas(const Layer* layer) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 0255e278b..00f199e02 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -666,6 +666,85 @@ namespace client return opt; } + + // copy of ConfigBase::get_computed_value + double get_computed_value(const t_config_option_key& opt_key) const + { + // Get stored option value. + const ConfigOption* raw_opt = this->optptr(opt_key); + if (raw_opt == nullptr) { + std::stringstream ss; ss << "You can't define an option that need " << opt_key << " without defining it!"; + throw std::runtime_error(ss.str()); + } + + if (!raw_opt->is_vector()) { + if (raw_opt->type() == coFloat) + return static_cast<const ConfigOptionFloat*>(raw_opt)->value; + if (raw_opt->type() == coInt) + return static_cast<const ConfigOptionInt*>(raw_opt)->value; + if (raw_opt->type() == coBool) + return static_cast<const ConfigOptionBool*>(raw_opt)->value ? 1 : 0; + const ConfigOptionDef* opt_def = nullptr; + const ConfigOptionPercent* cast_opt = nullptr; + if (raw_opt->type() == coFloatOrPercent) { + if (!static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->percent) + return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->value; + // Get option definition. + opt_def = print_config_def.get(opt_key); + cast_opt = static_cast<const ConfigOptionFloatOrPercent*>(raw_opt); + assert(opt_def != nullptr); + } + if (raw_opt->type() == coPercent) { + // Get option definition. + opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + cast_opt = static_cast<const ConfigOptionPercent*>(raw_opt); + } + if (opt_def != nullptr) { + //if over no other key, it's most probably a simple % + if (opt_def->ratio_over == "") + return cast_opt->get_abs_value(1); + // Compute absolute value over the absolute value of the base option. + //FIXME there are some ratio_over chains, which end with empty ratio_with. + // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return cast_opt->get_abs_value(this->get_computed_value(opt_def->ratio_over)); + + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); + } + } else { + // check if it's an extruder_id array + const ConfigOptionVectorBase* vector_opt = static_cast<const ConfigOptionVectorBase*>(raw_opt); + if (vector_opt->is_extruder_size()) { + + if (raw_opt->type() == coFloats || raw_opt->type() == coInts || raw_opt->type() == coBools) + return vector_opt->getFloat(current_extruder_id); + if (raw_opt->type() == coFloatsOrPercents) { + const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast<const ConfigOptionFloatsOrPercents*>(raw_opt); + if (!opt_fl_per->values[current_extruder_id].percent) + return opt_fl_per->values[current_extruder_id].value; + + const ConfigOptionDef* opt_def = print_config_def.get(opt_key); + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return opt_fl_per->get_abs_value(current_extruder_id, this->get_computed_value(opt_def->ratio_over)); + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); + } + if (raw_opt->type() == coPercents) { + const ConfigOptionPercents* opt_per = static_cast<const ConfigOptionPercents*>(raw_opt); + const ConfigOptionDef* opt_def = print_config_def.get(opt_key); + if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") + return opt_per->get_abs_value(current_extruder_id, this->get_computed_value(opt_def->ratio_over)); + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has no valid ratio_over to compute of"; + throw ConfigurationError(ss.str()); + } + } + } + std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " has not a valid option type for get_abs_value()"; + throw ConfigurationError(ss.str()); + } + const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } template <typename Iterator> @@ -754,8 +833,13 @@ namespace client OptWithPos<Iterator> &opt, expr<Iterator> &output) { - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (opt.opt->is_vector()) { + const ConfigOptionDef* opt_def = print_config_def.get(opt_key); + if (!opt_def->is_vector_extruder) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + } + const ConfigOptionDef* opt_def; switch (opt.opt->type()) { case coFloat: output.set_d(opt.opt->getFloat()); break; case coInt: output.set_i(opt.opt->getInt()); break; @@ -765,7 +849,6 @@ namespace client case coBool: output.set_b(opt.opt->getBool()); break; case coFloatOrPercent: { - std::string opt_key(opt.it_range.begin(), opt.it_range.end()); if (boost::ends_with(opt_key, "extrusion_width")) { // Extrusion width use the first nozzle diameter output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast<unsigned int>(ctx->current_extruder_id))); @@ -774,7 +857,7 @@ namespace client output.set_d(opt.opt->getFloat()); } else { // Resolve dependencies using the "ratio_over" link to a parent value. - const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + opt_def = print_config_def.get(opt_key); assert(opt_def != nullptr); double v = opt.opt->getFloat() * 0.01; // percent to ratio for (;;) { @@ -787,20 +870,53 @@ namespace client v *= Flow::extrusion_width(opt_def->ratio_over, static_cast<const ConfigOptionFloatOrPercent*>(opt_parent), *ctx, static_cast<unsigned int>(ctx->current_extruder_id)); break; } - if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { - v *= opt_parent->getFloat(); - if (opt_parent->type() == coFloat || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_parent)->percent) - break; - v *= 0.01; // percent to ratio - } - // Continue one level up in the "ratio_over" hierarchy. - opt_def = print_config_def.get(opt_def->ratio_over); - assert(opt_def != nullptr); + double val = ctx->get_computed_value(opt_def->ratio_over); + v *= val; + break; + // if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + // v *= opt_parent->getFloat(); + // if (opt_parent->type() == coFloat || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_parent)->percent) + // break; + // v *= 0.01; // percent to ratio + //} + //// Continue one level up in the "ratio_over" hierarchy. + //opt_def = print_config_def.get(opt_def->ratio_over); + //assert(opt_def != nullptr); } output.set_d(v); } break; } + case coInts: + opt_def = print_config_def.get(opt_key); + if (opt_def->is_vector_extruder) { + output.set_i((int)((ConfigOptionVectorBase*)opt.opt)->getFloat(ctx->current_extruder_id)); + break; + } else + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + case coFloats: + case coPercents: + opt_def = print_config_def.get(opt_key); + if (opt_def->is_vector_extruder) { + output.set_d(((ConfigOptionVectorBase*)opt.opt)->getFloat(ctx->current_extruder_id)); + break; + } else + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + case coStrings: + opt_def = print_config_def.get(opt_key); + if (opt_def->is_vector_extruder) { + output.set_s(((ConfigOptionStrings*)opt.opt)->values[ctx->current_extruder_id]); + break; + } else + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + case coPoints: + opt_def = print_config_def.get(opt_key); + if (opt_def->is_vector_extruder) { + output.set_s(to_string(((ConfigOptionPoints*)opt.opt)->values[ctx->current_extruder_id])); + break; + }else + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + //TODO: coFloatOrPercents default: ctx->throw_exception("Unknown scalar variable type", opt.it_range); } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index b13317323..d3250484a 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -24,7 +24,9 @@ public: explicit Polyline(const Points &points) : MultiPoint(points) {} explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {} Polyline& operator=(const Polyline &other) { points = other.points; return *this; } - Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; } + Polyline& operator=(Polyline&& other) { points = std::move(other.points); return *this; } + bool operator==(const Polyline& other) const { return points == other.points; } + bool operator!=(const Polyline& other) const { return points != other.points; } static Polyline new_scale(const std::vector<Vec2d> &points) { Polyline pl; pl.points.reserve(points.size()); @@ -152,7 +154,7 @@ class ThickPolyline : public Polyline { public: enum StartPos : int8_t{tpspBegin = -1, tpspBoth = 0, tpspEnd = 1}; /// width size must be == point size - std::vector<coordf_t> width; + std::vector<coord_t> width; /// if true => it's an endpoint, if false it join an other ThickPolyline. first is at front(), second is at back() std::pair<bool, bool> endpoints; //if it's important to begin at a specific bit. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 482cdc43d..66ae0080f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -502,6 +502,7 @@ const std::vector<std::string>& Preset::print_options() // speeds "external_perimeter_speed", "first_layer_speed", + "first_layer_min_speed", "infill_speed", "perimeter_speed", "small_perimeter_speed", @@ -597,6 +598,7 @@ const std::vector<std::string>& Preset::print_options() "hole_size_threshold", "hole_to_polyhole", "hole_to_polyhole_threshold", + "hole_to_polyhole_twisted", "threads", // wipe tower "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", @@ -673,6 +675,7 @@ const std::vector<std::string>& Preset::filament_options() "min_fan_speed", "max_fan_speed", "bridge_fan_speed", + "bridge_internal_fan_speed", "top_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", @@ -683,7 +686,7 @@ const std::vector<std::string>& Preset::filament_options() "external_perimeter_fan_speed", // Retract overrides "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", - "filament_retract_layer_change", "filament_wipe", "filament_wipe_extra_perimeter", "filament_retract_before_wipe", + "filament_retract_layer_change", "filament_wipe", "filament_wipe_speed", "filament_wipe_extra_perimeter", "filament_retract_before_wipe", // Profile compatibility "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" //merill adds @@ -722,11 +725,14 @@ const std::vector<std::string>& Preset::printer_options() "fan_speedup_overhangs", "fan_speedup_time", "fan_percentage", - "gcode_flavor", + "gcode_filename_illegal_char", + "gcode_flavor", "gcode_precision_xyz", + "gcode_precision_e", "use_relative_e_distances", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "min_length", + "max_gcode_per_second", //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. "host_type", "print_host", "printhost_apikey", "printhost_cafile", "printhost_port", "single_extruder_multi_material", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6b5008893..767eae486 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -81,6 +81,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option "between_objects_gcode", "bridge_acceleration", "bridge_fan_speed", + "bridge_internal_fan_speed", "colorprint_heights", "complete_objects_sort", "cooling", @@ -117,15 +118,18 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option "first_layer_flow_ratio", "first_layer_speed", "first_layer_infill_speed", + "first_layer_min_speed", "full_fan_speed_layer", "gap_fill_speed", "gcode_comments", + "gcode_filename_illegal_char", "gcode_label_objects", "gcode_precision_xyz", "gcode_precision_e", "infill_acceleration", "layer_gcode", "max_fan_speed", + "max_gcode_per_second", "max_print_height", "max_print_speed", "max_volumetric_speed", @@ -180,6 +184,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option "use_volumetric_e", "variable_layer_height", "wipe", + "wipe_speed", "wipe_extra_perimeter" }; @@ -350,10 +355,9 @@ bool Print::is_step_done(PrintObjectStep step) const } // returns 0-based indices of used extruders -std::vector<uint16_t> Print::object_extruders(const PrintObjectPtrs &objects) const +std::set<uint16_t> Print::object_extruders(const PrintObjectPtrs &objects) const { - std::vector<uint16_t> extruders; - extruders.reserve(m_regions.size() * 3); + std::set<uint16_t> extruders; std::vector<unsigned char> region_used(m_regions.size(), false); for (const PrintObject *object : objects) for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes) @@ -362,14 +366,13 @@ std::vector<uint16_t> Print::object_extruders(const PrintObjectPtrs &objects) co for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region) if (region_used[idx_region]) m_regions[idx_region]->collect_object_printing_extruders(extruders); - sort_remove_duplicates(extruders); return extruders; } // returns 0-based indices of used extruders -std::vector<uint16_t> Print::support_material_extruders() const +std::set<uint16_t> Print::support_material_extruders() const { - std::vector<uint16_t> extruders; + std::set<uint16_t> extruders; bool support_uses_current_extruder = false; auto num_extruders = (uint16_t)m_config.nozzle_diameter.size(); @@ -380,7 +383,7 @@ std::vector<uint16_t> Print::support_material_extruders() const support_uses_current_extruder = true; else { uint16_t i = (uint16_t)object->config().support_material_extruder - 1; - extruders.emplace_back((i >= num_extruders) ? 0 : i); + extruders.insert((i >= num_extruders) ? 0 : i); } if (object->config().support_material_interface_layers > 0) { assert(object->config().support_material_interface_extruder >= 0); @@ -388,7 +391,7 @@ std::vector<uint16_t> Print::support_material_extruders() const support_uses_current_extruder = true; else { uint16_t i = (uint16_t)object->config().support_material_interface_extruder - 1; - extruders.emplace_back((i >= num_extruders) ? 0 : i); + extruders.insert((i >= num_extruders) ? 0 : i); } } } @@ -398,16 +401,14 @@ std::vector<uint16_t> Print::support_material_extruders() const // Add all object extruders to the support extruders as it is not know which one will be used to print supports. append(extruders, this->object_extruders(m_objects)); - sort_remove_duplicates(extruders); return extruders; } // returns 0-based indices of used extruders -std::vector<uint16_t> Print::extruders() const +std::set<uint16_t> Print::extruders() const { - std::vector<uint16_t> extruders = this->object_extruders(m_objects); + std::set<uint16_t> extruders = this->object_extruders(m_objects); append(extruders, this->support_material_extruders()); - sort_remove_duplicates(extruders); return extruders; } @@ -1279,6 +1280,8 @@ bool Print::has_skirt() const static inline bool sequential_print_horizontal_clearance_valid(const Print &print) { + if (print.config().extruder_clearance_radius == 0) + return true; Polygons convex_hulls_other; std::map<ObjectID, Polygon> map_model_object_to_convex_hull; const double dist_grow = PrintConfig::min_object_distance(&print.default_region_config()) * 2; @@ -1365,6 +1368,41 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print) return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value); } + +double Print::get_object_first_layer_height(const PrintObject& object) const { + std::set<uint16_t> object_extruders; + for (size_t region_id = 0; region_id < object.region_volumes.size(); ++region_id) { + if (object.region_volumes[region_id].empty()) continue; + const PrintRegion* region = this->regions()[region_id]; + PrintRegion::collect_object_printing_extruders(config(), object.config(), region->config(), object_extruders); + } + //get object first layer height + double object_first_layer_height = object.config().first_layer_height.value; + if (object.config().first_layer_height.percent) { + object_first_layer_height = 1000000000; + for (uint16_t extruder_id : object_extruders) { + double nozzle_diameter = config().nozzle_diameter.values[extruder_id]; + object_first_layer_height = std::min(object_first_layer_height, object.config().first_layer_height.get_abs_value(nozzle_diameter)); + } + } + return object_first_layer_height; +} + +double Print::get_first_layer_height() const +{ + if (m_objects.empty()) + throw Slic3r::InvalidArgument("first_layer_height() can't be called without PrintObjects"); + + double min_layer_height = 10000000000.; + for(PrintObject* obj : m_objects) + min_layer_height = std::fmin(min_layer_height, get_object_first_layer_height(*obj)); + + if(min_layer_height == 10000000000.) + throw Slic3r::InvalidArgument("first_layer_height() can't be computed"); + + return min_layer_height; +} + // Precondition: Print::validate() requires the Print::apply() to be called its invocation. std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const { @@ -1401,8 +1439,8 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments - double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front()); - double first_filament_diam = m_config.filament_diameter.get_at(extruders().front()); + double first_nozzle_diam = m_config.nozzle_diameter.get_at(*extruders().begin()); + double first_filament_diam = m_config.filament_diameter.get_at(*extruders().begin()); for (const auto& extruder_idx : extruders()) { double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); double filament_diam = m_config.filament_diameter.get_at(extruder_idx); @@ -1502,7 +1540,7 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const } { - std::vector<uint16_t> extruders = this->extruders(); + std::set<uint16_t> extruders = this->extruders(); // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits<double>::max(); @@ -1522,6 +1560,7 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const return L("One or more object were assigned an extruder that the printer does not have."); #endif + const double print_first_layer_height = get_first_layer_height(); for (PrintObject *object : m_objects) { if (object->config().raft_layers > 0 || object->config().support_material.value) { if ((object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) { @@ -1545,50 +1584,49 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const } } } - + + const double object_first_layer_height = get_object_first_layer_height(*object); // validate layer_height for each region for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) { if (object->region_volumes[region_id].empty()) continue; const PrintRegion* region = this->regions()[region_id]; - std::vector<uint16_t> object_extruders; + std::set<uint16_t> object_extruders; PrintRegion::collect_object_printing_extruders(config(), object->config(), region->config(), object_extruders); - //object->region_volumes[region_id].front().first.second < object->layers() double layer_height = object->config().layer_height.value; for (uint16_t extruder_id : object_extruders) { double min_layer_height = config().min_layer_height.values[extruder_id]; double max_layer_height = config().max_layer_height.values[extruder_id]; double nozzle_diameter = config().nozzle_diameter.values[extruder_id]; - double first_layer_height = object->config().first_layer_height.get_abs_value(nozzle_diameter); if (max_layer_height < EPSILON) max_layer_height = nozzle_diameter * 0.75; - + double skirt_width = Flow::new_from_config_width(frPerimeter, *Flow::extrusion_option("skirt_extrusion_width", m_default_region_config), (float)m_config.nozzle_diameter.get_at(extruder_id), print_first_layer_height).width; //check first layer - if (object->region_volumes[region_id].front().first.first < first_layer_height) { - if (first_layer_height + EPSILON < min_layer_height) - return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be greater than %s")) % "min layer height").str() }; + if (object->region_volumes[region_id].front().first.first < object_first_layer_height) { + if (object_first_layer_height + EPSILON < min_layer_height) + return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be thinner than %s")) % "min layer height").str() }; for (auto tuple : std::vector<std::pair<double, const char*>>{ {nozzle_diameter, "nozzle diameter"}, {max_layer_height, "max layer height"}, - {skirt_flow(extruder_id).width, "skirt extrusion width"}, - {region->width(FlowRole::frSupportMaterial, true, *object), "support material extrusion width"}, + {skirt_width, "skirt extrusion width"}, + {object->config().support_material ? region->width(FlowRole::frSupportMaterial, true, *object) : object_first_layer_height, "support material extrusion width"}, {region->width(FlowRole::frPerimeter, true, *object), "perimeter extrusion width"}, {region->width(FlowRole::frExternalPerimeter, true, *object), "perimeter extrusion width"}, {region->width(FlowRole::frInfill, true, *object), "infill extrusion width"}, {region->width(FlowRole::frSolidInfill, true, *object), "solid infill extrusion width"}, {region->width(FlowRole::frTopSolidInfill, true, *object), "top solid infill extrusion width"}, }) - if (first_layer_height > tuple.first + EPSILON) + if (object_first_layer_height > tuple.first + EPSILON) return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be greater than %s")) % tuple.second).str() }; } //check not-first layer if (object->region_volumes[region_id].front().first.second > layer_height) { if (layer_height + EPSILON < min_layer_height) - return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be greater than %s")) % "min layer height").str() }; + return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be higher than %s")) % "min layer height").str() }; for (auto tuple : std::vector<std::pair<double, const char*>>{ {nozzle_diameter, "nozzle diameter"}, {max_layer_height, "max layer height"}, - {skirt_flow(extruder_id).width, "skirt extrusion width"}, - {region->width(FlowRole::frSupportMaterial, false, *object), "support material extrusion width"}, + {skirt_width, "skirt extrusion width"}, + {object->config().support_material ? region->width(FlowRole::frSupportMaterial, false, *object) : layer_height, "support material extrusion width"}, {region->width(FlowRole::frPerimeter, false, *object), "perimeter extrusion width"}, {region->width(FlowRole::frExternalPerimeter, false, *object), "perimeter extrusion width"}, {region->width(FlowRole::frInfill, false, *object), "infill extrusion width"}, @@ -1670,13 +1708,6 @@ BoundingBox Print::total_bounding_box() const } #endif -double Print::skirt_first_layer_height() const -{ - if (m_objects.empty()) - throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects"); - return m_objects.front()->config().get_abs_value("first_layer_height"); -} - Flow Print::brim_flow(size_t extruder_id, const PrintObjectConfig& brim_config) const { //use default region, but current object config. @@ -1686,18 +1717,44 @@ Flow Print::brim_flow(size_t extruder_id, const PrintObjectConfig& brim_config) frPerimeter, *Flow::extrusion_option("brim_extrusion_width", tempConf), (float)m_config.nozzle_diameter.get_at(extruder_id), - (float)this->skirt_first_layer_height() + (float)get_first_layer_height() ); } -Flow Print::skirt_flow(size_t extruder_id) const +Flow Print::skirt_flow(size_t extruder_id, bool first_layer/*=false*/) const { + if (m_objects.empty()) + throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects"); + + //get extruder used to compute first layer height + double max_nozzle_diam; + for (PrintObject* pobject : m_objects) { + PrintObject& object = *pobject; + std::set<uint16_t> object_extruders; + for (size_t region_id = 0; region_id < object.region_volumes.size(); ++region_id) { + if (object.region_volumes[region_id].empty()) continue; + const PrintRegion* region = this->regions()[region_id]; + PrintRegion::collect_object_printing_extruders(config(), object.config(), region->config(), object_extruders); + } + //get object first layer extruder + int first_layer_extruder = 0; + double object_first_layer_height = object.config().first_layer_height.value; + if (object.config().first_layer_height.percent) { + object_first_layer_height = 1000000000; + for (uint16_t extruder_id : object_extruders) { + double nozzle_diameter = config().nozzle_diameter.values[extruder_id]; + max_nozzle_diam = std::max(max_nozzle_diam, nozzle_diameter); + } + } + } + + //send m_default_object_config becasue it's the lowest config needed (extrusion_option need config from object & print) return Flow::new_from_config_width( frPerimeter, *Flow::extrusion_option("skirt_extrusion_width", m_default_region_config), - (float)m_config.nozzle_diameter.get_at(extruder_id), - (float)this->skirt_first_layer_height() + (float)max_nozzle_diam, + (float)get_first_layer_height() ); } @@ -1829,10 +1886,9 @@ void Print::process() if (config().complete_objects && !config().complete_objects_one_brim) { for (PrintObject *obj : obj_group) { //get flow - std::vector<uint16_t> set_extruders = this->object_extruders({ obj }); + std::set<uint16_t> set_extruders = this->object_extruders({ obj }); append(set_extruders, this->support_material_extruders()); - sort_remove_duplicates(set_extruders); - Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front(), obj->config()); + Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : *set_extruders.begin(), obj->config()); //don't consider other objects/instances. It's not possible because it's duplicated by some code afterward... i think. brim_area.clear(); //create a brim "pattern" (one per object) @@ -1854,10 +1910,9 @@ void Print::process() if (obj_groups.size() > 1) brim_area = union_ex(brim_area); //get the first extruder in the list for these objects... replicating gcode generation - std::vector<uint16_t> set_extruders = this->object_extruders(m_objects); + std::set<uint16_t> set_extruders = this->object_extruders(m_objects); append(set_extruders, this->support_material_extruders()); - sort_remove_duplicates(set_extruders); - Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front(), m_default_object_config); + Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : *set_extruders.begin(), m_default_object_config); if (brim_config.brim_ears) this->_make_brim_ears(flow, obj_group, brim_area, m_brim); else @@ -1970,15 +2025,12 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio // Skirt may be printed on several layers, having distinct layer heights, // but loops must be aligned so can't vary width/spacing - // TODO: use each extruder's own flow - double first_layer_height = this->skirt_first_layer_height(); std::vector<size_t> extruders; std::vector<double> extruders_e_per_mm; { - std::vector<uint16_t> set_extruders = this->object_extruders(objects); + std::set<uint16_t> set_extruders = this->object_extruders(objects); append(set_extruders, this->support_material_extruders()); - sort_remove_duplicates(set_extruders); extruders.reserve(set_extruders.size()); extruders_e_per_mm.reserve(set_extruders.size()); for (unsigned int extruder_id : set_extruders) { @@ -2033,7 +2085,7 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio erSkirt, (float)mm3_per_mm, // this will be overridden at G-code export time flow.width, - (float)first_layer_height // this will be overridden at G-code export time + (float)get_first_layer_height() // this will be overridden at G-code export time ))); eloop.paths.back().polyline = loop.split_at_first_point(); //we make it clowkwise, but as it will be reversed, it will be ccw @@ -2076,7 +2128,7 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio append(m_skirt_convex_hull, std::move(poly.points)); } -void Print::_extrude_brim_from_tree(std::vector<std::vector< BrimLoop>>& loops, const Polygons& frontiers, const Flow& flow, ExtrusionEntityCollection& out, bool reversed/*= false*/) { +void Print::_extrude_brim_from_tree(std::vector<std::vector<BrimLoop>>& loops, const Polygons& frontiers, const Flow& flow, ExtrusionEntityCollection& out, bool reversed/*= false*/) { // nest contour loops (same as in perimetergenerator) for (int d = loops.size() - 1; d >= 1; --d) { @@ -2089,8 +2141,8 @@ void Print::_extrude_brim_from_tree(std::vector<std::vector< BrimLoop>>& loops, for (size_t j = 0; j < loops[t].size(); ++j) { BrimLoop& candidate_parent = loops[t][j]; bool test = reversed - ? loop.polygon().contains(candidate_parent.line.first_point()) - : candidate_parent.polygon().contains(loop.line.first_point()); + ? loop.polygon().contains(candidate_parent.lines.front().first_point()) + : candidate_parent.polygon().contains(loop.lines.front().first_point()); if (test) { candidate_parent.children.push_back(loop); contours_d.erase(contours_d.begin() + i); @@ -2106,28 +2158,35 @@ void Print::_extrude_brim_from_tree(std::vector<std::vector< BrimLoop>>& loops, NEXT_CONTOUR:; } } + for (int i = loops.size() - 1; i > 0; --i) { + if (loops[i].empty()) { + loops.erase(loops.begin() + i); + } + } //def //cut loops if they go inside a forbidden region - std::function<void(BrimLoop&)> cut_loop = [&frontiers](BrimLoop& to_cut) { + std::function<void(BrimLoop&)> cut_loop = [&frontiers, reversed](BrimLoop& to_cut) { Polylines result; - if (to_cut.is_loop) + if (to_cut.is_loop) { result = intersection_pl(Polygons{ to_cut.polygon() }, frontiers, true); - else - result = intersection_pl(Polylines{ to_cut.line }, frontiers, true); - if (result.empty()) - to_cut.line.points.clear(); - else { - if (to_cut.line.points != result[0].points) { - to_cut.line.points = result[0].points; + } else { + result = intersection_pl(to_cut.lines, frontiers, true); + } + if (result.empty()) { + to_cut.lines.clear(); + } else { + if (to_cut.lines != result) { + to_cut.lines = result; + if (reversed) { + std::reverse(to_cut.lines.begin(), to_cut.lines.end()); + } to_cut.is_loop = false; } - for (int i = 1; i < result.size(); i++) - to_cut.children.insert(to_cut.children.begin() + i - 1, BrimLoop(std::move(result[i]))); } }; - //calls + //calls, deep-first std::list< std::pair<BrimLoop*,int>> cut_child_first; for (std::vector<BrimLoop>& loops : loops) { for (BrimLoop& loop : loops) { @@ -2153,29 +2212,32 @@ void Print::_extrude_brim_from_tree(std::vector<std::vector< BrimLoop>>& loops, //def: push into extrusions, in the right order float mm3_per_mm = float(flow.mm3_per_mm()); float width = float(flow.width); - float height = float(this->skirt_first_layer_height()); + float height = float(get_first_layer_height()); int nextIdx = 0; std::function<void(BrimLoop&, ExtrusionEntityCollection*)>* extrude_ptr; std::function<void(BrimLoop&, ExtrusionEntityCollection*) > extrude = [&mm3_per_mm, &width, &height, &extrude_ptr, &nextIdx](BrimLoop& to_cut, ExtrusionEntityCollection* parent) { int idx = nextIdx++; - bool i_have_line = !to_cut.line.points.empty() && to_cut.line.is_valid(); + //bool i_have_line = !to_cut.line.points.empty() && to_cut.line.is_valid(); + bool i_have_line = to_cut.lines.size() > 0 && to_cut.lines.front().size() > 0 && to_cut.lines.front().is_valid(); if (!i_have_line && to_cut.children.empty()) { //nothing } else if (i_have_line && to_cut.children.empty()) { - if (to_cut.line.points.back() == to_cut.line.points.front()) { - ExtrusionPath path(erSkirt, mm3_per_mm, width, height); - path.polyline.points = to_cut.line.points; - parent->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt)); - } else { - ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height); - parent->entities.push_back(extrusion_path); - extrusion_path->polyline = to_cut.line; - } + for(Polyline& line : to_cut.lines) + if (line.points.back() == line.points.front()) { + ExtrusionPath path(erSkirt, mm3_per_mm, width, height); + path.polyline.points = line.points; + parent->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt)); + } else { + ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height); + parent->entities.push_back(extrusion_path); + extrusion_path->polyline = line; + } } else if (!i_have_line && !to_cut.children.empty()) { if (to_cut.children.size() == 1) { (*extrude_ptr)(to_cut.children[0], parent); } else { ExtrusionEntityCollection* mycoll = new ExtrusionEntityCollection(); + //mycoll->no_sort = true; parent->entities.push_back(mycoll); for (BrimLoop& child : to_cut.children) (*extrude_ptr)(child, mycoll); @@ -2184,19 +2246,21 @@ void Print::_extrude_brim_from_tree(std::vector<std::vector< BrimLoop>>& loops, ExtrusionEntityCollection* print_me_first = new ExtrusionEntityCollection(); parent->entities.push_back(print_me_first); print_me_first->no_sort = true; - if (to_cut.line.points.back() == to_cut.line.points.front()) { - ExtrusionPath path(erSkirt, mm3_per_mm, width, height); - path.polyline.points = to_cut.line.points; - print_me_first->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt)); - } else { - ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height); - print_me_first->entities.push_back(extrusion_path); - extrusion_path->polyline = to_cut.line; - } + for (Polyline& line : to_cut.lines) + if (line.points.back() == line.points.front()) { + ExtrusionPath path(erSkirt, mm3_per_mm, width, height); + path.polyline.points = line.points; + print_me_first->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt)); + } else { + ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height); + print_me_first->entities.push_back(extrusion_path); + extrusion_path->polyline = line; + } if (to_cut.children.size() == 1) { (*extrude_ptr)(to_cut.children[0], print_me_first); } else { ExtrusionEntityCollection* children = new ExtrusionEntityCollection(); + //children->no_sort = true; print_me_first->entities.push_back(children); for (BrimLoop& child : to_cut.children) (*extrude_ptr)(child, children); @@ -2232,13 +2296,7 @@ void Print::_make_brim(const Flow &flow, const PrintObjectPtrs &objects, ExPolyg if (!object->support_layers().empty()) { Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(float(SCALED_EPSILON)); for (Polygon poly : polys) { - for (ExPolygon& expoly2 : union_ex(Polygons{ poly })) { - if (brim_config.brim_inside_holes || brim_config.brim_width_interior > 0) { - object_islands.emplace_back(brim_offset == 0 ? expoly2 : offset_ex(expoly2, brim_offset)[0]); - } else { - object_islands.emplace_back(brim_offset == 0 ? to_expolygon(expoly2.contour) : offset_ex(to_expolygon(expoly2.contour), brim_offset)[0]); - } - } + object_islands.emplace_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]); } } islands.reserve(islands.size() + object_islands.size() * object->m_instances.size()); @@ -2352,31 +2410,29 @@ void Print::_make_brim_ears(const Flow &flow, const PrintObjectPtrs &objects, Ex if (!object->support_layers().empty()) { Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(float(SCALED_EPSILON)); - for (Polygon poly : polys) - for (ExPolygon& expoly2 : union_ex(Polygons{ poly })) - //don't put ears over supports unless it's 100% fill - if (object->config().support_material_solid_first_layer) { - if (brim_config.brim_inside_holes || brim_config.brim_width_interior > 0) - object_islands.push_back(brim_offset == 0 ? expoly2 : offset_ex(expoly2, brim_offset)[0]); - else - object_islands.emplace_back(brim_offset == 0 ? to_expolygon(expoly2.contour) : offset_ex(to_expolygon(expoly2.contour), brim_offset)[0]); - } else { - unbrimmable_with_support.push_back(expoly2); - } + for (Polygon poly : polys) { + //don't put ears over supports unless it's 100% fill + if (object->config().support_material_solid_first_layer) { + object_islands.push_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]); + } else { + unbrimmable_with_support.push_back(ExPolygon{ poly }); + } + } } islands.reserve(islands.size() + object_islands.size() * object->m_instances.size()); + coord_t ear_detection_length = scale_t(object->config().brim_ears_detection_length.value); for (const PrintInstance ©_pt : object->m_instances) for (const ExPolygon &poly : object_islands) { islands.push_back(poly); islands.back().translate(copy_pt.shift.x(), copy_pt.shift.y()); Polygon decimated_polygon = poly.contour; // brim_ears_detection_length codepath - if (object->config().brim_ears_detection_length.value > 0) { + if (ear_detection_length > 0) { //decimate polygon Points points = poly.contour.points; points.push_back(points.front()); - points = MultiPoint::_douglas_peucker(points, scale_(object->config().brim_ears_detection_length.value)); - if (points.size() > 1) { + points = MultiPoint::_douglas_peucker(points, ear_detection_length); + if (points.size() > 4) { //don't decimate if it's going to be below 4 points, as it's surely enough to fill everything anyway points.erase(points.end() - 1); decimated_polygon.points = points; } @@ -2457,7 +2513,7 @@ void Print::_make_brim_ears(const Flow &flow, const PrintObjectPtrs &objects, Ex erSkirt, float(flow.mm3_per_mm()), float(flow.width), - float(this->skirt_first_layer_height()) + float(get_first_layer_height()) ); unbrimmable = union_ex(unbrimmable, offset_ex(mouse_ears_ex, flow.scaled_spacing()/2)); @@ -2507,15 +2563,17 @@ void Print::_make_brim_interior(const Flow &flow, const PrintObjectPtrs &objects const PrintObjectConfig &brim_config = objects.front()->config(); coord_t brim_offset = scale_(brim_config.brim_offset.value); ExPolygons islands; + coordf_t spacing; for (PrintObject *object : objects) { ExPolygons object_islands; for (ExPolygon &expoly : object->m_layers.front()->lslices) object_islands.push_back(brim_offset == 0 ? expoly : offset_ex(expoly, brim_offset)[0]); if (!object->support_layers().empty()) { - Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(float(SCALED_EPSILON)); - for (Polygon poly : polys) - for (ExPolygon& expoly2 : union_ex(Polygons{ poly })) - object_islands.push_back(brim_offset == 0 ? expoly2 : offset_ex(expoly2, brim_offset)[0]); + spacing = scaled(object->config().support_material_interface_spacing.value) + support_material_flow(object, float(get_first_layer_height())).scaled_width() * 1.5; + Polygons polys = offset2(object->support_layers().front()->support_fills.polygons_covered_by_spacing(float(SCALED_EPSILON)), spacing, -spacing); + for (Polygon poly : polys) { + object_islands.push_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]); + } } islands.reserve(islands.size() + object_islands.size() * object->instances().size()); for (const PrintInstance &instance : object->instances()) @@ -2802,7 +2860,7 @@ void Print::_make_wipe_tower() wipe_tower.set_extruder(i); m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>( - wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); + wipe_tower.prime((float)get_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); // Lets go through the wipe tower layers and determine pairs of extruder changes for each // to pass to wipe_tower (so that it can use it for planning the layout of the tower) @@ -2822,28 +2880,28 @@ void Print::_make_wipe_tower() float pigmentAft = m_config.filament_wipe_advanced_pigment.get_at(extruder_id); if (m_config.wipe_advanced_algo.value == waLinear) { volume_to_wipe += m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft); - std::cout << "advanced wiping (lin) "; - std::cout << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; - std::cout << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value + BOOST_LOG_TRIVIAL(info) << "advanced wiping (lin) "; + BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; + BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value << " * ( " << pigmentBef << " - " << pigmentAft << " )\n"; - std::cout << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft)) << "\n"; + BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft)) << "\n"; } else if (m_config.wipe_advanced_algo.value == waQuadra) { volume_to_wipe += m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft) + m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft) * (pigmentBef - pigmentAft) * (pigmentBef - pigmentAft); - std::cout << "advanced wiping (quadra) "; - std::cout << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; - std::cout << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value + BOOST_LOG_TRIVIAL(info) << "advanced wiping (quadra) "; + BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; + BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value << " * ( " << pigmentBef << " - " << pigmentAft << " ) + " << m_config.wipe_advanced_multiplier.value << " * ( " << pigmentBef << " - " << pigmentAft << " ) ^3 \n"; - std::cout << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft)) + BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft)) << " + " << (m_config.wipe_advanced_multiplier.value*(pigmentBef - pigmentAft)*(pigmentBef - pigmentAft)*(pigmentBef - pigmentAft))<<"\n"; } else if (m_config.wipe_advanced_algo.value == waHyper) { volume_to_wipe += m_config.wipe_advanced_multiplier.value * (0.5 + pigmentBef) / (0.5 + pigmentAft); - std::cout << "advanced wiping (hyper) "; - std::cout << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; - std::cout << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value + BOOST_LOG_TRIVIAL(info) << "advanced wiping (hyper) "; + BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n"; + BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value << " * ( 0.5 + " << pigmentBef << " ) / ( 0.5 + " << pigmentAft << " )\n"; - std::cout << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value * (0.5 + pigmentBef) / (0.5 + pigmentAft)) << "\n"; + BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value * (0.5 + pigmentBef) / (0.5 + pigmentAft)) << "\n"; } } //filament_wipe_advanced_pigment diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 300029d02..52a8612ef 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -74,8 +74,8 @@ public: coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Collect 0-based extruder indices used to print this region's object. - void collect_object_printing_extruders(std::vector<uint16_t> &object_extruders) const; - static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector<uint16_t> &object_extruders); + void collect_object_printing_extruders(std::set<uint16_t> &object_extruders) const; + static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::set<uint16_t> &object_extruders); // Methods modifying the PrintRegion's state: public: @@ -188,7 +188,8 @@ public: static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) - std::vector<uint16_t> object_extruders() const; + std::set<uint16_t> object_extruders() const; + double get_first_layer_height() const; // Called by make_perimeters() void slice(); @@ -369,13 +370,14 @@ struct PrintStatistics class BrimLoop { public: - BrimLoop(const Polygon& p) : line(p.split_at_first_point()), is_loop(true) {} - BrimLoop(const Polyline& l) : line(l), is_loop(false) {} - Polyline line; + BrimLoop(const Polygon& p) : lines(Polylines{ p.split_at_first_point() }), is_loop(true) {} + BrimLoop(const Polyline& l) : lines(Polylines{l}), is_loop(false) {} + Polylines lines; std::vector<BrimLoop> children; - bool is_loop; + bool is_loop; // has only one polyline stored and front == back Polygon polygon() const{ - Polygon poly = Polygon(line.points); + assert(is_loop); + Polygon poly = Polygon(lines.front().points); if (poly.points.front() == poly.points.back()) poly.points.resize(poly.points.size() - 1); return poly; @@ -431,13 +433,14 @@ public: // Returns an empty string if valid, otherwise returns an error message. std::pair<PrintValidationError, std::string> validate() const override; - double skirt_first_layer_height() const; Flow brim_flow(size_t extruder_id, const PrintObjectConfig &brim_config) const; - Flow skirt_flow(size_t extruder_id) const; + Flow skirt_flow(size_t extruder_id, bool first_layer=false) const; + double get_first_layer_height() const; + double get_object_first_layer_height(const PrintObject& object) const; - std::vector<uint16_t> object_extruders(const PrintObjectPtrs &objects) const; - std::vector<uint16_t> support_material_extruders() const; - std::vector<uint16_t> extruders() const; + std::set<uint16_t> object_extruders(const PrintObjectPtrs &objects) const; + std::set<uint16_t> support_material_extruders() const; + std::set<uint16_t> extruders() const; double max_allowed_layer_height() const; bool has_support_material() const; // Make sure the background processing has no access to this model_object during this call! diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index fb5e102c1..e4a0b45cc 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -62,12 +62,54 @@ std::string PrintBase::output_filename(const std::string &format, const std::str cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { - boost::filesystem::path filename = format.empty() ? + boost::filesystem::path filepath = format.empty() ? cfg.opt_string("input_filename_base") + default_ext : this->placeholder_parser().process(format, 0, &cfg); - if (filename.extension().empty()) - filename = boost::filesystem::change_extension(filename, default_ext); - return filename.string(); + //remove unwanted characters + std::string forbidden_base; + if (const ConfigOptionString* opt = this->placeholder_parser().external_config()->option<ConfigOptionString>("gcode_filename_illegal_char")) { + forbidden_base = opt->value; + } + if (!forbidden_base.empty()) { + const std::string filename_init = filepath.stem().string(); + std::string filename = filename_init; + std::string extension = filepath.extension().string(); + //remove {print_time} and things like that that may be computed after, and re-put them inside it after the replace. + std::regex placehoder = std::regex("\\{[a-z_]+\\}"); + std::smatch matches; + std::string::const_iterator searchStart(filename_init.cbegin()); + while (std::regex_search(searchStart, filename_init.cend(), matches, placehoder)) { + for (int i = 0; i < matches.size(); i++) { + filename.replace(matches.position(i), matches.length(i), matches.length(i), '_'); + } + searchStart = matches.suffix().first; + } + //remove unwanted characters from the cleaned string + bool regexp_used = false; + if (forbidden_base.front() == '(' || forbidden_base.front() == '[') { + try { + filename = std::regex_replace(filename, std::regex(forbidden_base), "_"); + regexp_used = true; + }catch(std::exception){} + } + if (!regexp_used) { + for(int i=0; i< forbidden_base.size(); i++) + std::replace(filename.begin(), filename.end(), forbidden_base.at(i), '_'); + } + //re-put {print_time} and things like that + searchStart = (filename_init.cbegin()); + while (std::regex_search(searchStart, filename_init.cend(), matches, placehoder)) { + for (int i = 0; i < matches.size(); i++) { + filename.replace(matches.position(i), matches.length(i), matches.str()); + } + searchStart = matches.suffix().first; + } + // set the path var + filepath = filename + extension; + } + if (filepath.extension().empty()) + filepath = boost::filesystem::change_extension(filepath, default_ext); + return filepath.string(); } catch (std::runtime_error &err) { throw Slic3r::PlaceholderParserError(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); } diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 5c0a1ebc9..aecb43089 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -389,13 +389,16 @@ public: virtual void finalize() {} struct SlicingStatus { - SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {} + SlicingStatus(int percent, const std::string& text, unsigned int flags = 0) : percent(percent), main_text(text), flags(flags) {} + SlicingStatus(int percent, const std::string& text, const std::vector<std::string>& args, unsigned int flags = 0) + : percent(percent), main_text(text), args(args), flags(flags) {} SlicingStatus(const PrintBase &print, int warning_step) : flags(UPDATE_PRINT_STEP_WARNINGS), warning_object_id(print.id()), warning_step(warning_step) {} SlicingStatus(const PrintObjectBase &print_object, int warning_step) : flags(UPDATE_PRINT_OBJECT_STEP_WARNINGS), warning_object_id(print_object.id()), warning_step(warning_step) {} - int percent { -1 }; - std::string text; + int percent { -1 }; + std::string main_text; + std::vector<std::string> args; // Bitmap of flags. enum FlagBits { DEFAULT = 0, @@ -422,8 +425,12 @@ public: // Register a custom status callback. void set_status_callback(status_callback_type cb) { m_status_callback = cb; } // Calls a registered callback to update the status, or print out the default message. - void set_status(int percent, const std::string &message, unsigned int flags = SlicingStatus::DEFAULT) { - if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags)); + void set_status(int percent, const std::string& message, unsigned int flags = SlicingStatus::DEFAULT) const { + if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags)); + else printf("%d => %s\n", percent, message.c_str()); + } + void set_status(int percent, const std::string& message, const std::vector<std::string>& args, unsigned int flags = SlicingStatus::DEFAULT) const { + if (m_status_callback) m_status_callback(SlicingStatus(percent, message, args, flags)); else printf("%d => %s\n", percent, message.c_str()); } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1f7a633d0..e875d8e29 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -111,7 +111,7 @@ void PrintConfigDef::init_common_params() def = this->add("layer_height", coFloat); def->label = L("Base Layer height"); - def->category = OptionCategory::perimeter; + def->category = OptionCategory::slicing; def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. " "Thinner layers give better accuracy but take more time to print."); def->sidetext = L("mm"); @@ -258,6 +258,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°C"); def->min = 0; def->max = 300; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 0 }); def = this->add("before_layer_gcode", coString); @@ -330,7 +331,7 @@ void PrintConfigDef::init_fff_params() def = this->add("bridge_fan_speed", coInts); def->label = L("Bridges fan speed"); def->category = OptionCategory::cooling; - def->tooltip = L("This fan speed is enforced during all bridges and overhangs. It won't slow down the fan if it's currently running at a higher speed." + def->tooltip = L("This fan speed is enforced during bridges and overhangs. It won't slow down the fan if it's currently running at a higher speed." "\nSet to 1 to disable the fan." "\nSet to -1 to disable this override." "\nCan only be overriden by disable_fan_first_layers."); @@ -338,7 +339,22 @@ void PrintConfigDef::init_fff_params() def->min = -1; def->max = 100; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts { 100 }); + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionInts{ 100 }); + + def = this->add("bridge_internal_fan_speed", coInts); + def->label = L("Infill bridges fan speed"); + def->category = OptionCategory::cooling; + def->tooltip = L("This fan speed is enforced during all infill bridges. It won't slow down the fan if it's currently running at a higher speed." + "\nSet to 1 to disable the fan." + "\nSet to -1 to disable this override (will take the value of Bridges fan speed)." + "\nCan only be overriden by disable_fan_first_layers."); + def->sidetext = L("%"); + def->min = -1; + def->max = 100; + def->mode = comAdvanced; + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionInts{ -1 }); def = this->add("top_fan_speed", coInts); def->label = L("Top fan speed"); @@ -351,6 +367,7 @@ void PrintConfigDef::init_fff_params() def->min = -1; def->max = 100; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ -1 }); def = this->add("bridge_flow_ratio", coPercent); @@ -501,6 +518,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 300; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ 0 }); def = this->add("clip_multipart_objects", coBool); @@ -607,6 +625,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This flag enables the automatic cooling logic that adjusts print speed " "and fan speed according to layer printing time."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { true }); def = this->add("cooling_tube_retraction", coFloat); @@ -664,6 +683,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 1000; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 3 }); def = this->add("dont_support_bridges", coBool); @@ -713,6 +733,7 @@ void PrintConfigDef::init_fff_params() def->full_width = true; def->height = 120; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" }); def = this->add("ensure_vertical_shell_thickness", coBool); @@ -897,6 +918,7 @@ void PrintConfigDef::init_fff_params() def->min = -1; def->max = 100; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { -1 }); def = this->add("external_perimeter_overlap", coPercent); @@ -1061,7 +1083,6 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If you have some problem with the 'Only one perimeter on Top surfaces' option, you can try to activate this on the problematic layer."); def->set_default_value(new ConfigOptionBool(false)); - def = this->add("extruder", coInt); def->gui_type = "i_enum_open"; def->label = L("Extruder"); @@ -1080,6 +1101,23 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back("8"); def->enum_labels.push_back("9"); + def = this->add("first_layer_extruder", coInt); + def->gui_type = "i_enum_open"; + def->label = L("First layer extruder"); + def->category = OptionCategory::extruders; + def->tooltip = L("The extruder to use (unless more specific extruder settings are specified) for the first layer."); + def->min = 0; // 0 = inherit defaults + def->enum_labels.push_back(L("default")); // override label for item 0 + def->enum_labels.push_back("1"); + def->enum_labels.push_back("2"); + def->enum_labels.push_back("3"); + def->enum_labels.push_back("4"); + def->enum_labels.push_back("5"); + def->enum_labels.push_back("6"); + def->enum_labels.push_back("7"); + def->enum_labels.push_back("8"); + def->enum_labels.push_back("9"); + def = this->add("extruder_clearance_height", coFloat); def->label = L("Height"); def->full_label = L("Extruder clearance height"); @@ -1100,7 +1138,8 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Set this to the clearance radius around your extruder. " "If the extruder is not centered, choose the largest value for safety. " "This setting is used to check for collisions and to display the graphical preview " - "in the plater."); + "in the plater." + "\nSet zero to disable clearance checking."); def->sidetext = L("mm"); def->min = 0; def->mode = comExpert; @@ -1113,6 +1152,7 @@ void PrintConfigDef::init_fff_params() def->gui_type = "color"; // Empty string means no color assigned yet. def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings{ "" }); def = this->add("extruder_offset", coPoints); @@ -1124,6 +1164,7 @@ void PrintConfigDef::init_fff_params() "from the XY coordinate)."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPoints{ Vec2d(0,0) }); def = this->add("extruder_temperature_offset", coFloats); @@ -1134,6 +1175,7 @@ void PrintConfigDef::init_fff_params() "\ninstead of 'M104 S[first_layer_temperature]' in the start_gcode"); def->sidetext = L("°C"); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0 }); def = this->add("extruder_fan_offset", coPercents); @@ -1142,6 +1184,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This offset wil be added to all fan values set in the filament properties. It won't make them go higher than 100% nor lower than 0%."); def->sidetext = L("%"); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPercents{ 0 }); @@ -1161,6 +1204,7 @@ void PrintConfigDef::init_fff_params() "check filament diameter and your firmware E steps."); def->mode = comSimple; def->max = 2; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 1. }); def = this->add("print_extrusion_multiplier", coPercent); @@ -1214,6 +1258,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If this is enabled, fan will continuously run at base speed if no other setting overrides that speed." " Useful for PLA, harmful for ABS."); def->mode = comSimple; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ false }); def = this->add("fan_below_layer_time", coInts); @@ -1221,11 +1266,12 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::cooling; def->tooltip = L("If layer print time is estimated below this number of seconds, fan will be enabled " "and its speed will be calculated by interpolating the default and maximum speeds." - "\nSet to 0 to disable."); + "\nSet zero to disable."); def->sidetext = L("approximate seconds"); def->min = 0; def->max = 1000; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 60 }); def = this->add("filament_colour", coStrings); @@ -1235,6 +1281,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This is only used in the Slic3r interface as a visual help."); def->gui_type = "color"; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings{ "#29B2B2" }); def = this->add("filament_notes", coStrings); @@ -1245,6 +1292,7 @@ void PrintConfigDef::init_fff_params() def->full_width = true; def->height = 13; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings { "" }); def = this->add("filament_max_speed", coFloats); @@ -1256,6 +1304,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0. }); def = this->add("filament_max_volumetric_speed", coFloats); @@ -1267,6 +1316,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³/s"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0. }); def = this->add("filament_max_wipe_tower_speed", coFloats); @@ -1284,6 +1334,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 200; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0 }); def = this->add("filament_loading_speed", coFloats); @@ -1292,6 +1343,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 28. }); //skinnydip section starts @@ -1299,18 +1351,21 @@ void PrintConfigDef::init_fff_params() def->label = L("Toolchange temperature enabled"); def->tooltip = L("Determines whether toolchange temperatures will be applied"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("filament_use_fast_skinnydip", coBools); def->label = L("Fast mode"); def->tooltip = L("Experimental: drops nozzle temperature during cooling moves instead of prior to extraction to reduce wait time."); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("filament_enable_toolchange_part_fan", coBools); def->label = L("Use part fan to cool hotend"); def->tooltip = L("Experimental setting. May enable the hotend to cool down faster during toolchanges"); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("filament_toolchange_part_fan_speed", coInts); @@ -1320,12 +1375,14 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 100; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 50 }); def = this->add("filament_use_skinnydip", coBools); def->label = L("Enable Skinnydip string reduction"); def->tooltip = L("Skinnydip performs a secondary dip into the meltzone to burn off fine strings of filament"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("filament_melt_zone_pause", coInts); @@ -1334,6 +1391,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("milliseconds"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 0 }); def = this->add("filament_cooling_zone_pause", coInts); @@ -1342,6 +1400,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("milliseconds"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 0 }); def = this->add("filament_dip_insertion_speed", coFloats); @@ -1350,6 +1409,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/sec"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 33. }); def = this->add("filament_dip_extraction_speed", coFloats); @@ -1358,6 +1418,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/sec"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 70. }); def = this->add("filament_toolchange_temp", coInts); @@ -1366,6 +1427,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°C"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 200 }); def = this->add("filament_skinnydip_distance", coFloats); @@ -1374,6 +1436,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 31. }); //skinnydip section ends @@ -1383,6 +1446,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 3. }); def = this->add("filament_unloading_speed", coFloats); @@ -1392,6 +1456,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 90. }); def = this->add("filament_unloading_speed_start", coFloats); @@ -1400,6 +1465,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 100. }); def = this->add("filament_toolchange_delay", coFloats); @@ -1410,6 +1476,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("filament_cooling_moves", coInts); @@ -1419,6 +1486,7 @@ void PrintConfigDef::init_fff_params() def->max = 0; def->max = 20; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 4 }); def = this->add("filament_cooling_initial_speed", coFloats); @@ -1427,6 +1495,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 2.2 }); def = this->add("filament_minimal_purge_on_wipe_tower", coFloats); @@ -1438,6 +1507,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 15. }); def = this->add("filament_cooling_final_speed", coFloats); @@ -1446,6 +1516,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 3.4 }); def = this->add("filament_load_time", coFloats); @@ -1454,12 +1525,14 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0.0 }); def = this->add("filament_ramming_parameters", coStrings); def->label = L("Ramming parameters"); def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters."); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|" " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" }); @@ -1469,6 +1542,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0.0 }); def = this->add("filament_diameter", coFloats); @@ -1478,6 +1552,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 1.75 }); def = this->add("filament_shrink", coPercents); @@ -1489,6 +1564,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("%"); def->min = 10; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPercents{ 100 }); def = this->add("filament_density", coFloats); @@ -1500,6 +1576,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("g/cm³"); def->min = 0; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0. }); def = this->add("filament_type", coStrings); @@ -1538,6 +1615,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("other8"); def->enum_values.push_back("other9"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings { "PLA" }); def = this->add("filament_soluble", coBools); @@ -1545,6 +1623,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::filament; def->tooltip = L("Soluble material is most likely used for a soluble support."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("filament_cost", coFloats); @@ -1554,6 +1633,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Enter your filament cost per kg here. This is only for statistical information."); def->sidetext = L("money/kg"); def->min = 0; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("filament_spool_weight", coFloats); @@ -1565,6 +1645,7 @@ void PrintConfigDef::init_fff_params() "of filament on the spool is sufficient to finish the print."); def->sidetext = L("g"); def->min = 0; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("filament_settings_id", coStrings); @@ -1774,6 +1855,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°C"); def->max = 0; def->max = 300; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 0 }); def = this->add("first_layer_extrusion_width", coFloatOrPercent); @@ -1812,11 +1894,11 @@ void PrintConfigDef::init_fff_params() def = this->add("first_layer_height", coFloatOrPercent); def->label = L("First layer height"); - def->category = OptionCategory::perimeter; + def->category = OptionCategory::slicing; def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " "bottom layer to improve adhesion and tolerance for non perfect build plates. " "This can be expressed as an absolute value or as a percentage (for example: 75%) " - "over the default nozzle width."); + "over the lowest nozzle diameter used in by the object."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; @@ -1824,14 +1906,14 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloatOrPercent(75, true)); def = this->add("first_layer_speed", coFloatOrPercent); - def->label = L("Default"); + def->label = L("Max"); def->full_label = L("Default first layer speed"); def->category = OptionCategory::speed; def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves " - "but infill of the first layer, it can be overwritten by the 'default' (default depends of the type of the path) " - "speed if it's lower than that. If expressed as a percentage " - "it will scale the current speed." - "\nSet it at 100% to remove any first layer speed modification (with first_layer_infill_speed)."); + "but infill of the first layer, it can be overwritten by the 'default' (default depends of the type of the path) " + "speed if it's lower than that. If expressed as a percentage " + "it will scale the current speed." + "\nSet it at 100% to remove any first layer speed modification (with first_layer_infill_speed and first_layer_speed_min)."); def->sidetext = L("mm/s or %"); def->ratio_over = "depends"; def->min = 0; @@ -1851,6 +1933,20 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloatOrPercent(30, false)); + + def = this->add("first_layer_min_speed", coFloatOrPercent); + def->label = L("Min"); + def->full_label = L("Min first layer speed"); + def->category = OptionCategory::speed; + def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves" + ", it can be overwritten by the 'default' (default depends of the type of the path) speed if it's higher than that." + " If expressed as a percentage it will scale the current speed." + "\nSet zero to disable."); + def->sidetext = L("mm/s or %"); + def->ratio_over = "depends"; + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); def = this->add("first_layer_temperature", coInts); def->label = L("First layer"); @@ -1861,6 +1957,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°C"); def->min = 0; def->max = max_temp; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 200 }); def = this->add("full_fan_speed_layer", coInts); @@ -1872,6 +1969,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 1000; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 0 }); def = this->add("gap_fill", coBool); @@ -1935,6 +2033,15 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(0)); + def = this->add("gcode_filename_illegal_char", coString); + def->label = L("Illegal characters"); + def->full_label = L("Illegal characters for filename"); + def->category = OptionCategory::output; + def->tooltip = L("All characters that are written here will be replaced by '_' when writing the gcode file name." + "\nIf the first charater is '[' or '(', then this field will be considered as a regexp (enter '[^a-zA-Z0-9]' to only use ascii char)."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionString("")); + def = this->add("gcode_flavor", coEnum); def->label = L("G-code flavor"); def->category = OptionCategory::general; @@ -1971,6 +2078,15 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfSprinter)); + def = this->add("gcode_filename_illegal_char", coString); + def->label = L("Illegal characters"); + def->full_label = L("Illegal characters for filename"); + def->category = OptionCategory::output; + def->tooltip = L("All characters that are written here will be replaced by '_' when writing the gcode file name." + "\nIf the first charater is '[' or '(', then this field will be considered as a regexp (enter '[^a-zA-Z]' to only use ascii char)."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionString("")); + def = this->add("gcode_label_objects", coBool); def->label = L("Label objects"); def->category = OptionCategory::output; @@ -2558,6 +2674,17 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); + def = this->add("max_gcode_per_second", coFloat); + def->label = L("Maximum G1 per second"); + def->category = OptionCategory::speed; + def->tooltip = L("If your firmware stops while printing, it may have its gcode queue full." + " Set this parameter to merge extrusions into bigger ones to reduce the number of gcode commands the printer has to process each second." + "\nNote that reducing your printing speed (at least for the external extrusions) will reduce the number of time this will triggger and so increase quality." + "\nSet zero to disable."); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(1500)); + def = this->add("max_fan_speed", coInts); def->label = L("Max"); def->full_label = L("Max fan speed"); @@ -2567,6 +2694,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 100; def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 100 }); def = this->add("max_layer_height", coFloats); @@ -2580,6 +2708,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comSimple; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("max_print_speed", coFloat); @@ -2601,6 +2730,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 100; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPercents{ 90 }); def = this->add("max_volumetric_speed", coFloat); @@ -2648,6 +2778,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 100; def->mode = comSimple; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ 35 }); def = this->add("fan_percentage", coBool); @@ -2667,13 +2798,15 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comSimple; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0.07 }); def = this->add("min_length", coFloat); def->label = L("Minimum extrusion length"); def->category = OptionCategory::speed; - def->tooltip = L("Too many too small commands may overload the firmware / connection. Put a higher value here if you see strange slowdown." - "\n0 to disable."); + def->tooltip = L("[Deprecated] Prefer using max_gcode_per_second instead, as it's much better when you have very different speeds for features." + "\nToo many too small commands may overload the firmware / connection. Put a higher value here if you see strange slowdown." + "\nSet zero to disable."); def->sidetext = L("mm"); def->min = 0; def->precision = 8; @@ -2699,6 +2832,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 10. }); def = this->add("min_skirt_length", coFloat); @@ -2729,6 +2863,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)"); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0.4 }); def = this->add("host_type", coEnum); @@ -3066,7 +3201,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::slicing; def->tooltip = L("Minimum detail resolution, used to simplify the input file for speeding up " "the slicing job and reducing memory usage. High-resolution models often carry " - "more details than printers can render. Set to zero to disable any simplification " + "more details than printers can render. Set zero to disable any simplification " "and use full resolution from input. " "\nNote: Slic3r has an internal working resolution of 0.0001mm." "\nInfill & Thin areas are simplified up to 0.0125mm."); @@ -3083,6 +3218,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->mode = comAdvanced; def->min = 0; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 2. }); def = this->add("retract_before_wipe", coPercents); @@ -3092,6 +3228,7 @@ void PrintConfigDef::init_fff_params() "before doing the wipe movement."); def->sidetext = L("%"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPercents { 0. }); def = this->add("retract_layer_change", coBools); @@ -3099,6 +3236,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::extruders; def->tooltip = L("This flag enforces a retraction whenever a Z move is done (before it)."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools { false }); def = this->add("retract_length", coFloats); @@ -3109,6 +3247,7 @@ void PrintConfigDef::init_fff_params() "(the length is measured on raw filament, before it enters the extruder)."); def->sidetext = L("mm (zero to disable)"); def->min = 0; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 2. }); def = this->add("print_retract_length", coFloat); @@ -3127,6 +3266,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm (zero to disable)"); def->mode = comExpert; def->min = 0; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 10. }); def = this->add("retract_lift", coFloats); @@ -3136,6 +3276,7 @@ void PrintConfigDef::init_fff_params() "is triggered. When using multiple extruders, only the setting for the first extruder " "will be considered."); def->sidetext = L("mm"); + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("retract_lift_above", coFloats); @@ -3146,6 +3287,7 @@ void PrintConfigDef::init_fff_params() "absolute Z. You can tune this setting for skipping lift on the first layers."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); @@ -3159,6 +3301,7 @@ void PrintConfigDef::init_fff_params() "to the first layers."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("retract_lift_first_layer", coBools); @@ -3167,6 +3310,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::extruders; def->tooltip = L("Select this option to enforce z-lift on the first layer."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ false }); def = this->add("retract_lift_top", coStrings); @@ -3180,6 +3324,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back(("Not on top")); def->enum_values.push_back(("Only on top")); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings{ "All surfaces" }); @@ -3189,6 +3334,7 @@ void PrintConfigDef::init_fff_params() "this additional amount of filament. This setting is rarely needed."); def->sidetext = L("mm"); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("retract_restart_extra_toolchange", coFloats); @@ -3198,6 +3344,7 @@ void PrintConfigDef::init_fff_params() "this additional amount of filament."); def->sidetext = L("mm"); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("retract_speed", coFloats); @@ -3207,6 +3354,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The speed for retractions (this only applies to the extruder motor)."); def->sidetext = L("mm/s"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 40. }); def = this->add("deretract_speed", coFloats); @@ -3217,6 +3365,7 @@ void PrintConfigDef::init_fff_params() "(this only applies to the extruder motor). If left as zero, the retraction speed is used."); def->sidetext = L("mm/s"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("seam_position", coEnum); @@ -3345,11 +3494,12 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::cooling; def->tooltip = L("If layer print time is estimated below this number of seconds, print moves " "speed will be scaled down to extend duration to this value, if possible." - "\nSet to 0 to disable."); + "\nSet zero to disable."); def->sidetext = L("approximate seconds"); def->min = 0; def->max = 1000; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ 5 }); def = this->add("small_perimeter_speed", coFloatOrPercent); @@ -3440,7 +3590,7 @@ void PrintConfigDef::init_fff_params() def->label = L("cutoff"); def->full_label = L("Curve smoothing cutoff dist"); def->category = OptionCategory::slicing; - def->tooltip = L("Maximum distance between two points to allow adding new ones. Allow to avoid distorting long strait areas. 0 to disable."); + def->tooltip = L("Maximum distance between two points to allow adding new ones. Allow to avoid distorting long strait areas.\nSet zero to disable."); def->sidetext = L("mm"); def->min = 0; def->cli = "curve-smoothing-cutoff-dist=f"; @@ -3599,6 +3749,7 @@ void PrintConfigDef::init_fff_params() def->full_width = true; def->height = 12; def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings { "; Filament gcode\n" }); def = this->add("model_precision", coFloat); @@ -3607,7 +3758,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::slicing; def->tooltip = L("This is the rounding error of the input object." " It's used to align points that should be in the same line." - " Put 0 to disable."); + "\nSet zero to disable."); def->sidetext = L("mm"); def->min = 0; def->precision = 8; @@ -3673,7 +3824,7 @@ void PrintConfigDef::init_fff_params() " This number allow to keep some if there is a low number of perimeter over the void." "\nIf this setting is equal or higher than the top/bottom solid layer count, it won't evict anything." "\nIf this setting is set to 1, it will evict all solid fill are are only over perimeters." - "\nSet it to 0 to disable."); + "\nSet zero to disable."); def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(2)); @@ -3781,9 +3932,9 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(0)); def = this->add("support_material_extruder", coInt); - def->label = L("Support material/raft/skirt extruder"); + def->label = L("Support material extruder"); def->category = OptionCategory::extruders; - def->tooltip = L("The extruder to use when printing support material, raft and skirt " + def->tooltip = L("The extruder to use when printing support material " "(1+, 0 to use the current extruder to minimize tool changes)."); def->min = 0; def->mode = comAdvanced; @@ -3947,6 +4098,7 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Nozzle temperature"); def->min = 0; def->max = max_temp; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts { 200 }); def = this->add("print_temperature", coInt); @@ -4071,6 +4223,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::extruders; def->tooltip = L("Only used for Klipper, where you can name the extruder. If not set, will be 'extruderX' with 'X' replaced by the extruder number."); def->mode = comExpert; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings("")); def = this->add("top_infill_extrusion_width", coFloatOrPercent); @@ -4214,11 +4367,20 @@ void PrintConfigDef::init_fff_params() def = this->add("wipe", coBools); def->label = L("Wipe while retracting"); - def->category = OptionCategory::general; + def->category = OptionCategory::extruders; def->tooltip = L("This flag will move the nozzle while retracting to minimize the possible blob " - "on leaky extruders."); + "on leaky extruders."); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools { false }); + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionBools{ false }); + + def = this->add("wipe_speed", coFloats); + def->label = L("Wipe speed"); + def->category = OptionCategory::extruders; + def->tooltip = L("Speed in mm/s of the wipe. If it's faster, it will try to go further away, as the wipe time is set by ( 100% - 'retract before wipe') * 'retaction length' / 'retraction speed'." + "\nIf set to zero, the travel speed is used."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{ 0 }); def = this->add("wipe_tower", coBool); def->label = L("Enable"); @@ -4266,6 +4428,7 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->min = 0; def->max = 1; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0.5 }); def = this->add("wipe_advanced_multiplier", coFloat); @@ -4356,6 +4519,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats{ 0.f }); def = this->add("wipe_tower_bridging", coFloat); @@ -4426,13 +4590,21 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Polyhole detection margin"); def->category = OptionCategory::slicing; def->tooltip = L("Maximum defection of a point to the estimated radius of the circle." - "\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference." - " This setting allows you some leway to broaden the detection." - "\nIn mm or in % of the radius."); + "\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference." + " This setting allows you some leway to broaden the detection." + "\nIn mm or in % of the radius."); def->sidetext = L("mm or %"); def->mode = comExpert; def->set_default_value(new ConfigOptionFloatOrPercent(0.01, false)); + def = this->add("hole_to_polyhole_twisted", coBool); + def->label = L("Twisting"); + def->full_label = L("Polyhole twist"); + def->category = OptionCategory::slicing; + def->tooltip = L("Rotate the polyhole every layer."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("z_offset", coFloat); def->label = L("Z offset"); def->category = OptionCategory::general; @@ -4449,7 +4621,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Set this to the height moved when your Z motor (or equivalent) turns one step." "If your motor needs 200 steps to move your head/plater by 1mm, this field should be 1/200 = 0.005." "\nNote that the gcode will write the z values with 6 digits after the dot if z_step is set (it's 3 digits if it's disabled)." - "\nPut 0 to disable."); + "\nSet zero to disable."); def->cli = "z-step=f"; def->sidetext = L("mm"); def->min = 0; @@ -4461,7 +4633,7 @@ void PrintConfigDef::init_fff_params() for (const char *opt_key : { // floats "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", "retract_restart_extra", "retract_before_travel", - "wipe_extra_perimeter", + "wipe_extra_perimeter", "wipe_speed", // bools "retract_layer_change", "wipe", // percents @@ -4508,6 +4680,7 @@ void PrintConfigDef::init_extruder_option_keys() "retract_before_travel", "wipe", "wipe_extra_perimeter", + "wipe_speed", "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", @@ -4527,7 +4700,8 @@ void PrintConfigDef::init_extruder_option_keys() "retract_restart_extra", "retract_speed", "wipe", - "wipe_extra_perimeter" + "wipe_extra_perimeter", + "wipe_speed", }; assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end())); } @@ -4572,6 +4746,7 @@ void PrintConfigDef::init_milling_params() def->tooltip = L("This is the diameter of your cutting tool."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats(3.14)); def = this->add("milling_offset", coPoints); @@ -4583,6 +4758,7 @@ void PrintConfigDef::init_milling_params() "from the XY coordinate)."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionPoints( Vec2d(0,0) )); def = this->add("milling_z_offset", coFloats); @@ -4591,6 +4767,7 @@ void PrintConfigDef::init_milling_params() def->tooltip = L("."); def->sidetext = L("mm"); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloats(0)); def = this->add("milling_z_lift", coFloats); @@ -4609,6 +4786,7 @@ void PrintConfigDef::init_milling_params() " previous_extruder is the 'extruder number' of the previous tool, it may be a normal extruder, if it's below the number of extruders." " The number of extruder is available at [extruder] and the number of milling tool is available at [milling_cutter]."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings("")); def = this->add("milling_toolchange_end_gcode", coStrings); @@ -4619,6 +4797,7 @@ void PrintConfigDef::init_milling_params() " next_extruder is the 'extruder number' of the next tool, it may be a normal extruder, if it's below the number of extruders." " The number of extruder is available at [extruder]and the number of milling tool is available at [milling_cutter]."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings("")); def = this->add("milling_post_process", coBool); @@ -5449,99 +5628,124 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } } -void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, const DynamicConfig& all_conf) { - - std::unordered_set<std::string> to_remove_keys = { -"thumbnails_color", -"thumbnails_custom_color", -"thumbnails_with_bed", -"thumbnails_with_support", +std::unordered_set<std::string> prusa_export_to_remove_keys = { "allow_empty_layers", "avoid_crossing_not_first_layer", -"top_fan_speed", -"over_bridge_flow_ratio", +"bridge_internal_fan_speed", "bridge_overlap", "bridge_speed_internal", -"brim_inside_holes", -"brim_width_interior", -"brim_ears", +"bridged_infill_margin", "brim_ears_detection_length", "brim_ears_max_angle", "brim_ears_pattern", +"brim_ears", +"brim_inside_holes", "brim_offset", +"brim_width_interior", "chamber_temperature", -"complete_objects_one_skirt", "complete_objects_one_brim", +"complete_objects_one_skirt", "complete_objects_sort", -"solid_fill_pattern", +"curve_smoothing_angle_concave", +"curve_smoothing_angle_convex", +"curve_smoothing_cutoff_dist", +"curve_smoothing_precision", "enforce_full_fill_volume", +"exact_last_layer_height", "external_infill_margin", -"bridged_infill_margin", "external_perimeter_cut_corners", +"external_perimeter_extrusion_spacing", "external_perimeter_fan_speed", "external_perimeter_overlap", -"perimeter_overlap", -"perimeter_bonding", -"external_perimeters_vase", -"external_perimeters_nothole", "external_perimeters_hole", -"perimeter_loop", -"perimeter_loop_seam", -"extra_perimeters_overhangs", +"external_perimeters_nothole", +"external_perimeters_vase", "extra_perimeters_odd_layers", -"only_one_perimeter_top", -"only_one_perimeter_top_other_algo", -"extruder_temperature_offset", +"extra_perimeters_overhangs", "extruder_fan_offset", -"print_extrusion_multiplier", +"extruder_temperature_offset", +"extrusion_spacing", +"fan_kickstart", +"fan_percentage", +"fan_speedup_overhangs", +"fan_speedup_time", +"feature_gcode", +"filament_cooling_zone_pause", +"filament_dip_extraction_speed", +"filament_dip_insertion_speed", +"filament_enable_toolchange_part_fan", +"filament_enable_toolchange_temp", "filament_max_speed", "filament_max_wipe_tower_speed", -"filament_enable_toolchange_temp", -"filament_use_fast_skinnydip", -"filament_enable_toolchange_part_fan", -"filament_toolchange_part_fan_speed", -"filament_use_skinnydip", "filament_melt_zone_pause", -"filament_cooling_zone_pause", -"filament_dip_insertion_speed", -"filament_dip_extraction_speed", -"filament_toolchange_temp", -"filament_skinnydip_distance", "filament_shrink", +"filament_skinnydip_distance", +"filament_toolchange_part_fan_speed", +"filament_toolchange_temp", +"filament_use_fast_skinnydip", +"filament_use_skinnydip", +"filament_wipe_advanced_pigment", "fill_angle_increment", +"fill_smooth_distribution", +"fill_smooth_width", "fill_top_flow_ratio", "fill_top_flow_ratio", +"first_layer_extrusion_spacing", "first_layer_flow_ratio", -"fill_smooth_width", -"fill_smooth_distribution", "first_layer_infill_speed", -"gap_fill", +"first_layer_min_speed", +"first_layer_size_compensation_layers", +"gap_fill_infill", "gap_fill_min_area", "gap_fill_overlap", -"gap_fill_infill", -"infill_dense", +"gap_fill", +"gcode_filename_illegal_char", +"hole_size_compensation", +"hole_size_threshold", +"hole_to_polyhole_threshold", +"hole_to_polyhole_twisted", +"hole_to_polyhole", "infill_connection", "infill_dense_algo", -"feature_gcode", -"exact_last_layer_height", -"fan_speedup_time", -"fan_speedup_overhangs", -"fan_percentage", -"fan_kickstart", +"infill_dense", +"infill_extrusion_spacing", "machine_max_acceleration_travel", "max_speed_reduction", +"milling_after_z", +"milling_cutter", +"milling_diameter", +"milling_extra_size", +"milling_offset", +"milling_post_process", +"milling_speed", +"milling_toolchange_end_gcode", +"milling_toolchange_start_gcode", +"milling_z_lift", +"milling_z_offset", "min_length", "min_width_top_surface", -"printhost_apikey", -"printhost_cafile", -"print_host", +"model_precision", +"no_perimeter_unsupported_algo", +"only_one_perimeter_top_other_algo", +"only_one_perimeter_top", +"over_bridge_flow_ratio", +"overhangs_reverse_threshold", +"overhangs_reverse", "overhangs_speed", "overhangs_width_speed", -"overhangs_reverse", -"overhangs_reverse_threshold", -"no_perimeter_unsupported_algo", -"support_material_solid_first_layer", +"perimeter_bonding", +"perimeter_extrusion_spacing", +"perimeter_loop_seam", +"perimeter_loop", +"perimeter_overlap", +"perimeter_round_corners", +"print_extrusion_multiplier", +"print_host", "print_retract_length", +"print_retract_lift", +"print_temperature", +"printhost_apikey", +"printhost_cafile", "retract_lift_first_layer", "retract_lift_top", "seam_angle_cost", @@ -5549,65 +5753,46 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, "skirt_brim", "skirt_distance_from_brim", "skirt_extrusion_width", -"small_perimeter_min_length", "small_perimeter_max_length", -"curve_smoothing_angle_convex", -"curve_smoothing_angle_concave", -"curve_smoothing_precision", -"curve_smoothing_cutoff_dist", -"model_precision", -"support_material_contact_distance_type", +"small_perimeter_min_length", +"solid_fill_pattern", +"solid_infill_extrusion_spacing", +"start_gcode_manual", "support_material_contact_distance_bottom", +"support_material_contact_distance_type", "support_material_interface_pattern", -"print_temperature", -"print_retract_lift", -"thin_perimeters", +"support_material_solid_first_layer", "thin_perimeters_all", +"thin_perimeters", +"thin_walls_merge", "thin_walls_min_width", "thin_walls_overlap", -"thin_walls_merge", "thin_walls_speed", +"thumbnails_color", +"thumbnails_custom_color", +"thumbnails_with_bed", +"thumbnails_with_support", "time_estimation_compensation", "tool_name", -"wipe_advanced", -"wipe_advanced_nozzle_melted_volume", -"filament_wipe_advanced_pigment", -"wipe_advanced_multiplier", +"top_fan_speed", +"top_infill_extrusion_spacing", +"travel_acceleration", +"travel_speed_z", "wipe_advanced_algo", -"wipe_tower_brim", +"wipe_advanced_multiplier", +"wipe_advanced_nozzle_melted_volume", +"wipe_advanced", "wipe_extra_perimeter", +"wipe_speed", +"wipe_tower_brim", "xy_inner_size_compensation", -"hole_size_compensation", -"hole_size_threshold", -"hole_to_polyhole", -"hole_to_polyhole_threshold", "z_step", -"milling_cutter", -"milling_diameter", -"milling_offset", -"milling_z_offset", -"milling_z_lift", -"milling_toolchange_start_gcode", -"milling_toolchange_end_gcode", -"milling_post_process", -"milling_extra_size", -"milling_after_z", -"milling_speed", -"extrusion_spacing", -"first_layer_extrusion_spacing", -"perimeter_extrusion_spacing", -"external_perimeter_extrusion_spacing", -"infill_extrusion_spacing", -"solid_infill_extrusion_spacing", -"top_infill_extrusion_spacing", -"start_gcode_manual", -"perimeter_round_corners", -"travel_speed_z", -"first_layer_size_compensation_layers", -"travel_acceleration", - }; +}; + +void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, const DynamicConfig& all_conf) { + //looks if it's to be removed, or have to be transformed - if (to_remove_keys.find(opt_key) != to_remove_keys.end()) { + if (prusa_export_to_remove_keys.find(opt_key) != prusa_export_to_remove_keys.end()) { opt_key = ""; value = ""; } else if (opt_key.find("_pattern") != std::string::npos) { @@ -5635,7 +5820,7 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, || "overhangs_speed" == opt_key || "ironing_speed" == opt_key){ // remove '%' if (value.find("%") != std::string::npos) { - value = std::to_string(all_conf.get_abs_value(opt_key)); + value = std::to_string(all_conf.get_computed_value(opt_key)); } } else if ("gap_fill_speed" == opt_key && all_conf.has("gap_fill") && !all_conf.option<ConfigOptionBool>("gap_fill")->value) { value = "0"; @@ -5658,7 +5843,7 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, double val = all_conf.option<ConfigOptionFloatOrPercent>("support_material_contact_distance_top")->get_abs_value(all_conf.option<ConfigOptionFloats>("nozzle_diameter")->values.front()); if (SupportZDistanceType::zdFilament == dist_type) { // not exact but good enough effort val += all_conf.option<ConfigOptionFloats>("nozzle_diameter")->values.front(); - val -= all_conf.get_abs_value("layer_height"); + val -= all_conf.get_computed_value("layer_height", 0); } value = boost::lexical_cast<std::string>(val); } @@ -5738,7 +5923,6 @@ double min_object_distance(const ConfigBase &cfg) return ret; }*/ - double PrintConfig::min_object_distance() const { return PrintConfig::min_object_distance(static_cast<const ConfigBase*>(this)); @@ -5770,7 +5954,9 @@ double PrintConfig::min_object_distance(const ConfigBase *config, double ref_hei base_dist = extruder_clearance_radius; } - const double first_layer_height = config->get_abs_value("first_layer_height"); + // we use the max nozzle, just to be on the safe side + //ideally, we should use print::first_layer_height() + const double first_layer_height = dynamic_cast<const ConfigOptionFloatOrPercent*>(config->option("first_layer_height"))->get_abs_value(max_nozzle_diam); //add the skirt int skirts = config->option("skirts")->getInt(); if (skirts > 0 && ref_height == 0) @@ -5789,7 +5975,7 @@ double PrintConfig::min_object_distance(const ConfigBase *config, double ref_hei //set to 0 becasue it's incorporated into the base_dist, so we don't want to be added in to it again. skirt_dist = 0; } else { - double skirt_height = ((double)config->option("skirt_height")->getInt() - 1) * config->get_abs_value("layer_height") + first_layer_height; + double skirt_height = ((double)config->option("skirt_height")->getInt() - 1) * config->get_computed_value("layer_height") + first_layer_height; if (ref_height <= skirt_height) { skirt_dist = config->option("skirt_distance")->getFloat(); Flow skirt_flow = Flow::new_from_config_width( @@ -5845,6 +6031,8 @@ void DynamicPrintConfig::normalize_fdm() // this->option("support_material_interface_extruder", true)->setInt(extruder); } } + if (this->has("first_layer_extruder")) + this->erase("first_layer_extruder"); if (!this->has("solid_infill_extruder") && this->has("infill_extruder")) this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt()); @@ -6219,13 +6407,14 @@ bool DynamicPrintConfig::value_changed(const t_config_option_key& opt_key, const std::string FullPrintConfig::validate() { // --layer-height - if (this->get_abs_value("layer_height") <= 0) + if (this->get_computed_value("layer_height") <= 0) return "Invalid value for --layer-height"; - if (fabs(fmod(this->get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) + if (fabs(fmod(this->get_computed_value("layer_height"), SCALING_FACTOR)) > 1e-4) return "--layer-height must be a multiple of print resolution"; // --first-layer-height - if (this->get_abs_value("first_layer_height") <= 0) + //if (this->get_abs_value("first_layer_height") <= 0) //can't do that, as the extruder isn't defined + if(this->first_layer_height.value <= 0) return "Invalid value for --first-layer-height"; // --filament-diameter diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8175e11a5..ea6f18673 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -798,6 +798,7 @@ public: ConfigOptionFloatOrPercent infill_anchor_max; ConfigOptionBool hole_to_polyhole; ConfigOptionFloatOrPercent hole_to_polyhole_threshold; + ConfigOptionBool hole_to_polyhole_twisted; ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; @@ -914,6 +915,7 @@ protected: OPT_PTR(infill_anchor_max); OPT_PTR(hole_to_polyhole); OPT_PTR(hole_to_polyhole_threshold); + OPT_PTR(hole_to_polyhole_twisted); OPT_PTR(infill_extruder); OPT_PTR(infill_extrusion_width); OPT_PTR(infill_every_layers); @@ -1088,12 +1090,14 @@ public: ConfigOptionFloats filament_cooling_final_speed; ConfigOptionStrings filament_ramming_parameters; ConfigOptionBool gcode_comments; + ConfigOptionString gcode_filename_illegal_char; ConfigOptionEnum<GCodeFlavor> gcode_flavor; ConfigOptionBool gcode_label_objects; ConfigOptionInt gcode_precision_xyz; ConfigOptionInts gcode_precision_e; ConfigOptionString layer_gcode; ConfigOptionString feature_gcode; + ConfigOptionFloat max_gcode_per_second; ConfigOptionFloat max_print_speed; ConfigOptionFloat max_volumetric_speed; #ifdef HAS_PRESSURE_EQUALIZER @@ -1139,6 +1143,7 @@ public: ConfigOptionFloat wipe_advanced_multiplier; ConfigOptionFloats wipe_extra_perimeter; ConfigOptionEnum<WipeAlgo> wipe_advanced_algo; + ConfigOptionFloats wipe_speed; ConfigOptionFloat z_step; ConfigOptionString color_change_gcode; ConfigOptionString pause_print_gcode; @@ -1201,12 +1206,14 @@ protected: OPT_PTR(filament_cooling_final_speed); OPT_PTR(filament_ramming_parameters); OPT_PTR(gcode_comments); + OPT_PTR(gcode_filename_illegal_char); OPT_PTR(gcode_flavor); OPT_PTR(gcode_label_objects); OPT_PTR(gcode_precision_xyz); OPT_PTR(gcode_precision_e); OPT_PTR(layer_gcode); OPT_PTR(feature_gcode); + OPT_PTR(max_gcode_per_second); OPT_PTR(max_print_speed); OPT_PTR(max_volumetric_speed); OPT_PTR(milling_z_lift); @@ -1252,6 +1259,7 @@ protected: OPT_PTR(wipe_advanced_multiplier); OPT_PTR(wipe_advanced_algo); OPT_PTR(wipe_extra_perimeter); + OPT_PTR(wipe_speed); OPT_PTR(z_step); OPT_PTR(color_change_gcode); OPT_PTR(pause_print_gcode); @@ -1276,6 +1284,7 @@ public: ConfigOptionInts bed_temperature; ConfigOptionFloatOrPercent bridge_acceleration; ConfigOptionInts bridge_fan_speed; + ConfigOptionInts bridge_internal_fan_speed; ConfigOptionInts chamber_temperature; ConfigOptionBool complete_objects; ConfigOptionBool complete_objects_one_skirt; @@ -1301,6 +1310,7 @@ public: ConfigOptionPercent first_layer_flow_ratio; ConfigOptionFloatOrPercent first_layer_speed; ConfigOptionFloatOrPercent first_layer_infill_speed; + ConfigOptionFloatOrPercent first_layer_min_speed; ConfigOptionInts first_layer_temperature; ConfigOptionInts full_fan_speed_layer; ConfigOptionFloatOrPercent infill_acceleration; @@ -1375,6 +1385,7 @@ protected: OPT_PTR(bed_temperature); OPT_PTR(bridge_acceleration); OPT_PTR(bridge_fan_speed); + OPT_PTR(bridge_internal_fan_speed); OPT_PTR(chamber_temperature); OPT_PTR(complete_objects); OPT_PTR(complete_objects_one_skirt); @@ -1400,6 +1411,7 @@ protected: OPT_PTR(first_layer_flow_ratio); OPT_PTR(first_layer_speed); OPT_PTR(first_layer_infill_speed); + OPT_PTR(first_layer_min_speed); OPT_PTR(first_layer_temperature); OPT_PTR(full_fan_speed_layer); OPT_PTR(infill_acceleration); @@ -1938,6 +1950,7 @@ public: const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } int extruder() const { return opt_int("extruder"); } + int first_layer_extruder() const { return opt_int("first_layer_extruder"); } double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } @@ -1950,8 +1963,6 @@ public: // Not thread safe! Should not be called from other than the main thread! void touch() { m_timestamp = ++ s_last_timestamp; } - void to_prusa(t_config_option_key& opt_key, std::string& value) const - { m_data.to_prusa(opt_key, value); } private: friend class cereal::access; template<class Archive> void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 457931b7b..3296f3eb6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -151,25 +151,41 @@ namespace Slic3r { } - Polygon create_polyhole(const Point center, const coord_t radius, const coord_t nozzle_diameter) + + Polygons create_polyholes(const Point center, const coord_t radius, const coord_t nozzle_diameter, bool multiple) { // n = max(round(2 * d), 3); // for 0.4mm nozzle - size_t nb_polygons = (int)std::max(3, (int)std::round(4.0 * unscaled(radius) * 0.4 / unscaled(nozzle_diameter))); + size_t nb_edges = (int)std::max(3, (int)std::round(4.0 * unscaled(radius) * 0.4 / unscaled(nozzle_diameter))); // cylinder(h = h, r = d / cos (180 / n), $fn = n); - Points pts; - const float new_radius = radius / std::cos(PI / nb_polygons); - for (int i = 0; i < nb_polygons; ++i) { - float angle = (PI * 2 * i) / nb_polygons; - pts.emplace_back(center.x() + new_radius * cos(angle), center.y() + new_radius * sin(angle)); + //create x polyholes by rotation if multiple + int nb_polyhole = 1; + float rotation = 0; + if (multiple) { + nb_polyhole = 5; + rotation = 2 * float(PI) / (nb_edges * nb_polyhole); + } + Polygons list; + for (int i_poly = 0; i_poly < nb_polyhole; i_poly++) + list.emplace_back(); + for (int i_poly = 0; i_poly < nb_polyhole; i_poly++) { + Polygon& pts = (((i_poly % 2) == 0) ? list[i_poly / 2] : list[(nb_polyhole + 1) / 2 + i_poly / 2]); + const float new_radius = radius / float(std::cos(PI / nb_edges)); + for (int i_edge = 0; i_edge < nb_edges; ++i_edge) { + float angle = rotation * i_poly + (float(PI) * 2 * i_edge) / nb_edges; + pts.points.emplace_back(center.x() + new_radius * cos(angle), center.y() + new_radius * sin(angle)); + } + pts.make_clockwise(); } - return Polygon{ pts }; + //alternate + return list; } void PrintObject::_transform_hole_to_polyholes() { // get all circular holes for each layer // the id is center-diameter-extruderid - std::vector<std::vector<std::pair<std::tuple<Point, float, int, coord_t>, Polygon*>>> layerid2center; + //the tuple is Point center; float diameter_max; int extruder_id; coord_t max_variation; bool twist; + std::vector<std::vector<std::pair<std::tuple<Point, float, int, coord_t, bool>, Polygon*>>> layerid2center; for (size_t i = 0; i < this->m_layers.size(); i++) layerid2center.emplace_back(); tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), @@ -196,9 +212,10 @@ namespace Slic3r { } // SCALED_EPSILON was a bit too harsh. Now using a config, as some may want some harsh setting and some don't. coord_t max_variation = std::max(SCALED_EPSILON, scale_(this->m_layers[layer_idx]->m_regions[region_idx]->region()->config().hole_to_polyhole_threshold.get_abs_value(unscaled(diameter_sum / hole.points.size())))); + bool twist = this->m_layers[layer_idx]->m_regions[region_idx]->region()->config().hole_to_polyhole_twisted.value; if (diameter_max - diameter_min < max_variation * 2) { layerid2center[layer_idx].emplace_back( - std::tuple<Point, float, int, coord_t>{center, diameter_max, layer->m_regions[region_idx]->region()->config().perimeter_extruder.value, max_variation}, & hole); + std::tuple<Point, float, int, coord_t, bool>{center, diameter_max, layer->m_regions[region_idx]->region()->config().perimeter_extruder.value, max_variation, twist}, & hole); } } } @@ -209,7 +226,7 @@ namespace Slic3r { } }); //sort holes per center-diameter - std::map<std::tuple<Point, float, int, coord_t>, std::vector<std::pair<Polygon*, int>>> id2layerz2hole; + std::map<std::tuple<Point, float, int, coord_t, bool>, std::vector<std::pair<Polygon*, int>>> id2layerz2hole; //search & find hole that span at least X layers const size_t min_nb_layers = 2; @@ -217,7 +234,7 @@ namespace Slic3r { for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) { for (size_t hole_idx = 0; hole_idx < layerid2center[layer_idx].size(); ++hole_idx) { //get all other same polygons - std::tuple<Point, float, int, coord_t>& id = layerid2center[layer_idx][hole_idx].first; + std::tuple<Point, float, int, coord_t, bool>& id = layerid2center[layer_idx][hole_idx].first; float max_z = layers()[layer_idx]->print_z; std::vector<std::pair<Polygon*, int>> holes; holes.emplace_back(layerid2center[layer_idx][hole_idx].second, layer_idx); @@ -225,7 +242,7 @@ namespace Slic3r { if (layers()[search_layer_idx]->print_z - layers()[search_layer_idx]->height - max_z > EPSILON) break; //search an other polygon with same id for (size_t search_hole_idx = 0; search_hole_idx < layerid2center[search_layer_idx].size(); ++search_hole_idx) { - std::tuple<Point, float, int, coord_t>& search_id = layerid2center[search_layer_idx][search_hole_idx].first; + std::tuple<Point, float, int, coord_t, bool>& search_id = layerid2center[search_layer_idx][search_hole_idx].first; if (std::get<2>(id) == std::get<2>(search_id) && std::get<0>(id).distance_to(std::get<0>(search_id)) < std::get<3>(id) && std::abs(std::get<1>(id) - std::get<1>(search_id)) < std::get<3>(id) @@ -246,9 +263,9 @@ namespace Slic3r { } //create a polyhole per id and replace holes points by it. for (auto entry : id2layerz2hole) { - Polygon polyhole = create_polyhole(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1))); - polyhole.make_clockwise(); + Polygons polyholes = create_polyholes(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1)), std::get<4>(entry.first)); for (auto& poly_to_replace : entry.second) { + Polygon polyhole = polyholes[poly_to_replace.second % polyholes.size()]; //search the clone in layers->slices for (ExPolygon& explo_slice : m_layers[poly_to_replace.second]->lslices) { for (Polygon& poly_slice : explo_slice.holes) { @@ -286,6 +303,11 @@ namespace Slic3r { m_typed_slices = false; } + // atomic counter for gui progress + std::atomic<int> atomic_count{ 0 }; + int nb_layers_update = std::max(1, (int)m_layers.size() / 20); + std::chrono::time_point<std::chrono::system_clock> last_update = std::chrono::system_clock::now(); + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- @@ -359,10 +381,22 @@ namespace Slic3r { BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), - [this](const tbb::blocked_range<size_t>& range) { + [this, &atomic_count, &last_update, nb_layers_update](const tbb::blocked_range<size_t>& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + std::chrono::time_point<std::chrono::system_clock> start_make_perimeter = std::chrono::system_clock::now(); m_print->throw_if_canceled(); m_layers[layer_idx]->make_perimeters(); + + // updating progress + int nb_layers_done = (++atomic_count); + std::chrono::time_point<std::chrono::system_clock> end_make_perimeter = std::chrono::system_clock::now(); + if (nb_layers_done % nb_layers_update == 0 || (static_cast<std::chrono::duration<double>>(end_make_perimeter - start_make_perimeter)).count() > 5) { + if ((static_cast<std::chrono::duration<double>>(end_make_perimeter - last_update)).count() > 0.2) { + // note: i don't care if a thread erase last_update in-between here + last_update = std::chrono::system_clock::now(); + m_print->set_status( int((nb_layers_done * 100) / m_layers.size()), L("Generating perimeters: layer %s / %s"), { std::to_string(nb_layers_done), std::to_string(m_layers.size()) }); + } + } } } ); @@ -531,13 +565,30 @@ namespace Slic3r { if (this->set_started(posInfill)) { auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + // atomic counter for gui progress + std::atomic<int> atomic_count{ 0 }; + int nb_layers_update = std::max(1, (int)m_layers.size() / 20); + std::chrono::time_point<std::chrono::system_clock> last_update = std::chrono::system_clock::now(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &atomic_count , &last_update, nb_layers_update](const tbb::blocked_range<size_t>& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + std::chrono::time_point<std::chrono::system_clock> start_make_fill = std::chrono::system_clock::now(); m_print->throw_if_canceled(); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); + + // updating progress + int nb_layers_done = (++atomic_count); + std::chrono::time_point<std::chrono::system_clock> end_make_fill = std::chrono::system_clock::now(); + if (nb_layers_done % nb_layers_update == 0 || (static_cast<std::chrono::duration<double>>(end_make_fill - start_make_fill)).count() > 5) { + if ((static_cast<std::chrono::duration<double>>(end_make_fill - last_update)).count() > 0.2) { + // note: i don't care if a thread erase last_update in-between here + last_update = std::chrono::system_clock::now(); + m_print->set_status( int((nb_layers_done * 100) / m_layers.size()), L("Infilling layer %s / %s"), { std::to_string(nb_layers_done), std::to_string(m_layers.size()) }); + } + } } } ); @@ -2226,7 +2277,7 @@ namespace Slic3r { size_t num_extruders = print_config.nozzle_diameter.size(); object_config = object_config_from_model_object(object_config, model_object, num_extruders); - std::vector<uint16_t> object_extruders; + std::set<uint16_t> object_extruders; for (const ModelVolume* model_volume : model_object.volumes) if (model_volume->is_model_part()) { PrintRegion::collect_object_printing_extruders( @@ -2244,7 +2295,6 @@ namespace Slic3r { region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders), object_extruders); } - sort_remove_duplicates(object_extruders); if (object_max_z <= 0.f) object_max_z = (float)model_object.raw_bounding_box().size().z(); @@ -2252,17 +2302,29 @@ namespace Slic3r { } // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) - std::vector<uint16_t> PrintObject::object_extruders() const + std::set<uint16_t> PrintObject::object_extruders() const { - std::vector<uint16_t> extruders; - extruders.reserve(this->region_volumes.size() * 3); + std::set<uint16_t> extruders; for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) if (!this->region_volumes[idx_region].empty()) m_print->get_region(idx_region)->collect_object_printing_extruders(extruders); - sort_remove_duplicates(extruders); return extruders; } + double PrintObject::get_first_layer_height() const + { + //get object first layer height + double object_first_layer_height = config().first_layer_height.value; + if (config().first_layer_height.percent) { + object_first_layer_height = 1000000000; + for (uint16_t extruder_id : object_extruders()) { + double nozzle_diameter = print()->config().nozzle_diameter.values[extruder_id]; + object_first_layer_height = std::fmin(object_first_layer_height, config().first_layer_height.get_abs_value(nozzle_diameter)); + } + } + return object_first_layer_height; + } + bool PrintObject::update_layer_height_profile(const ModelObject& model_object, const SlicingParameters& slicing_parameters, std::vector<coordf_t>& layer_height_profile) { bool updated = false; @@ -3829,4 +3891,5 @@ static void fix_mesh_connectivity(TriangleMesh &mesh) return (it == m_layers.begin()) ? nullptr : *(--it); } + } // namespace Slic3r diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index e62b34ea6..94f1a65f3 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -108,13 +108,13 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.get_abs_value(1)); } -void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector<uint16_t> &object_extruders) +void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::set<uint16_t> &object_extruders) { // These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields. auto num_extruders = (int)print_config.nozzle_diameter.size(); auto emplace_extruder = [num_extruders, &object_extruders](int extruder_id) { int i = std::max(0, extruder_id - 1); - object_extruders.emplace_back((i >= num_extruders) ? 0 : i); + object_extruders.insert((i >= num_extruders) ? 0 : i); }; if (region_config.perimeters.value > 0 || object_config.brim_width.value > 0) emplace_extruder(region_config.perimeter_extruder); @@ -124,7 +124,7 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con emplace_extruder(region_config.solid_infill_extruder); } -void PrintRegion::collect_object_printing_extruders(std::vector<uint16_t> &object_extruders) const +void PrintRegion::collect_object_printing_extruders(std::set<uint16_t> &object_extruders) const { // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder. // If not, then there must be something wrong with the Print::apply() function. diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 76003b45f..6423f750a 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -72,17 +72,32 @@ coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_c return check_z_step(std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height), print_config.opt_float("z_step")); } + SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::vector<uint16_t> &object_extruders) + const std::set<uint16_t> &object_extruders) { //first layer height is got from the first_layer_height setting unless the value was garbage. // if the first_layer_height setting depends of the nozzle width, use the first one. Apply the z_step - coordf_t first_layer_height = (object_config.first_layer_height.get_abs_value(print_config.nozzle_diameter.empty() ? 0. : print_config.nozzle_diameter.get_at(0)) <= 0) ? - object_config.layer_height.value : - object_config.first_layer_height.get_abs_value(print_config.nozzle_diameter.get_at(0)); + + //get object first layer height + double first_layer_height = object_config.first_layer_height.value; + if (object_config.first_layer_height.percent) { + first_layer_height = 1000000000.; + for (uint16_t extruder_id : object_extruders) { + if (print_config.nozzle_diameter.size() <= extruder_id) + break; + double nozzle_diameter = print_config.nozzle_diameter.values[extruder_id]; + first_layer_height = std::min(first_layer_height, object_config.first_layer_height.get_abs_value(nozzle_diameter)); + } + if (first_layer_height == 1000000000.) + first_layer_height = 0; + } + + if (first_layer_height == 0) + object_config.layer_height.value; first_layer_height = check_z_step(first_layer_height, print_config.z_step); // If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0, // print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index e8e0888c8..4d18e342f 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -35,7 +35,7 @@ struct SlicingParameters const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::vector<uint16_t> &object_extruders); + const std::set<uint16_t> &object_extruders); // Has any raft layers? bool has_raft() const { return raft_layers() > 0; } diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index d85d77a77..f7d751b14 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -172,7 +172,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; if (! m_can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { // One of the support extruders is of "don't care" type. - auto object_extruders = m_object->print()->object_extruders(m_object->print()->objects()); + std::set<uint16_t> object_extruders = m_object->print()->object_extruders(m_object->print()->objects()); if (object_extruders.size() == 1 && *object_extruders.begin() == std::max<unsigned int>(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value)) // Object is printed with the same extruder as the support. diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 71103513b..1c5057687 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -16,6 +16,7 @@ #include <stdint.h> #include <stdarg.h> #include <vector> +#include <set> #include <cassert> #include <cmath> #include <type_traits> @@ -99,11 +100,10 @@ extern Semver SEMVER; template<typename T, typename Q> inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); } -inline double unscaled(double v) { return v * SCALING_FACTOR; } -inline coordf_t unscale_(coord_t v) { return v * SCALING_FACTOR; } -inline coord_t scale_t(coordf_t v) { return (coord_t)(v * UNSCALING_FACTOR); } -inline double scale_d(coordf_t v) { return (v * UNSCALING_FACTOR); } -inline double scale_d(coord_t v) { return (double(v) * UNSCALING_FACTOR); } +inline double unscaled(coord_t v) { return double(v) * SCALING_FACTOR; } +inline double unscaled(coordf_t v) { return v * SCALING_FACTOR; } +inline coord_t scale_t(double v) { return coord_t(v * UNSCALING_FACTOR); } +inline coordf_t scale_d(double v) { return coordf_t(v * UNSCALING_FACTOR); } enum Axis { X=0, @@ -126,6 +126,15 @@ inline void append(std::vector<T>& dest, const std::vector<T>& src) } template <typename T> +inline void append(std::set<T>& dest, const std::set<T>& src) +{ + if (dest.empty()) + dest = src; + else + dest.insert(src.begin(), src.end()); +} + +template <typename T> inline void append(std::vector<T>& dest, std::vector<T>&& src) { if (dest.empty()) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 4dcea595e..54ddeac97 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -337,7 +337,11 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) Polylines axes_lines; Polylines axes_lines_big; Polylines axes_lines_small; - for (coord_t x = bed_bbox.min(0), idx= 0; x <= bed_bbox.max(0); x += scale_(5.0), idx++) { + coord_t square = scale_(5); + while (bed_bbox.radius() > square * 100) { + square *= 10; + } + for (coord_t x = bed_bbox.min(0), idx= 0; x <= bed_bbox.max(0); x += square, idx++) { Polyline line; line.append(Point(x, bed_bbox.min(1))); line.append(Point(x, bed_bbox.max(1))); @@ -348,7 +352,7 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) else axes_lines.push_back(line); } - for (coord_t y = bed_bbox.min(1), idx = 0; y <= bed_bbox.max(1); y += scale_(5.0), idx++) { + for (coord_t y = bed_bbox.min(1), idx = 0; y <= bed_bbox.max(1); y += square, idx++) { Polyline line; line.append(Point(bed_bbox.min(0), y)); line.append(Point(bed_bbox.max(0), y)); diff --git a/src/slic3r/GUI/CalibrationAbstractDialog.cpp b/src/slic3r/GUI/CalibrationAbstractDialog.cpp index 4b72513f6..620ce5e46 100644 --- a/src/slic3r/GUI/CalibrationAbstractDialog.cpp +++ b/src/slic3r/GUI/CalibrationAbstractDialog.cpp @@ -124,6 +124,7 @@ ModelObject* CalibrationAbstractDialog::add_part(ModelObject* model_object, std: // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + new_volume->config.set_key_value("first_layer_extruder", new ConfigOptionInt(0)); //move to bed /* const TriangleMesh& hull = new_volume->get_convex_hull(); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 9fcf901e5..532c5d29b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -430,7 +430,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : { "solid_fill_pattern", "infill_connection_solid" }) toggle_field(el, has_solid_infill); // should be top_solid_layers") > 1 || bottom_solid_layers") > 1 - toggle_field("hole_to_polyhole_threshold", config->opt_bool("hole_to_polyhole")); + for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" }) + toggle_field(el, config->opt_bool("hole_to_polyhole")); bool have_default_acceleration = config->option<ConfigOptionFloatOrPercent>("default_acceleration")->value > 0; for (auto el : { "perimeter_acceleration", "infill_acceleration", diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 92811abb4..2157709e9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -65,6 +65,7 @@ #include <float.h> #include <algorithm> #include <cmath> +#include <set> #include "DoubleSlider.hpp" #include <imgui/imgui_internal.h> @@ -1339,9 +1340,10 @@ void GLCanvas3D::reset_volumes(bool is_destroying) _set_current(); - m_selection.clear(is_destroying); - m_volumes.clear(); - m_dirty = true; + m_selection.clear(is_destroying); + m_volumes.release_geometry(); + m_volumes.clear(); + m_dirty = true; if(!is_destroying) _set_warning_texture(WarningTexture::ObjectOutside, false); @@ -2326,21 +2328,21 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old.finalize_geometry(gl_initialized); } -void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) +void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors) { - m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); + if (m_dirty_gcode) { + m_dirty_gcode = false; + m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); - if (wxGetApp().is_editor()) { - m_gcode_viewer.update_shells_color_by_extruder(m_config); - _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); - } -} + if (wxGetApp().is_editor()) { + m_gcode_viewer.update_shells_color_by_extruder(m_config); + _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); + } -void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors) -{ - m_gcode_viewer.refresh(gcode_result, str_tool_colors); - set_as_dirty(); - request_extra_frame(); + m_gcode_viewer.refresh(gcode_result, str_tool_colors); + set_as_dirty(); + request_extra_frame(); + } } #if ENABLE_RENDER_PATH_REFRESH_AFTER_OPTIONS_CHANGE @@ -2367,23 +2369,27 @@ void GLCanvas3D::load_sla_preview() void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values) { - const Print *print = this->fff_print(); + const Print* print = this->fff_print(); if (print == nullptr) return; _set_current(); - // Release OpenGL data before generating new data. - this->reset_volumes(); + if (m_dirty_preview || m_volumes.empty()) { + m_dirty_preview = false; + // Release OpenGL data before generating new data. + this->reset_volumes(); + //note: this isn't releasing all the memory in all os, can make it crash on linux for exemple. - _load_print_toolpaths(); - _load_wipe_tower_toolpaths(str_tool_colors); - for (const PrintObject* object : print->objects()) + _load_print_toolpaths(); + _load_wipe_tower_toolpaths(str_tool_colors); + for (const PrintObject* object : print->objects()) _load_print_object_toolpaths(*object, str_tool_colors, color_print_values); - _update_toolpath_volumes_outside_state(); - _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); + _update_toolpath_volumes_outside_state(); + _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } +} void GLCanvas3D::bind_event_handlers() { @@ -4835,7 +4841,7 @@ bool GLCanvas3D::_init_collapse_toolbar() bool GLCanvas3D::_set_current() { return m_context != nullptr && m_canvas->SetCurrent(*m_context); - } +} void GLCanvas3D::_resize(unsigned int w, unsigned int h) { @@ -5801,9 +5807,11 @@ void GLCanvas3D::_load_print_toolpaths() //skirt & brim from objects for (const PrintObject* print_object : print->objects()) { if (!print_object->brim().empty()) - _3DScene::extrusionentity_to_verts(print_object->brim(), print_zs[i], Point(0, 0), *volume); + for(const PrintInstance& inst : print_object->instances()) + _3DScene::extrusionentity_to_verts(print_object->brim(), print_zs[i], inst.shift, *volume); if (print_object->skirt_first_layer()) - _3DScene::extrusionentity_to_verts(*print_object->skirt_first_layer(), print_zs[i], Point(0, 0), *volume); + for (const PrintInstance& inst : print_object->instances()) + _3DScene::extrusionentity_to_verts(*print_object->skirt_first_layer(), print_zs[i], inst.shift, *volume); } } //skirts from print @@ -5812,7 +5820,8 @@ void GLCanvas3D::_load_print_toolpaths() //skirts from objects for (const PrintObject* print_object : print->objects()) { if ( !print_object->skirt().empty() && (i != 0 || !print_object->skirt_first_layer())) - _3DScene::extrusionentity_to_verts(print_object->skirt(), print_zs[i], Point(0, 0), *volume); + for (const PrintInstance& inst : print_object->instances()) + _3DScene::extrusionentity_to_verts(print_object->skirt(), print_zs[i], inst.shift, *volume); } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7d6238c80..a13df2036 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -475,6 +475,8 @@ private: // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; + bool m_dirty_preview = true; + bool m_dirty_gcode = true; bool m_initialized; bool m_apply_zoom_to_volumes_filter; mutable std::vector<int> m_hover_volume_idxs; @@ -557,6 +559,9 @@ public: void post_event(wxEvent &&event); void set_as_dirty(); + void set_preview_dirty() { m_dirty_preview = true; } + bool is_preview_dirty() { return m_dirty_preview; } + void set_gcode_viewer_dirty() { m_dirty_gcode = true; } void set_items_show(bool show_objects, bool show_gcode); unsigned int get_volumes_count() const; @@ -666,8 +671,7 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); - void load_gcode_preview(const GCodeProcessor::Result& gcode_result); - void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors); + void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors); #if ENABLE_RENDER_PATH_REFRESH_AFTER_OPTIONS_CHANGE void refresh_gcode_preview_render_paths(); #endif // ENABLE_RENDER_PATH_REFRESH_AFTER_OPTIONS_CHANGE diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d6e8cc242..207b8f597 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -416,8 +416,9 @@ void Preview::reload_print(bool keep_volumes) #ifdef __linux__ m_volumes_cleanup_required || #endif /* __linux__ */ - !keep_volumes) + (!keep_volumes && m_canvas->is_preview_dirty())) { + m_canvas->set_preview_dirty(); m_canvas->reset_volumes(); m_loaded = false; #ifdef __linux__ @@ -1020,8 +1021,9 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->set_selected_extruder(0); if (current_force_state == ForceState::ForceGcode || (gcode_preview_data_valid && current_force_state != ForceState::ForceExtrusions)) { // Load the real G-code preview. - m_canvas->load_gcode_preview(*m_gcode_result); - m_canvas->refresh_gcode_preview(*m_gcode_result, colors); + if (current_force_state == ForceState::NoForce) + m_canvas->set_items_show(false, true); + m_canvas->load_gcode_preview(*m_gcode_result, colors); m_left_sizer->Show(m_bottom_toolbar_panel); m_left_sizer->Layout(); Refresh(); @@ -1029,6 +1031,8 @@ void Preview::load_print_as_fff(bool keep_z_range) m_loaded = true; } else { // Load the initial preview based on slices, not the final G-code. + if (current_force_state == ForceState::NoForce) + m_canvas->set_items_show(true, false); m_canvas->load_preview(colors, color_print_values); m_left_sizer->Hide(m_bottom_toolbar_panel); m_left_sizer->Layout(); @@ -1039,8 +1043,9 @@ void Preview::load_print_as_fff(bool keep_z_range) // all layers filtered out hide_layers_slider(); m_canvas_widget->Refresh(); - } else + } else { update_layers_slider(zs, keep_z_range); + } } #if ENABLE_PREVIEW_TYPE_CHANGE @@ -1071,6 +1076,13 @@ void Preview::load_print_as_fff(bool keep_z_range) #endif // ENABLE_PREVIEW_TYPE_CHANGE } +void Preview::reset_gcode_toolpaths() +{ + if (current_force_state == ForceState::NoForce) + m_canvas->set_items_show(true, false); + m_canvas->reset_gcode_toolpaths(); +} + void Preview::load_print_as_sla() { if (m_loaded || (m_process->current_printer_technology() != ptSLA)) diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 8c72d7a3f..5b4d7f50e 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -203,6 +203,7 @@ Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSl bool can_display_gcode(); bool can_display_volume(); + void reset_gcode_toolpaths(); private: ForceState current_force_state = ForceState::NoForce; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1dac5e694..c5269a882 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1700,7 +1700,7 @@ void MainFrame::quick_slice(const int qs) wxFileDialog dlg(this, from_u8((boost::format(_utf8(L("Save %s file as:"))) % ((qs & qsExportSVG) ? _L("SVG") : _L("G-code"))).str()), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxFD_SAVE | (wxGetApp().app_config->get_show_overwrite_dialog() ? wxFD_OVERWRITE_PROMPT : 0)); if (dlg.ShowModal() != wxID_OK) return; output_file = dlg.GetPath(); @@ -1711,7 +1711,7 @@ void MainFrame::quick_slice(const int qs) else if (qs & qsExportPNG) { wxFileDialog dlg(this, _L("Save zip file as:"), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), - get_base_name(output_file), "*.sl1", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + get_base_name(output_file), "*.sl1", wxFD_SAVE | (wxGetApp().app_config->get_show_overwrite_dialog() ? wxFD_OVERWRITE_PROMPT : 0)); if (dlg.ShowModal() != wxID_OK) return; output_file = dlg.GetPath(); @@ -1773,7 +1773,7 @@ void MainFrame::repair_stl() { wxFileDialog dlg( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"), get_dir_name(output_file), get_base_name(output_file, ".obj"), - file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + file_wildcards(FT_OBJ), wxFD_SAVE | (wxGetApp().app_config->get_show_overwrite_dialog() ? wxFD_OVERWRITE_PROMPT : 0)); if (dlg.ShowModal() != wxID_OK) return; output_file = dlg.GetPath(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4337cb5f9..fcd568603 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2750,7 +2750,7 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) wxFileDialog dlg(q, dlg_title, from_path(output_file.parent_path()), from_path(output_file.filename()), - wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wildcard, wxFD_SAVE |(wxGetApp().app_config->get_show_overwrite_dialog() ? wxFD_OVERWRITE_PROMPT : 0) ); if (dlg.ShowModal() != wxID_OK) return wxEmptyString; @@ -3576,8 +3576,14 @@ void Plater::priv::set_current_panel(wxTitledPanel* panel) // FIXME: it may be better to have a single function making this check and let it be called wherever needed bool export_in_progress = this->background_process.is_export_scheduled(); bool model_fits = view3D->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside; - if (!model.objects.empty() && !export_in_progress && model_fits) - this->q->reslice(); + + if (!model.objects.empty() && !export_in_progress && model_fits) { + //check if already slicing + bool already_running = this->background_process.state() == BackgroundSlicingProcess::State::STATE_RUNNING || + this->background_process.state() == BackgroundSlicingProcess::State::STATE_STARTED; + if(!already_running) + this->q->reslice(); + } // keeps current gcode preview, if any preview->reload_print(true); } @@ -3669,7 +3675,24 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) } this->statusbar()->set_progress(evt.status.percent); - this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…")); + if (evt.status.args.empty()) { + this->statusbar()->set_status_text(_(evt.status.main_text) + wxString::FromUTF8("…")); + } else { + std::vector<wxString> arg_lst; + for (std::string str : evt.status.args) + arg_lst.push_back(_(str)); + //FIXME use var-args list + if (arg_lst.size() == 1) + this->statusbar()->set_status_text(wxString::Format(_(evt.status.main_text), arg_lst[0])); + else if (arg_lst.size() == 2) + this->statusbar()->set_status_text(wxString::Format(_(evt.status.main_text), arg_lst[0], arg_lst[1])); + else if (arg_lst.size() == 3) + this->statusbar()->set_status_text(wxString::Format(_(evt.status.main_text), arg_lst[0], arg_lst[1], arg_lst[2])); + else if (arg_lst.size() == 4) + this->statusbar()->set_status_text(wxString::Format(_(evt.status.main_text), arg_lst[0], arg_lst[1], arg_lst[2], arg_lst[3])); + else + this->statusbar()->set_status_text(wxString::Format(_(evt.status.main_text), arg_lst[0], arg_lst[1], arg_lst[2], arg_lst[3], arg_lst[4])); + } //notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f); } if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { @@ -3722,6 +3745,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) void Plater::priv::on_slicing_completed(wxCommandEvent & evt) { + preview->get_canvas3d()->set_gcode_viewer_dirty(); // auto_switch_preview == 0 means "no force tab change" // auto_switch_preview == 1 means "force tab change" // auto_switch_preview == 2 means "force tab change only if already on a plater one" @@ -3753,6 +3777,8 @@ void Plater::priv::on_slicing_began() { clear_warnings(); notification_manager->close_notification_of_type(NotificationType::SlicingComplete); + preview->get_canvas3d()->set_gcode_viewer_dirty(); + preview->get_canvas3d()->set_preview_dirty(); } void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid) { @@ -4390,7 +4416,7 @@ void Plater::priv::enable_preview_moves_slider(bool enable) void Plater::priv::reset_gcode_toolpaths() { - preview->get_canvas3d()->reset_gcode_toolpaths(); + preview->reset_gcode_toolpaths(); } bool Plater::priv::can_set_instance_to_object() const @@ -5506,12 +5532,12 @@ void Plater::export_gcode(bool prefer_removable) fs::path output_path; { - std::string ext = default_output_file.extension().string(); + std::string ext = default_output_file.extension().string(); wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"), start_dir, from_path(default_output_file.filename()), GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT + wxFD_SAVE | (wxGetApp().app_config->get_show_overwrite_dialog() ? wxFD_OVERWRITE_PROMPT : 0) ); if (dlg.ShowModal() == wxID_OK) output_path = into_path(dlg.GetPath()); @@ -6126,9 +6152,14 @@ void Plater::force_print_bed_update() void Plater::on_activate() { -#if defined(__linux__) || defined(_WIN32) // Activating the main frame, and no window has keyboard focus. // Set the keyboard focus to the visible Canvas3D. +#if defined(__linux__) + if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) + this->p->view3D->get_wxglcanvas()->SetFocus(); + else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) + this->p->preview->get_wxglcanvas()->SetFocus(); +#elif defined(_WIN32) if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) CallAfter([this]() { this->p->view3D->get_wxglcanvas()->SetFocus(); }); else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 258eaf0ee..07051d939 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -35,12 +35,29 @@ static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& tit return optgroup; } -static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup) +std::shared_ptr<ConfigOptionsGroup> PreferencesDialog::create_general_options_group(const wxString& title, wxNotebook* tabs) +{ + + std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>((wxPanel*)tabs->GetPage(0), title); + optgroup->title_width = 40; + optgroup->label_width = 40; + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset") + m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard"; + else if (std::unordered_set<std::string>{ "splash_screen_editor", "splash_screen_gcodeviewer", "auto_switch_preview" }.count(opt_key) > 0) + m_values[opt_key] = boost::any_cast<std::string>(value); + else + m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; + }; + return optgroup; +} + +static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup, int padding = 20) { optgroup->activate(); optgroup->update_visibility(comSimple); wxBoxSizer* sizer = static_cast<wxBoxSizer*>(static_cast<wxPanel*>(optgroup->parent())->GetSizer()); - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 20); + sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, padding); } void PreferencesDialog::build() @@ -55,45 +72,33 @@ void PreferencesDialog::build() // Add "General" tab m_optgroup_general = create_options_tab(_L("General"), tabs); - m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) { - if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset") - m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard"; - else if (std::unordered_set<std::string>{ "splash_screen_editor" ,"splash_screen_gcodeviewer" ,"auto_switch_preview" }.count(opt_key) > 0) - m_values[opt_key] = boost::any_cast<std::string>(value); - else - m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; - }; bool is_editor = wxGetApp().is_editor(); ConfigOptionDef def; Option option(def, ""); - if (is_editor) { - def.label = L("Remember output directory"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory " - "instead of the one containing the input files."); - def.set_default_value(new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true }); - option = Option(def, "remember_output_path"); - m_optgroup_general->append_single_option_line(option); - def.label = L("Auto-center parts"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will auto-center objects " - "around the print bed center."); - def.set_default_value(new ConfigOptionBool{ app_config->get("autocenter") == "1" }); - option = Option(def, "autocenter"); - m_optgroup_general->append_single_option_line(option); + if (is_editor) { - def.label = L("Background processing"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon " - "as they\'re loaded in order to save time when exporting G-code."); - def.set_default_value(new ConfigOptionBool{ app_config->get("background_processing") == "1" }); - option = Option(def, "background_processing"); - m_optgroup_general->append_single_option_line(option); + //activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Automation"), tabs); + + def.label = L("Auto-center parts"); + def.type = coBool; + def.tooltip = L("If this is enabled, Slic3r will auto-center objects " + "around the print bed center."); + def.set_default_value(new ConfigOptionBool{ app_config->get("autocenter") == "1" }); + option = Option(def, "autocenter"); + m_optgroup_general->append_single_option_line(option); + + def.label = L("Background processing"); + def.type = coBool; + def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon " + "as they\'re loaded in order to save time when exporting G-code."); + def.set_default_value(new ConfigOptionBool{ app_config->get("background_processing") == "1" }); + option = Option(def, "background_processing"); + m_optgroup_general->append_single_option_line(option); - if (is_editor) { def_combobox_auto_switch_preview.label = L("Switch to Preview when sliced"); def_combobox_auto_switch_preview.type = coStrings; def_combobox_auto_switch_preview.tooltip = L("When an object is sliced, it will switch your view from the curent view to the " @@ -104,7 +109,7 @@ void PreferencesDialog::build() def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Switch when possible")); def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Only if on plater")); def_combobox_auto_switch_preview.enum_values.push_back(_u8L("Only when GCode is ready")); - if(app_config->get("auto_switch_preview") == "0") + if (app_config->get("auto_switch_preview") == "0") def_combobox_auto_switch_preview.set_default_value(new ConfigOptionStrings{ def_combobox_auto_switch_preview.enum_values[0] }); else if (app_config->get("auto_switch_preview") == "1") def_combobox_auto_switch_preview.set_default_value(new ConfigOptionStrings{ def_combobox_auto_switch_preview.enum_values[1] }); @@ -116,23 +121,60 @@ void PreferencesDialog::build() def_combobox_auto_switch_preview.set_default_value(new ConfigOptionStrings{ def_combobox_auto_switch_preview.enum_values[2] }); option = Option(def_combobox_auto_switch_preview, "auto_switch_preview"); m_optgroup_general->append_single_option_line(option); - } - // Please keep in sync with ConfigWizard - def.label = L("Check for application updates"); - def.type = coBool; - def.tooltip = L("If enabled, Slic3r will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."); - def.set_default_value(new ConfigOptionBool(app_config->get("version_check") == "1")); - option = Option(def, "version_check"); - m_optgroup_general->append_single_option_line(option); - // Please keep in sync with ConfigWizard - def.label = L("Export sources full pathnames to 3mf and amf"); - def.type = coBool; - def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked."); - def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1")); - option = Option(def, "export_sources_full_pathnames"); - m_optgroup_general->append_single_option_line(option); + activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Presets and updates"), tabs); + + // Please keep in sync with ConfigWizard + def.label = L("Check for application updates"); + def.type = coBool; + def.tooltip = L("If enabled, Slic3r will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."); + def.set_default_value(new ConfigOptionBool(app_config->get("version_check") == "1")); + option = Option(def, "version_check"); + m_optgroup_general->append_single_option_line(option); + + // Please keep in sync with ConfigWizard + def.label = L("Update built-in Presets automatically"); + def.type = coBool; + def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."); + def.set_default_value(new ConfigOptionBool(app_config->get("preset_update") == "1")); + option = Option(def, "preset_update"); + m_optgroup_general->append_single_option_line(option); + + def.label = L("Suppress \" - default - \" presets"); + def.type = coBool; + def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer " + "selections once there are any other valid presets available."); + def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" }); + option = Option(def, "no_defaults"); + m_optgroup_general->append_single_option_line(option); + + def.label = L("Show incompatible print and filament presets"); + def.type = coBool; + def.tooltip = L("When checked, the print and filament presets are shown in the preset editor " + "even if they are marked as incompatible with the active printer"); + def.set_default_value(new ConfigOptionBool{ app_config->get("show_incompatible_presets") == "1" }); + option = Option(def, "show_incompatible_presets"); + m_optgroup_general->append_single_option_line(option); + + def.label = L("Main GUI always in expert mode"); + def.type = coBool; + def.tooltip = L("If enabled, the gui will be in expert mode even if the simple or advanced mode is selected (but not the setting tabs)."); + def.set_default_value(new ConfigOptionBool{ app_config->get("objects_always_expert") == "1" }); + option = Option(def, "objects_always_expert"); + m_optgroup_general->append_single_option_line(option); + + activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Files"), tabs); + + // Please keep in sync with ConfigWizard + def.label = L("Export sources full pathnames to 3mf and amf"); + def.type = coBool; + def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked."); + def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1")); + option = Option(def, "export_sources_full_pathnames"); + m_optgroup_general->append_single_option_line(option); #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 @@ -153,36 +195,16 @@ void PreferencesDialog::build() #endif // _WIN32 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN - // Please keep in sync with ConfigWizard - def.label = L("Update built-in Presets automatically"); - def.type = coBool; - def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."); - def.set_default_value(new ConfigOptionBool(app_config->get("preset_update") == "1")); - option = Option(def, "preset_update"); - m_optgroup_general->append_single_option_line(option); + def.label = L("Remember output directory"); + def.type = coBool; + def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory " + "instead of the one containing the input files."); + def.set_default_value(new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true }); + option = Option(def, "remember_output_path"); + m_optgroup_general->append_single_option_line(option); - def.label = L("Suppress \" - default - \" presets"); - def.type = coBool; - def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer " - "selections once there are any other valid presets available."); - def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" }); - option = Option(def, "no_defaults"); - m_optgroup_general->append_single_option_line(option); - - def.label = L("Show incompatible print and filament presets"); - def.type = coBool; - def.tooltip = L("When checked, the print and filament presets are shown in the preset editor " - "even if they are marked as incompatible with the active printer"); - def.set_default_value(new ConfigOptionBool{ app_config->get("show_incompatible_presets") == "1" }); - option = Option(def, "show_incompatible_presets"); - m_optgroup_general->append_single_option_line(option); - - def.label = L("Main GUI always in expert mode"); - def.type = coBool; - def.tooltip = L("If enabled, the gui will be in expert mode even if the simple or advanced mode is selected (but not the setting tabs)."); - def.set_default_value(new ConfigOptionBool{ app_config->get("objects_always_expert") == "1" }); - option = Option(def, "objects_always_expert"); - m_optgroup_general->append_single_option_line(option); + activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Dialogs"), tabs); def.label = L("Show drop project dialog"); def.type = coBool; @@ -191,6 +213,13 @@ void PreferencesDialog::build() option = Option(def, "show_drop_project_dialog"); m_optgroup_general->append_single_option_line(option); + def.label = L("Show overwrite dialog."); + def.type = coBool; + def.tooltip = L("If this is enabled, Slic3r will prompt for when overwriting files from save dialogs."); + def.set_default_value(new ConfigOptionBool{ app_config->has("show_overwrite_dialog") ? app_config->get("show_overwrite_dialog") == "1" : true }); + option = Option(def, "show_overwrite_dialog"); + m_optgroup_general->append_single_option_line(option); + #if __APPLE__ def.label = (boost::format(_u8L("Allow just a single %1% instance")) % SLIC3R_APP_NAME).str(); @@ -201,9 +230,9 @@ void PreferencesDialog::build() def.type = coBool; def.tooltip = L("If this is enabled, when starting Slic3r and another instance of the same Slic3r is already running, that instance will be reactivated instead."); #endif - def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false }); - option = Option(def, "single_instance"); - m_optgroup_general->append_single_option_line(option); + def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false }); + option = Option(def, "single_instance"); + m_optgroup_general->append_single_option_line(option); def.label = L("Ask for unsaved changes when closing application"); def.type = coBool; @@ -256,6 +285,11 @@ void PreferencesDialog::build() m_optgroup_general->append_single_option_line(option); #endif + if (is_editor) { + activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Splash screen"), tabs); + } + // Show/Hide splash screen def.label = L("Show splash screen"); def.type = coBool; @@ -274,7 +308,7 @@ void PreferencesDialog::build() // splashscreen image { ConfigOptionDef def_combobox; - def_combobox.label = L("Splashscreen image"); + def_combobox.label = L("Splash screen image"); def_combobox.type = coStrings; def_combobox.tooltip = L("Choose the image to use as splashscreen"); def_combobox.gui_type = "f_enum_open"; @@ -294,6 +328,10 @@ void PreferencesDialog::build() #if ENABLE_CTRL_M_ON_WINDOWS #if defined(_WIN32) || defined(__APPLE__) + if (is_editor) { + activate_options_tab(m_optgroup_general, 3); + m_optgroup_general = create_general_options_group(_L("Others"), tabs); + } def.label = L("Enable support for legacy 3DConnexion devices"); def.type = coBool; def.tooltip = L("If enabled, the legacy 3DConnexion devices settings dialog is available by pressing CTRL+M"); @@ -303,7 +341,7 @@ void PreferencesDialog::build() #endif // _WIN32 || __APPLE__ #endif // ENABLE_CTRL_M_ON_WINDOWS - activate_options_tab(m_optgroup_general); + activate_options_tab(m_optgroup_general, m_optgroup_general->parent()->GetSizer()->GetItemCount() > 1 ? 3 : 20); // Add "Paths" tab diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 1fc0961e1..16beaec4e 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -47,6 +47,7 @@ protected: void layout(); void create_icon_size_slider(); void create_settings_mode_widget(); + std::shared_ptr<ConfigOptionsGroup> create_general_options_group(const wxString& title, wxNotebook* tabs); }; } // GUI diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 459df570a..0955bbcd9 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -22,6 +22,7 @@ std::string PresetHints::cooling_description(const Preset &preset) int max_fan_speed = preset.config.opt_int("max_fan_speed", 0); int top_fan_speed = preset.config.opt_int("top_fan_speed", 0); int bridge_fan_speed = preset.config.opt_int("bridge_fan_speed", 0); + int bridge_internal_fan_speed = preset.config.opt_int("bridge_internal_fan_speed", 0); int ext_peri_fan_speed = preset.config.opt_int("external_perimeter_fan_speed", 0); int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0); int slowdown_below_layer_time = preset.config.opt_int("slowdown_below_layer_time", 0); @@ -32,9 +33,11 @@ std::string PresetHints::cooling_description(const Preset &preset) //for the time being, -1 shoudl eb for disabel, but it's 0 from legacy. if (top_fan_speed == 0) top_fan_speed = -1; if (bridge_fan_speed == 0) bridge_fan_speed = -1; + if (bridge_internal_fan_speed == 0) bridge_internal_fan_speed = -1; if (ext_peri_fan_speed == 0) ext_peri_fan_speed = -1; if (top_fan_speed == 1) top_fan_speed = 0; if (bridge_fan_speed == 1) bridge_fan_speed = 0; + if (bridge_internal_fan_speed == 1) bridge_internal_fan_speed = 0; if (ext_peri_fan_speed == 1) ext_peri_fan_speed = 0; //if (preset.config.opt_bool("cooling", 0)) { @@ -52,6 +55,9 @@ std::string PresetHints::cooling_description(const Preset &preset) if (bridge_fan_speed >= 0 && bridge_fan_speed > min_fan_speed) { out += ", " + (boost::format(_utf8(L("at %1%%% over bridges"))) % bridge_fan_speed).str(); } + if (bridge_internal_fan_speed >= 0 && bridge_internal_fan_speed > min_fan_speed) { + out += ", " + (boost::format(_utf8(L("at %1%%% over infill bridges"))) % bridge_internal_fan_speed).str(); + } if (disable_fan_first_layers > 1) out += ", " + (boost::format(_utf8(L("except for the first %1% layers where the fan is disabled"))) % disable_fan_first_layers).str(); else if (disable_fan_first_layers == 1) @@ -79,9 +85,14 @@ std::string PresetHints::cooling_description(const Preset &preset) } if (bridge_fan_speed > max_fan_speed) { out += ", " + (boost::format(_utf8(L("at %1%%% over bridges"))) % bridge_fan_speed).str(); - }else if (bridge_fan_speed > min_fan_speed) { + } else if (bridge_fan_speed > min_fan_speed) { out += ", " + (boost::format(_utf8(L("at %1%%% over bridges"))) % bridge_fan_speed).str() + " " + L("if it's above the current computed fan speed value"); } + if (bridge_internal_fan_speed > max_fan_speed) { + out += ", " + (boost::format(_utf8(L("at %1%%% over infill bridges"))) % bridge_internal_fan_speed).str(); + } else if (bridge_internal_fan_speed > min_fan_speed) { + out += ", " + (boost::format(_utf8(L("at %1%%% over infill bridges"))) % bridge_internal_fan_speed).str() + " " + L("if it's above the current computed fan speed value"); + } if (disable_fan_first_layers > 1) out += " ; " + ((boost::format(_utf8(L("except for the first %1% layers where the fan is disabled"))) % disable_fan_first_layers).str()); else if (disable_fan_first_layers == 1) @@ -118,6 +129,7 @@ std::string PresetHints::cooling_description(const Preset &preset) //tooltip for Depractaed values bridge_fan_speed = preset.config.opt_int("bridge_fan_speed", 0); + bridge_internal_fan_speed = preset.config.opt_int("bridge_internal_fan_speed", 0); ext_peri_fan_speed = preset.config.opt_int("external_perimeter_fan_speed", 0); top_fan_speed = preset.config.opt_int("top_fan_speed", 0); if (top_fan_speed == 0) @@ -126,6 +138,8 @@ std::string PresetHints::cooling_description(const Preset &preset) out += "\n\n!!! 0 for the External perimeters fan speed is Deprecated, please set it to -1 to disable it !!!"; if (bridge_fan_speed == 0) out += "\n\n!!! 0 for the Bridge fan speed is Deprecated, please set it to -1 to disable it !!!"; + if (bridge_internal_fan_speed == 0) + out += "\n\n!!! 0 for the Infill bridge fan speed is Deprecated, please set it to -1 to disable it !!!"; return out; } @@ -183,7 +197,8 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle const auto &support_material_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("support_material_extrusion_width"); const auto &top_infill_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("top_infill_extrusion_width"); const auto &first_layer_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_speed"); - const auto &first_layer_infill_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_infill_speed"); + const auto& first_layer_infill_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_infill_speed"); + const auto& first_layer_min_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_infill_speed"); // Index of an extruder assigned to a feature. If set to 0, an active extruder will be used for a multi-material print. // If different from idx_extruder, it will not be taken into account for this hint. @@ -214,16 +229,24 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle const float bfr = bridging ? bridge_flow_ratio : 0.f; double max_flow = 0.; std::string max_flow_extrusion_type; - auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) { - if (first_layer && first_layer_speed.value > 0) + auto limit_by_first_layer_speed = [&first_layer_min_speed , &first_layer_speed, first_layer](double speed_normal, double speed_max) { + if (first_layer) { + const double base_speed = speed_normal; // Apply the first layer limit. - speed_normal = first_layer_speed.get_abs_value(speed_normal); + if (first_layer_speed.value > 0) + speed_normal = std::min(first_layer_speed.get_abs_value(base_speed), speed_normal); + speed_normal = std::max(first_layer_min_speed.get_abs_value(base_speed), speed_normal); + } return (speed_normal > 0.) ? speed_normal : speed_max; }; - auto limit_infill_by_first_layer_speed = [&first_layer_infill_speed, first_layer](double speed_normal, double speed_max) { - if (first_layer && first_layer_infill_speed.value > 0) + auto limit_infill_by_first_layer_speed = [&first_layer_min_speed, &first_layer_infill_speed, first_layer](double speed_normal, double speed_max) { + if (first_layer) { + const double base_speed = speed_normal; // Apply the first layer limit. - speed_normal = first_layer_infill_speed.get_abs_value(speed_normal); + if(first_layer_infill_speed.value > 0) + speed_normal = std::min(first_layer_infill_speed.get_abs_value(base_speed), speed_normal); + speed_normal = std::max(first_layer_min_speed.get_abs_value(base_speed), speed_normal); + } return (speed_normal > 0.) ? speed_normal : speed_max; }; if (perimeter_extruder_active) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a20d10839..5ea82a7dd 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2263,6 +2263,7 @@ void TabFilament::add_filament_overrides_page() "filament_retract_before_travel", "filament_retract_layer_change", "filament_wipe", + "filament_wipe_speed", "filament_wipe_extra_perimeter", "filament_retract_before_wipe" }) @@ -2290,6 +2291,7 @@ void TabFilament::update_filament_overrides_page() "filament_retract_before_travel", "filament_retract_layer_change", "filament_wipe", + "filament_wipe_speed", "filament_wipe_extra_perimeter", "filament_retract_before_wipe" }; @@ -2912,17 +2914,20 @@ void TabPrinter::toggle_options() // some options only apply when not using firmware retraction vec.resize(0); - vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe" }; + vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe", "wipe_speed" }; for (auto el : vec) { field = get_field(el, i); if (field) field->toggle(retraction && !use_firmware_retraction); } - bool wipe = m_config->opt_bool("wipe", i); - field = get_field("retract_before_wipe", i); - if (field) - field->toggle(wipe); + bool wipe = m_config->opt_bool("wipe", i) && have_retract_length; + vec = { "retract_before_wipe", "wipe_speed" }; + for (auto el : vec) { + field = get_field(el, i); + if (field) + field->toggle(wipe); + } if (use_firmware_retraction && wipe) { wxMessageDialog dialog(parent(), |