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(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(raw_opt)->value; - if (raw_opt->type() == coInt) - return static_cast(raw_opt)->value; - if (raw_opt->type() == coBool) - return static_cast(raw_opt)->value?1:0; - const ConfigOptionDef* opt_def = nullptr; - const ConfigOptionPercent* cast_opt = nullptr; - if (raw_opt->type() == coFloatOrPercent) { - if(!static_cast(raw_opt)->percent) - return static_cast(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(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(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()); - } - return cast_opt->get_abs_value(static_cast(this->option(opt_def->ratio_over))->values[0]); + + if (!raw_opt->is_vector()) { + if (raw_opt->type() == coFloat) + return static_cast(raw_opt)->value; + if (raw_opt->type() == coInt) + return static_cast(raw_opt)->value; + if (raw_opt->type() == coBool) + return static_cast(raw_opt)->value ? 1 : 0; + const ConfigOptionDef* opt_def = nullptr; + const ConfigOptionPercent* cast_opt = nullptr; + if (raw_opt->type() == coFloatOrPercent) { + if (!static_cast(raw_opt)->percent) + return static_cast(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(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(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(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(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(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()); + } } - // 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 void serialize(Archive& ar) { ar(this->phony); } + template 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*>(rhs)); this->value = static_cast*>(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 void serialize(Archive & ar) { ar(this->phony); ar(this->value); } + template 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*>(rhs)); this->values = static_cast*>(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 void serialize(Archive & ar) { ar(this->phony); ar(this->values); } + template void serialize(Archive & ar) { ar(this->flags); ar(this->values); } }; class ConfigOptionFloat : public ConfigOptionSingle @@ -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: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum 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 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 default_value; - void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr(ptr); } + void set_default_value(const ConfigOption* ptr) { + this->default_value = Slic3r::clonable_ptr(ptr); + } + void set_default_value(ConfigOptionVectorBase* ptr) { + ptr->set_is_extruder_size(this->is_vector_extruder); + this->default_value = Slic3r::clonable_ptr(ptr); + } template const T* get_default_value() const { return static_cast(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 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 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 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/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/GCode.cpp b/src/libslic3r/GCode.cpp index fc26fde9d..2ee128c92 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -225,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); } } @@ -741,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 { @@ -1188,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); @@ -1293,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 extruder_set = print.extruders(); + this->set_extruders(std::vector(extruder_set.begin(), extruder_set.end())); if(has_milling) m_writer.set_mills(std::vector() = { 0 }); } else { @@ -3760,31 +3764,31 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double spee //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_abs_value("perimeter_speed"); + speed = m_config.get_computed_value("perimeter_speed"); } else if (path.role() == erExternalPerimeter) { - speed = m_config.get_abs_value("external_perimeter_speed"); + speed = m_config.get_computed_value("external_perimeter_speed"); } else if (path.role() == erBridgeInfill) { - speed = m_config.get_abs_value("bridge_speed"); + speed = m_config.get_computed_value("bridge_speed"); } else if (path.role() == erInternalBridgeInfill) { - speed = m_config.get_abs_value("bridge_speed_internal"); + speed = m_config.get_computed_value("bridge_speed_internal"); } else if (path.role() == erOverhangPerimeter) { - speed = m_config.get_abs_value("overhangs_speed"); + speed = m_config.get_computed_value("overhangs_speed"); } else if (path.role() == erInternalInfill) { - speed = m_config.get_abs_value("infill_speed"); + speed = m_config.get_computed_value("infill_speed"); } else if (path.role() == erSolidInfill) { - speed = m_config.get_abs_value("solid_infill_speed"); + speed = m_config.get_computed_value("solid_infill_speed"); } else if (path.role() == erTopSolidInfill) { - speed = m_config.get_abs_value("top_solid_infill_speed"); + speed = m_config.get_computed_value("top_solid_infill_speed"); } else if (path.role() == erThinWall) { - speed = m_config.get_abs_value("thin_walls_speed"); + speed = m_config.get_computed_value("thin_walls_speed"); } else if (path.role() == erGapFill) { - speed = m_config.get_abs_value("gap_fill_speed"); + speed = m_config.get_computed_value("gap_fill_speed"); } else if (path.role() == erIroning) { - speed = m_config.get_abs_value("ironing_speed"); + speed = m_config.get_computed_value("ironing_speed"); } else if (path.role() == erNone) { - speed = m_config.get_abs_value("travel_speed"); + speed = m_config.get_computed_value("travel_speed"); } else if (path.role() == erMilling) { - speed = m_config.get_abs_value("milling_speed"); + speed = m_config.get_computed_value("milling_speed"); } else { throw Slic3r::InvalidArgument("Invalid speed"); } 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 printing_extruders = print_object.object_extruders(); + std::set 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/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 874ff4be1..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); } @@ -773,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); } diff --git a/src/libslic3r/Milling/MillingPostProcess.cpp b/src/libslic3r/Milling/MillingPostProcess.cpp index 9a26e1979..50147de21 100644 --- a/src/libslic3r/Milling/MillingPostProcess.cpp +++ b/src/libslic3r/Milling/MillingPostProcess.cpp @@ -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/Print.cpp b/src/libslic3r/Print.cpp index 0dad08f01..c0716d7f5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -355,10 +355,9 @@ bool Print::is_step_done(PrintObjectStep step) const } // returns 0-based indices of used extruders -std::vector Print::object_extruders(const PrintObjectPtrs &objects) const +std::set Print::object_extruders(const PrintObjectPtrs &objects) const { - std::vector extruders; - extruders.reserve(m_regions.size() * 3); + std::set extruders; std::vector region_used(m_regions.size(), false); for (const PrintObject *object : objects) for (const std::vector> &volumes_per_region : object->region_volumes) @@ -367,14 +366,13 @@ std::vector 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 Print::support_material_extruders() const +std::set Print::support_material_extruders() const { - std::vector extruders; + std::set extruders; bool support_uses_current_extruder = false; auto num_extruders = (uint16_t)m_config.nozzle_diameter.size(); @@ -385,7 +383,7 @@ std::vector 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); @@ -393,7 +391,7 @@ std::vector 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); } } } @@ -403,16 +401,14 @@ std::vector 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 Print::extruders() const +std::set Print::extruders() const { - std::vector extruders = this->object_extruders(m_objects); + std::set extruders = this->object_extruders(m_objects); append(extruders, this->support_material_extruders()); - sort_remove_duplicates(extruders); return extruders; } @@ -1372,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 { + //get object first layer height + double object_first_layer_height = object.config().first_layer_height.value; + if (object.config().first_layer_height.percent) { + std::set 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); + } + 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 Print::validate() const { @@ -1408,8 +1439,8 @@ std::pair 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); @@ -1509,7 +1540,7 @@ std::pair Print::validate() const } { - std::vector extruders = this->extruders(); + std::set extruders = this->extruders(); // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); @@ -1529,6 +1560,7 @@ std::pair 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) { @@ -1552,50 +1584,49 @@ std::pair 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 object_extruders; + std::set 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>{ {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>{ {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"}, @@ -1677,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. @@ -1693,18 +1717,40 @@ 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 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; + 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() ); } @@ -1836,10 +1882,9 @@ void Print::process() if (config().complete_objects && !config().complete_objects_one_brim) { for (PrintObject *obj : obj_group) { //get flow - std::vector set_extruders = this->object_extruders({ obj }); + std::set 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) @@ -1861,10 +1906,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 set_extruders = this->object_extruders(m_objects); + std::set 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 @@ -1977,15 +2021,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 extruders; std::vector extruders_e_per_mm; { - std::vector set_extruders = this->object_extruders(objects); + std::set 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) { @@ -2040,7 +2081,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 @@ -2167,7 +2208,7 @@ void Print::_extrude_brim_from_tree(std::vector>& loops, c //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* extrude_ptr; std::function extrude = [&mm3_per_mm, &width, &height, &extrude_ptr, &nextIdx](BrimLoop& to_cut, ExtrusionEntityCollection* parent) { @@ -2468,7 +2509,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)); @@ -2524,7 +2565,7 @@ void Print::_make_brim_interior(const Flow &flow, const PrintObjectPtrs &objects 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()) { - spacing = scaled(object->config().support_material_interface_spacing.value) + support_material_flow(object, float(this->skirt_first_layer_height())).scaled_width() * 1.5; + 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]); @@ -2815,7 +2856,7 @@ void Print::_make_wipe_tower() wipe_tower.set_extruder(i); m_wipe_tower_data.priming = Slic3r::make_unique>( - 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) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 08da296c5..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 &object_extruders) const; - static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); + void collect_object_printing_extruders(std::set &object_extruders) const; + static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::set &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 object_extruders() const; + std::set object_extruders() const; + double get_first_layer_height() const; // Called by make_perimeters() void slice(); @@ -432,13 +433,14 @@ public: // Returns an empty string if valid, otherwise returns an error message. std::pair 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 object_extruders(const PrintObjectPtrs &objects) const; - std::vector support_material_extruders() const; - std::vector extruders() const; + std::set object_extruders(const PrintObjectPtrs &objects) const; + std::set support_material_extruders() const; + std::set 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/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index cd7f6f4ab..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); @@ -338,6 +339,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{ 100 }); def = this->add("bridge_internal_fan_speed", coInts); @@ -351,6 +353,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("top_fan_speed", coInts); @@ -364,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); @@ -514,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); @@ -620,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); @@ -677,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); @@ -726,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); @@ -910,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); @@ -1074,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"); @@ -1093,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"); @@ -1127,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); @@ -1138,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); @@ -1148,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); @@ -1156,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 }); @@ -1175,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); @@ -1228,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); @@ -1240,6 +1271,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 { 60 }); def = this->add("filament_colour", coStrings); @@ -1249,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); @@ -1259,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); @@ -1270,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); @@ -1281,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); @@ -1298,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); @@ -1306,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 @@ -1313,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); @@ -1334,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); @@ -1348,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); @@ -1356,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); @@ -1364,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); @@ -1372,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); @@ -1380,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); @@ -1388,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 @@ -1397,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); @@ -1406,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); @@ -1414,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); @@ -1424,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); @@ -1433,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); @@ -1441,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); @@ -1452,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); @@ -1460,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); @@ -1468,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" }); @@ -1483,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); @@ -1492,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); @@ -1503,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); @@ -1514,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); @@ -1552,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); @@ -1559,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); @@ -1568,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); @@ -1579,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); @@ -1788,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); @@ -1826,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; @@ -1889,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); @@ -1900,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); @@ -2624,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); @@ -2637,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); @@ -2658,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); @@ -2705,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); @@ -2724,6 +2798,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.07 }); def = this->add("min_length", coFloat); @@ -2757,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); @@ -2787,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); @@ -3141,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); @@ -3150,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); @@ -3157,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); @@ -3167,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); @@ -3185,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); @@ -3194,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); @@ -3204,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. }); @@ -3217,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); @@ -3225,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); @@ -3238,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" }); @@ -3247,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); @@ -3256,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); @@ -3265,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); @@ -3275,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); @@ -3408,6 +3499,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{ 5 }); def = this->add("small_perimeter_speed", coFloatOrPercent); @@ -3657,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); @@ -3839,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; @@ -4005,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); @@ -4129,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); @@ -4276,6 +4371,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("This flag will move the nozzle while retracting to minimize the possible blob " "on leaky extruders."); def->mode = comAdvanced; + def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ false }); def = this->add("wipe_speed", coFloats); @@ -4332,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); @@ -4422,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); @@ -4602,8 +4700,8 @@ void PrintConfigDef::init_extruder_option_keys() "retract_restart_extra", "retract_speed", "wipe", + "wipe_extra_perimeter", "wipe_speed", - "wipe_extra_perimeter" }; assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end())); } @@ -4648,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); @@ -4659,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); @@ -4667,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); @@ -4685,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); @@ -4695,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); @@ -5717,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("gap_fill")->value) { value = "0"; @@ -5740,7 +5843,7 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, double val = all_conf.option("support_material_contact_distance_top")->get_abs_value(all_conf.option("nozzle_diameter")->values.front()); if (SupportZDistanceType::zdFilament == dist_type) { // not exact but good enough effort val += all_conf.option("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(val); } @@ -5820,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(this)); @@ -5852,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(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) @@ -5871,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( @@ -5927,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()); @@ -6301,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 d91e07e16..ea6f18673 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1950,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); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d32d44fc4..3296f3eb6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2277,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 object_extruders; + std::set object_extruders; for (const ModelVolume* model_volume : model_object.volumes) if (model_volume->is_model_part()) { PrintRegion::collect_object_printing_extruders( @@ -2295,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(); @@ -2303,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 PrintObject::object_extruders() const + std::set PrintObject::object_extruders() const { - std::vector extruders; - extruders.reserve(this->region_volumes.size() * 3); + std::set 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& layer_height_profile) { bool updated = false; @@ -3880,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 &object_extruders) +void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::set &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 &object_extruders) const +void PrintRegion::collect_object_printing_extruders(std::set &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 &object_extruders) + const std::set &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 &object_extruders); + const std::set &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 object_extruders = m_object->print()->object_extruders(m_object->print()->objects()); if (object_extruders.size() == 1 && *object_extruders.begin() == std::max(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 98c3778f3..1c5057687 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,15 @@ inline void append(std::vector& dest, const std::vector& src) dest.insert(dest.end(), src.begin(), src.end()); } +template +inline void append(std::set& dest, const std::set& src) +{ + if (dest.empty()) + dest = src; + else + dest.insert(src.begin(), src.end()); +} + template inline void append(std::vector& dest, std::vector&& src) { 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/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f05f34f68..2157709e9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include "DoubleSlider.hpp" #include