From 715d58da888e591116a82f70ba6ff30020275ced Mon Sep 17 00:00:00 2001 From: remi durand Date: Sun, 25 Apr 2021 01:55:30 +0200 Subject: [PATCH] Add some spacing settings at the side of width - now a setting can be "phony" - a phony setting isn't saved in a config file - a phony setting should be computed from other settings - change/add colors, icons, and callbacks (in PrintConfig.cpp) to make phony settings works --- resources/ui_layout/print.ui | 43 ++- src/libslic3r/Config.cpp | 20 +- src/libslic3r/Config.hpp | 18 +- src/libslic3r/Fill/FillAdaptive.cpp | 4 +- src/libslic3r/GCode.cpp | 10 +- src/libslic3r/GCodeWriter.cpp | 6 +- src/libslic3r/PerimeterGenerator.cpp | 8 +- src/libslic3r/Preset.cpp | 24 +- src/libslic3r/Print.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 439 ++++++++++++++++++++++++-- src/libslic3r/PrintConfig.hpp | 7 + src/libslic3r/PrintObject.cpp | 26 +- src/libslic3r/SupportMaterial.cpp | 6 +- src/slic3r/GUI/ButtonsDescription.cpp | 28 +- src/slic3r/GUI/GUI_App.cpp | 31 +- src/slic3r/GUI/GUI_App.hpp | 4 + src/slic3r/GUI/Tab.cpp | 67 +++- src/slic3r/GUI/Tab.hpp | 9 +- 18 files changed, 673 insertions(+), 79 deletions(-) diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index 3ba018d23..784d2d6d0 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -258,15 +258,40 @@ group:Autospeed (advanced) page:Width & Flow:width group:Extrusion width - setting:extrusion_width - setting:first_layer_extrusion_width - setting:perimeter_extrusion_width - setting:external_perimeter_extrusion_width - setting:infill_extrusion_width - setting:solid_infill_extrusion_width - setting:top_infill_extrusion_width - setting:support_material_extrusion_width - setting:skirt_extrusion_width + line:default + setting:sidetext_width$10:label$width:extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:extrusion_spacing + end_line + line:first layer + setting:sidetext_width$10:label$width:first_layer_extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:first_layer_extrusion_spacing + end_line + line:perimeter + setting:sidetext_width$10:label$width:perimeter_extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:perimeter_extrusion_spacing + end_line + line:external perimeter + setting:sidetext_width$10:label$width:external_perimeter_extrusion_width + setting:sidetext_width$10:label_width$15:label$width&spacing combo:external_perimeter_extrusion_spacing + end_line + line:infill + setting:sidetext_width$10:label$width:infill_extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:infill_extrusion_spacing + end_line + line:solid infill + setting:sidetext_width$10:label$width:solid_infill_extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:solid_infill_extrusion_spacing + end_line + line:top infill + setting:sidetext_width$10:label$width:top_infill_extrusion_width + setting:sidetext_width$10:label_width$15:label$spacing:top_infill_extrusion_spacing + end_line + line:support material + setting:sidetext_width$10:label$width:support_material_extrusion_width + end_line + line:skirt + setting:sidetext_width$10:label$width:skirt_extrusion_width + end_line recommended_extrusion_width_description group:Overlap line:Perimeter overlap diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index f6c92404a..6d5baf69a 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -473,7 +473,9 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const for (const t_config_option_key &opt_key : this->keys()) { const ConfigOption *this_opt = this->option(opt_key); const ConfigOption *other_opt = other.option(opt_key); - if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt) + //dirty if both exist, they aren't both phony and value is different + if (this_opt != nullptr && other_opt != nullptr && !(this_opt->phony && other_opt->phony) + && ((*this_opt != *other_opt) || (this_opt->phony != other_opt->phony))) diff.emplace_back(opt_key); } return diff; @@ -495,6 +497,8 @@ std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const { const ConfigOption* opt = this->option(opt_key); assert(opt != nullptr); + if (opt->phony) + return ""; return opt->serialize(); } @@ -584,7 +588,19 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con ConfigOption *opt = this->option(opt_key, true); if (opt == nullptr) throw new UnknownOptionException(opt_key); - bool ok= opt->deserialize(value, append); + + bool ok = true; + if (!optdef->can_phony || value != "") + ok = opt->deserialize(value, append); + //set phony status + if (optdef->can_phony) + if(value == "") + opt->phony = true; + else + opt->phony = false; + else + opt->phony = false; + return ok; } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 52c057c76..3481bcf48 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -214,6 +214,12 @@ inline OutputFormat operator&=(OutputFormat& a, OutputFormat b) { // 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. + bool phony; + + ConfigOption() : phony(false) {} + ConfigOption(bool phony) : phony(phony) {} + virtual ~ConfigOption() {} virtual ConfigOptionType type() const = 0; @@ -258,6 +264,7 @@ class ConfigOptionSingle : public ConfigOption { public: T value; explicit ConfigOptionSingle(T value) : value(value) {} + explicit ConfigOptionSingle(T value, bool phony) : ConfigOption(phony), value(value) {} operator T() const { return this->value; } void set(const ConfigOption *rhs) override @@ -266,6 +273,7 @@ public: throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; + this->phony = rhs->phony; } bool operator==(const ConfigOption &rhs) const override @@ -340,6 +348,7 @@ public: throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->values = static_cast*>(rhs)->values; + this->phony = rhs->phony; } // Set from a vector of ConfigOptions. @@ -504,6 +513,7 @@ class ConfigOptionFloat : public ConfigOptionSingle public: ConfigOptionFloat() : ConfigOptionSingle(0) {} explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} + explicit ConfigOptionFloat(double _value, bool _phony) : ConfigOptionSingle(_value, _phony) {} static ConfigOptionType static_type() { return coFloat; } ConfigOptionType type() const override { return static_type(); } @@ -850,6 +860,7 @@ class ConfigOptionPercent : public ConfigOptionFloat public: ConfigOptionPercent() : ConfigOptionFloat(0) {} explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {} + explicit ConfigOptionPercent(double _value, bool _phony) : ConfigOptionFloat(_value, _phony) {} static ConfigOptionType static_type() { return coPercent; } ConfigOptionType type() const override { return static_type(); } @@ -943,6 +954,7 @@ public: bool percent; ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {} explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {} + explicit ConfigOptionFloatOrPercent(double _value, bool _percent, bool _phony) : ConfigOptionPercent(_value, _phony), percent(_percent) {} static ConfigOptionType static_type() { return coFloatOrPercent; } ConfigOptionType type() const override { return static_type(); } @@ -1427,6 +1439,7 @@ public: throw Slic3r::RuntimeError("ConfigOptionEnum: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = (T)rhs->getInt(); + this->phony = rhs->phony; } std::string serialize() const override @@ -1511,6 +1524,7 @@ public: throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = rhs->getInt(); + this->phony = rhs->phony; } std::string serialize() const override @@ -1658,7 +1672,9 @@ public: // For text input: If true, the GUI formats text as code (fixed-width) bool is_code = false; // Not editable. Currently only used for the display of the number of threads. - bool readonly = false; + bool readonly = false; + // Can be phony. if not present at laoding, mark it as phony. Also adapt the gui to look for phony status. + bool can_phony = false; // Height of a multiline GUI text box. int height = -1; // Optional width of an input field. diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 152ad6cbd..14515f511 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -299,8 +299,8 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob for (const PrintRegion *region : print_object.print()->regions()) { const PrintRegionConfig &config = region->config(); bool nonempty = config.fill_density > 0; - bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic; - bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic; + bool has_adaptive_infill = nonempty && config.fill_pattern.value == ipAdaptiveCubic; + bool has_support_infill = nonempty && config.fill_pattern.value == ipSupportCubic; double infill_extrusion_width = config.infill_extrusion_width.get_abs_value(max_nozzle_diameter); region_fill_data.push_back(RegionFillData({ has_adaptive_infill ? Tristate::Maybe : Tristate::No, diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ee992cdf1..c09be0790 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -743,7 +743,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re namespace DoExport { static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled) { - silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlin) && config.silent_mode; + silent_time_estimator_enabled = (config.gcode_flavor.value == gcfMarlin) && config.silent_mode; processor.reset(); processor.apply_config(config); processor.enable_stealth_time_estimator(silent_time_estimator_enabled); @@ -1490,7 +1490,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu bbox_prime.offset(0.5f); bool overlap = bbox_prime.overlap(bbox_print); - if (print.config().gcode_flavor == gcfMarlin) { + if (print.config().gcode_flavor.value == gcfMarlin) { _write(file, this->retract()); _write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. if (overlap) { @@ -1727,14 +1727,14 @@ void GCode::print_machine_envelope(FILE *file, Print &print) int(print.config().machine_max_acceleration_travel.values.front() + 0.5), int(print.config().machine_max_acceleration_travel.values.front() + 0.5)); if (std::set{gcfMarlin, gcfLerdge, gcfRepetier, gcfSmoothie, gcfSprinter}.count(print.config().gcode_flavor.value) > 0) - fprintf(file, (print.config().gcode_flavor == gcfMarlin || print.config().gcode_flavor == gcfSmoothie) + fprintf(file, (print.config().gcode_flavor.value == gcfMarlin || print.config().gcode_flavor.value == gcfSmoothie) ? "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n" : "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/min\n", int(print.config().machine_max_feedrate_x.values.front() + 0.5), int(print.config().machine_max_feedrate_y.values.front() + 0.5), int(print.config().machine_max_feedrate_z.values.front() + 0.5), int(print.config().machine_max_feedrate_e.values.front() + 0.5)); - if (print.config().gcode_flavor == gcfRepRap) { + if (print.config().gcode_flavor.value == gcfRepRap) { fprintf(file, "M203 X%d Y%d Z%d E%d I%d; sets maximum feedrates, mm/min\n", int(print.config().machine_max_feedrate_x.values.front() + 0.5), int(print.config().machine_max_feedrate_y.values.front() + 0.5), @@ -1806,7 +1806,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c { // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; - bool include_g10 = print.config().gcode_flavor == gcfRepRap; + bool include_g10 = print.config().gcode_flavor.value == gcfRepRap; if (custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) { // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 5388e560f..001bee6de 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -6,9 +6,9 @@ #include #include -#define FLAVOR_IS(val) this->config.gcode_flavor == val -#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val -#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment; +#define FLAVOR_IS(val) this->config.gcode_flavor.value == val +#define FLAVOR_IS_NOT(val) this->config.gcode_flavor.value != val +#define COMMENT(comment) if (this->config.gcode_comments.value && !comment.empty()) gcode << " ; " << comment; #define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val) #define XYZF_NUM(val) PRECISION(val, this->config.gcode_precision_xyz.value) #define E_NUM(val) PRECISION(val, this->config.gcode_precision_e.get_at(m_tool->id())) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 54e94bdf1..08c71bae1 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -156,7 +156,7 @@ void PerimeterGenerator::process() ExPolygons bridgeable = union_ex(detector.coverage(-1, true)); if (!bridgeable.empty()) { //check if we get everything or just the bridgeable area - if (this->config->no_perimeter_unsupported_algo == npuaNoPeri || this->config->no_perimeter_unsupported_algo == npuaFilled) { + if (this->config->no_perimeter_unsupported_algo.value == npuaNoPeri || this->config->no_perimeter_unsupported_algo.value == npuaFilled) { //we bridge everything, even the not-bridgeable bits for (size_t i = 0; i < unsupported_filtered.size();) { ExPolygon &poly_unsupp = *(unsupported_filtered.begin() + i); @@ -177,7 +177,7 @@ void PerimeterGenerator::process() } unsupported_filtered = intersection_ex(last, offset2_ex(unsupported_filtered, double(-perimeter_spacing / 2), double(perimeter_spacing * 3 / 2))); - if (this->config->no_perimeter_unsupported_algo == npuaFilled) { + if (this->config->no_perimeter_unsupported_algo.value == npuaFilled) { for (ExPolygon &expol : unsupported_filtered) { //check if the holes won't be covered by the upper layer //TODO: if we want to do that, we must modify the geometry before making perimeters. @@ -227,7 +227,7 @@ void PerimeterGenerator::process() } //TODO: add other polys as holes inside this one (-margin) - } else if (this->config->no_perimeter_unsupported_algo == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo == npuaBridges){ + } else if (this->config->no_perimeter_unsupported_algo.value == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo.value == npuaBridges){ //simplify to avoid most of artefacts from printing lines. ExPolygons bridgeable_simplified; for (ExPolygon &poly : bridgeable) { @@ -246,7 +246,7 @@ void PerimeterGenerator::process() //unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width); - if (this->config->no_perimeter_unsupported_algo == npuaBridges) { + if (this->config->no_perimeter_unsupported_algo.value == npuaBridges) { ExPolygons unbridgeable = unsupported_filtered; for (ExPolygon &expol : unbridgeable) expol.holes.clear(); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 3d7fcd441..e3a4fbca8 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -535,9 +535,23 @@ const std::vector& Preset::print_options() "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", + "ooze_prevention", "standby_temperature_delta", "interface_shells", + "extrusion_spacing", + "extrusion_width", + "first_layer_extrusion_spacing", + "first_layer_extrusion_width", + "perimeter_extrusion_spacing", + "perimeter_extrusion_width", + "external_perimeter_extrusion_spacing", + "external_perimeter_extrusion_width", + "infill_extrusion_spacing", + "infill_extrusion_width", + "solid_infill_extrusion_spacing", + "solid_infill_extrusion_width", + "top_infill_extrusion_spacing", + "top_infill_extrusion_width", + "support_material_extrusion_width", + "infill_overlap", "bridge_flow_ratio", "infill_anchor", "infill_anchor_max", "clip_multipart_objects", @@ -1373,7 +1387,9 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi for (const t_config_option_key &opt_key : config_this.keys()) { const ConfigOption *this_opt = config_this.option(opt_key); const ConfigOption *other_opt = config_other.option(opt_key); - if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt) + //dirty if both exist, they aren't both phony and value is different + if (this_opt != nullptr && other_opt != nullptr && !(this_opt->phony && other_opt->phony) + && ((*this_opt != *other_opt) || (this_opt->phony != other_opt->phony))) { if (opt_key == "bed_shape" || opt_key == "thumbnails" || opt_key == "compatible_prints" || opt_key == "compatible_printers") { // Scalar variable, or a vector variable, which is independent from number of extruders, diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index abf223733..6dec9589d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1535,7 +1535,7 @@ std::pair Print::validate() const "all nozzles have to be of the same diameter.") }; } if (this->has_wipe_tower()) { - if (object->config().support_material_contact_distance_type == zdNone) { + if (object->config().support_material_contact_distance_type.value == zdNone) { // Soluble interface if (! object->config().support_material_synchronize_layers) return { PrintBase::PrintValidationError::pveWrongSettings,L("For the Wipe Tower to work with the soluble supports, the support layers need to be synchronized with the object layers.") }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1f17dabb3..b970f99a9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -830,9 +830,23 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("external_perimeter_extrusion_spacing", coFloatOrPercent); + def->label = L("External perimeters"); + def->full_label = L("External perimeters spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like the External perimeters width, but this value is the distance between the edge and the 'frontier' to the next perimeter." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("external_perimeter_cut_corners", coPercent); def->label = L("Cutting corners"); def->full_label = L("Ext. peri. cut corners"); @@ -1140,16 +1154,30 @@ void PrintConfigDef::init_fff_params() def->label = L("Default extrusion width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to allow a manual extrusion width. " - "If left to zero, Slic3r derives extrusion widths from the nozzle diameter " - "(see the tooltips for perimeter extrusion width, infill extrusion width etc). " - "If expressed as percentage (for example: 105%), it will be computed over nozzle diameter."); + "If left to zero, Slic3r derives extrusion widths from the nozzle diameter " + "(see the tooltips for perimeter extrusion width, infill extrusion width etc). " + "If expressed as percentage (for example: 105%), it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("extrusion_spacing", coFloatOrPercent); + def->label = L("Default extrusion spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like Default extrusion width but spacing is the distance between two lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("fan_always_on", coBools); def->label = L("Keep fan always on"); def->category = OptionCategory::cooling; @@ -1709,16 +1737,32 @@ void PrintConfigDef::init_fff_params() def->full_label = L("First layer width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for first layer. " - "You can use this to force fatter extrudates for better adhesion. If expressed " - "as percentage (for example 140%) it will be computed over the nozzle diameter " - "of the nozzle used for the type of extrusion. " - "If set to zero, it will use the default extrusion width."); + "You can use this to force fatter extrudates for better adhesion. If expressed " + "as percentage (for example 140%) it will be computed over the nozzle diameter " + "of the nozzle used for the type of extrusion. " + "If set to zero, it will use the default extrusion width."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(140, true)); + def = this->add("first_layer_extrusion_spacing", coFloatOrPercent); + def->label = L("First layer"); + def->full_label = L("First layer spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like First layer width but spacing is the distance between two lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("first_layer_height", coFloatOrPercent); def->label = L("First layer height"); def->category = OptionCategory::perimeter; @@ -2086,17 +2130,34 @@ void PrintConfigDef::init_fff_params() def = this->add("infill_extrusion_width", coFloatOrPercent); def->label = L("Infill"); + def->full_label = L("Infill width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill. " - "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " - "You may want to use fatter extrudates to speed up the infill and make your parts stronger. " - "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " + "You may want to use fatter extrudates to speed up the infill and make your parts stronger. " + "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("infill_extrusion_spacing", coFloatOrPercent); + def->label = L("Infill"); + def->full_label = L("Infill spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like First layer width but spacing is the distance between two lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("infill_first", coBool); def->label = L("Infill before perimeters"); def->category = OptionCategory::infill; @@ -2799,15 +2860,31 @@ void PrintConfigDef::init_fff_params() def->full_label = ("Perimeter width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for perimeters. " - "You may want to use thinner extrudates to get more accurate surfaces. " - "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " - "If expressed as percentage (for example 105%) it will be computed over nozzle diameter."); + "You may want to use thinner extrudates to get more accurate surfaces. " + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " + "If expressed as percentage (for example 105%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->aliases = { "perimeters_extrusion_width" }; def->min = 0; + def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("perimeter_extrusion_spacing", coFloatOrPercent); + def->label = L("Perimeters"); + def->full_label = ("Perimeter spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like Perimeter width but spacing is the distance between two perimeter lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->aliases = { "perimeters_extrusion_width" }; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("perimeter_speed", coFloat); def->label = L("Default"); def->full_label = ("Default speed"); @@ -3141,10 +3218,12 @@ void PrintConfigDef::init_fff_params() def = this->add("skirt_extrusion_width", coFloatOrPercent); def->label = L("Skirt"); + def->full_label = L("Skirt width"); def->category = OptionCategory::width; def->tooltip = L("Horizontal width of the skirt that will be printed around each object."); def->sidetext = L("mm"); def->min = 0; + def->max = 1000; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -3306,14 +3385,30 @@ void PrintConfigDef::init_fff_params() def->full_label = ("Solid infill width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. " - "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " - "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " + "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("solid_infill_extrusion_spacing", coFloatOrPercent); + def->label = L("Solid spacing"); + def->full_label = ("Solid infill spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like Solid infill width but spacing is the distance between two lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("solid_infill_speed", coFloatOrPercent); def->label = L("Solid"); def->full_label = ("Solid infill speed"); @@ -3577,11 +3672,12 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Support material width"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for support material. " - "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " - "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); + "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " + "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->max = 1000; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); @@ -3857,15 +3953,30 @@ void PrintConfigDef::init_fff_params() def->label = L("Top solid infill"); def->category = OptionCategory::width; def->tooltip = L("Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. " - "You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. " - "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " - "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); + "You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. " + "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " + "If expressed as percentage (for example 110%) it will be computed over nozzle diameter."); def->sidetext = L("mm or %"); def->ratio_over = "nozzle_diameter"; def->min = 0; + def->max = 1000; + def->can_phony = true; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("top_infill_extrusion_spacing", coFloatOrPercent); + def->label = L("Top solid spacing"); + def->category = OptionCategory::width; + def->tooltip = L("Like Top solid infill width but spacing is the distance between two lines (as they overlap a bit, it's not the same)." + "\nSetting the spacing will deactivate the width setting, and vice versa."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 1000; + def->can_phony = true; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def = this->add("top_solid_infill_speed", coFloatOrPercent); def->label = L("Top solid"); def->full_label = L("Top solid speed"); @@ -5141,6 +5252,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va // In PrusaSlicer 2.3.0-alpha0 the "monotonic" infill was introduced, which was later renamed to "monotonous". if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern" || opt_key == "solid_fill_pattern" || opt_key == "support_material_interface_pattern")) value = "monotonic"; + // some changes has occurs between rectilineargapfill and monotonicgapfill. Set them at the right value for each type + if (value == "rectilineargapfill" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern" || opt_key == "support_material_interface_pattern")) + value = "monotonicgapfill"; + if (value == "monotonicgapfill" && (opt_key == "solid_fill_pattern")) + value = "rectilineargapfill"; + if (ignore.find(opt_key) != ignore.end()) { opt_key = ""; @@ -5292,7 +5409,14 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, "milling_post_process", "milling_extra_size", "milling_after_z", -"milling_speed" +"milling_speed", +"extrusion_spacing", +"first_layer_extrusion_spacing", +"perimeter_extrusion_spacing", +"external_perimeter_extrusion_spacing", +"infill_extrusion_spacing", +"solid_infill_extrusion_spacing", +"top_infill_extrusion_spacing" }; //looks if it's to be removed, or have to be transformed @@ -5360,6 +5484,15 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, value = "marlin"; else if ("klipper" == value) value = "reprap"; + } else if (opt_key.find("extrusion_width") != std::string::npos) { + if (std::set{"extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", + "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width"}.count(opt_key) > 0) { + const ConfigOptionFloatOrPercent* opt = all_conf.option(opt_key); + if (opt->phony) { + //bypass the phony kill switch from Config::opt_serialize + value = opt->serialize(); + } + } } } @@ -5592,6 +5725,7 @@ std::string DynamicPrintConfig::validate() { FullPrintConfig fpc; fpc.apply(*this, true); + // Verify this print options through the FullPrintConfig. return fpc.validate(); } @@ -5601,6 +5735,261 @@ std::string DynamicPrintConfig::validate() } } +template +const TYPE* find_option(const t_config_option_key &opt_key, DynamicPrintConfig* default_config, const std::vector &other_config) { + const TYPE* option = default_config->option(opt_key); + if (option) + return option; + for (const DynamicPrintConfig* conf : other_config) { + option = conf->option(opt_key); + if (option) + return option; + } + return nullptr; +} + +bool DynamicPrintConfig::update_phony(const std::vector config_collection) { + bool something_changed = false; + //update width/spacing links + const char* widths[] = { "", "external_perimeter_", "perimeter_", "infill_", "solid_infill_", "top_infill_", "support_material_", "first_layer_", "skirt_" }; + for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++i) { + std::string key_width(widths[i]); + key_width += "extrusion_width"; + std::string key_spacing(widths[i]); + key_spacing += "extrusion_spacing"; + ConfigOptionFloatOrPercent* width_option = this->option(key_width); + ConfigOptionFloatOrPercent* spacing_option = this->option(key_spacing); + if (width_option && spacing_option) + if (!spacing_option->phony && width_option->phony) + something_changed |= value_changed(key_spacing, config_collection); + else + something_changed |= value_changed(key_width, config_collection); + } + + return something_changed; +} + +bool DynamicPrintConfig::value_changed(const t_config_option_key& opt_key, const std::vector config_collection) { + + if (opt_key == "layer_height") { + update_phony(config_collection); + } + + bool something_changed = false; + // width -> spacing + if (opt_key.find("extrusion_spacing") != std::string::npos) { + const ConfigOptionFloats* nozzle_diameter_option = find_option("nozzle_diameter", this, config_collection); + const ConfigOptionFloat* layer_height_option = find_option("layer_height", this, config_collection); + ConfigOptionFloatOrPercent* spacing_option = this->option(opt_key); + if (layer_height_option && spacing_option && nozzle_diameter_option) { + //compute spacing with current height and change the width + double max_nozzle_diameter = 0; + for (double dmr : nozzle_diameter_option->values) + max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); + Flow flow = Flow::new_from_spacing(spacing_option->get_abs_value(max_nozzle_diameter), max_nozzle_diameter, layer_height_option->value, false); + if (opt_key == "extrusion_spacing") { + ConfigOptionFloatOrPercent* width_option = this->option("extrusion_width"); + if (width_option) { + width_option->phony = true; + spacing_option->phony = false; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "first_layer_extrusion_spacing") { + ConfigOptionFloatOrPercent* width_option = this->option("first_layer_extrusion_width"); + if (width_option) { + width_option->phony = true; + spacing_option->phony = false; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "perimeter_extrusion_spacing") { + const ConfigOptionPercent* perimeter_overlap_option = find_option("perimeter_overlap", this, config_collection); + ConfigOptionFloatOrPercent* width_option = this->option("perimeter_extrusion_width"); + if (width_option && perimeter_overlap_option) { + width_option->phony = true; + spacing_option->phony = false; + flow.spacing_ratio = perimeter_overlap_option->get_abs_value(1); + flow.width = spacing_option->get_abs_value(max_nozzle_diameter) + layer_height_option->value * (1. - 0.25 * PI) * flow.spacing_ratio; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "external_perimeter_extrusion_spacing") { + const ConfigOptionPercent* external_perimeter_overlap_option = find_option("external_perimeter_overlap", this, config_collection); + ConfigOptionFloatOrPercent* width_option = this->option("external_perimeter_extrusion_width"); + if (width_option && external_perimeter_overlap_option) { + width_option->phony = true; + spacing_option->phony = false; + flow.spacing_ratio = external_perimeter_overlap_option->get_abs_value(0.5); + flow.width = spacing_option->get_abs_value(max_nozzle_diameter) + layer_height_option->value * (1. - 0.25 * PI) * flow.spacing_ratio; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "infill_extrusion_spacing") { + ConfigOptionFloatOrPercent* width_option = this->option("infill_extrusion_width"); + if (width_option) { + width_option->phony = true; + spacing_option->phony = false; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "solid_infill_extrusion_spacing") { + ConfigOptionFloatOrPercent* width_option = this->option("solid_infill_extrusion_width"); + if (width_option) { + width_option->phony = true; + spacing_option->phony = false; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + if (opt_key == "top_infill_extrusion_spacing") { + ConfigOptionFloatOrPercent* width_option = this->option("top_infill_extrusion_width"); + if (width_option) { + width_option->phony = true; + spacing_option->phony = false; + width_option->value = (spacing_option->percent) ? std::round(100 * flow.width / max_nozzle_diameter) : (std::round(flow.width * 10000) / 10000); + width_option->percent = spacing_option->percent; + something_changed = true; + } + } + /*if (opt_key == "support_material_extrusion_spacing") { + if (spacing_option->percent) + this->set_key_value("support_material_extrusion_width", new ConfigOptionFloatOrPercent(std::round(100 * flow.width / max_nozzle_diameter), true)); + else + this->set_key_value("support_material_extrusion_width", new ConfigOptionFloatOrPercent(std::round(flow.width * 10000) / 10000, false)); + something_changed = true; + } + if (opt_key == "skirt_extrusion_spacing") { + if (spacing_option->percent) + this->set_key_value("skirt_extrusion_width", new ConfigOptionFloatOrPercent(std::round(100 * flow.width / max_nozzle_diameter), true)); + else + this->set_key_value("skirt_extrusion_width", new ConfigOptionFloatOrPercent(std::round(flow.width * 10000) / 10000, false)); + something_changed = true; + }*/ + } + } + if (opt_key.find("extrusion_width") != std::string::npos) { + const ConfigOptionFloats* nozzle_diameter_option = find_option("nozzle_diameter", this, config_collection); + const ConfigOptionFloat* layer_height_option = find_option("layer_height", this, config_collection); + ConfigOptionFloatOrPercent* width_option = this->option(opt_key); + if (layer_height_option && width_option && nozzle_diameter_option) { + //compute spacing with current height and change the width + double max_nozzle_diameter = 0; + for (double dmr : nozzle_diameter_option->values) + max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); + if (opt_key == "extrusion_width") { + ConfigOptionFloatOrPercent* spacing_option = this->option("extrusion_spacing"); + if (width_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frPerimeter, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "first_layer_extrusion_width") { + ConfigOptionFloatOrPercent* spacing_option = this->option("first_layer_extrusion_spacing"); + if (width_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frPerimeter, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "perimeter_extrusion_width") { + const ConfigOptionPercent* perimeter_overlap_option = find_option("perimeter_overlap", this, config_collection); + ConfigOptionFloatOrPercent* spacing_option = this->option("perimeter_extrusion_spacing"); + if (width_option && perimeter_overlap_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frExternalPerimeter, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + flow.spacing_ratio = perimeter_overlap_option->get_abs_value(1); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "external_perimeter_extrusion_width") { + const ConfigOptionPercent* external_perimeter_overlap_option = find_option("external_perimeter_overlap", this, config_collection); + ConfigOptionFloatOrPercent* spacing_option = this->option("external_perimeter_extrusion_spacing"); + if (width_option && external_perimeter_overlap_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow ext_perimeter_flow = Flow::new_from_config_width(FlowRole::frPerimeter, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + ext_perimeter_flow.spacing_ratio = external_perimeter_overlap_option->get_abs_value(0.5); + spacing_option->value = (width_option->percent) ? std::round(100 * ext_perimeter_flow.spacing() / max_nozzle_diameter) : (std::round(ext_perimeter_flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "infill_extrusion_width") { + ConfigOptionFloatOrPercent* spacing_option = this->option("infill_extrusion_spacing"); + if (width_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frInfill, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "solid_infill_extrusion_width") { + ConfigOptionFloatOrPercent* spacing_option = this->option("solid_infill_extrusion_spacing"); + if (width_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frSolidInfill, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + if (opt_key == "top_infill_extrusion_width") { + ConfigOptionFloatOrPercent* spacing_option = this->option("top_infill_extrusion_spacing"); + if (width_option) { + width_option->phony = false; + spacing_option->phony = true; + Flow flow = Flow::new_from_config_width(FlowRole::frTopSolidInfill, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + spacing_option->value = (width_option->percent) ? std::round(100 * flow.spacing() / max_nozzle_diameter) : (std::round(flow.spacing() * 10000) / 10000); + spacing_option->percent = width_option->percent; + something_changed = true; + } + } + //if (opt_key == "support_material_extrusion_width") { + // Flow flow = Flow::new_from_config_width(FlowRole::frSupportMaterial, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + // if (width_option->percent) + // this->set_key_value("support_material_extrusion_spacing", new ConfigOptionFloatOrPercent(std::round(100 * flow.spacing() / max_nozzle_diameter), true)); + // else + // this->set_key_value("support_material_extrusion_spacing", new ConfigOptionFloatOrPercent(std::round(flow.spacing() * 10000) / 10000, false)); + // something_changed = true; + //} + //if (opt_key == "skirt_extrusion_width") { + // Flow flow = Flow::new_from_config_width(FlowRole::frPerimeter, *width_option, max_nozzle_diameter, layer_height_option->value, 0); + // if (width_option->percent) + // this->set_key_value("skirt_extrusion_spacing", new ConfigOptionFloatOrPercent(std::round(100 * flow.spacing() / max_nozzle_diameter), true)); + // else + // this->set_key_value("skirt_extrusion_spacing", new ConfigOptionFloatOrPercent(std::round(flow.spacing() * 10000) / 10000, false)); + // something_changed = true; + //} + } + } + return something_changed; +} + //FIXME localize this function. std::string FullPrintConfig::validate() { @@ -5701,8 +6090,8 @@ std::string FullPrintConfig::validate() return "Invalid value for --extrusion-multiplier"; // --default-acceleration - if ((this->perimeter_acceleration != 0. || this->infill_acceleration != 0. || this->bridge_acceleration != 0. || this->first_layer_acceleration != 0.) && - this->default_acceleration == 0.) + if ((this->perimeter_acceleration.value != 0. || this->infill_acceleration.value != 0. || this->bridge_acceleration.value != 0. || this->first_layer_acceleration.value != 0.) && + this->default_acceleration.value == 0.) return "Invalid zero value for --default-acceleration when using other acceleration settings"; // --spiral-vase @@ -5732,10 +6121,10 @@ std::string FullPrintConfig::validate() double max_nozzle_diameter = 0.; for (double dmr : this->nozzle_diameter.values) max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); - const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" }; + const char *widths[] = { "", "external_perimeter_", "perimeter_", "infill_", "solid_infill_", "top_infill_", "support_material_", "first_layer_", "skirt_" }; for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { std::string key(widths[i]); - key += "_extrusion_width"; + key += "extrusion_width"; if (this->get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter) return std::string("Invalid extrusion width (too large): ") + key; } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4a6f6fe01..6bb0493e5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -456,6 +456,13 @@ public: void to_prusa(t_config_option_key& opt_key, std::string& value) const override { PrintConfigDef::to_prusa(opt_key, value, *this); } + + /// + /// callback to changed other settings that are linked (like width & spacing) + /// + /// name of the changed option + bool value_changed(const t_config_option_key& opt_key, const std::vector config_collection); + bool update_phony(const std::vector config_collection); }; class StaticPrintConfig : public StaticConfig diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2dd4d1c8b..a852fc77f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -725,7 +725,7 @@ namespace Slic3r { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { steps.emplace_back(posSupportMaterial); - if (m_config.support_material_contact_distance_top == 0. || m_config.support_material_contact_distance_bottom == 0.) { + if (m_config.support_material_contact_distance_top.value == 0. || m_config.support_material_contact_distance_bottom.value == 0.) { // Enabling / disabling supports while soluble support interface is enabled. // This changes the bridging logic (bridging enabled without supports, disabled with supports). // Reset everything. @@ -1091,20 +1091,20 @@ namespace Slic3r { if (!intersect.empty()) { double area_intersect = 0; // calculate area to decide if area is small enough for autofill - if (layerm->region()->config().infill_dense_algo == dfaAutoNotFull || layerm->region()->config().infill_dense_algo == dfaAutoOrEnlarged) + if (layerm->region()->config().infill_dense_algo.value == dfaAutoNotFull || layerm->region()->config().infill_dense_algo.value == dfaAutoOrEnlarged) for (ExPolygon poly_inter : intersect) area_intersect += poly_inter.area(); - if (layerm->region()->config().infill_dense_algo == dfaEnlarged - || (layerm->region()->config().infill_dense_algo == dfaAutoOrEnlarged && surf.area() <= area_intersect * COEFF_SPLIT)) { + if (layerm->region()->config().infill_dense_algo.value == dfaEnlarged + || (layerm->region()->config().infill_dense_algo.value == dfaAutoOrEnlarged && surf.area() <= area_intersect * COEFF_SPLIT)) { //expand the area a bit intersect = offset_ex(intersect, double(scale_(layerm->region()->config().external_infill_margin.get_abs_value( region->config().perimeters == 0 ? 0 : (layerm->flow(frExternalPerimeter).width + layerm->flow(frPerimeter).spacing() * (region->config().perimeters - 1)))))); - } else if (layerm->region()->config().infill_dense_algo == dfaAutoNotFull - || layerm->region()->config().infill_dense_algo == dfaAutomatic) { + } else if (layerm->region()->config().infill_dense_algo.value == dfaAutoNotFull + || layerm->region()->config().infill_dense_algo.value == dfaAutomatic) { //like intersect.empty() but more resilient - if (layerm->region()->config().infill_dense_algo == dfaAutomatic + if (layerm->region()->config().infill_dense_algo.value == dfaAutomatic || surf.area() > area_intersect * COEFF_SPLIT) { ExPolygons cover_intersect; @@ -1237,7 +1237,7 @@ namespace Slic3r { Polygons layerm_slices_surfaces = to_polygons(layerm->slices().surfaces); // no_perimeter_full_bridge allow to put bridges where there are nothing, hence adding area to slice, that's why we need to start from the result of PerimeterGenerator. - if (layerm->region()->config().no_perimeter_unsupported_algo == npuaFilled) { + if (layerm->region()->config().no_perimeter_unsupported_algo.value == npuaFilled) { layerm_slices_surfaces = union_(layerm_slices_surfaces, to_polygons(layerm->fill_surfaces)); } @@ -3512,11 +3512,11 @@ namespace Slic3r { 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. - ((region->config().fill_pattern == ipRectilinear || - region->config().fill_pattern == ipMonotonic || - region->config().fill_pattern == ipGrid || - region->config().fill_pattern == ipLine || - region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + ((region->config().fill_pattern.value == ipRectilinear || + region->config().fill_pattern.value == ipMonotonic || + region->config().fill_pattern.value == ipGrid || + region->config().fill_pattern.value == ipLine || + region->config().fill_pattern.value == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon& expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index dfdac2fe8..d85d77a77 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1242,7 +1242,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // it will support layers printed with a bridging flow. if (SupportMaterialInternal::has_bridging_extrusions(layer)) { coordf_t bridging_height = layer.height; - if (m_object_config->support_material_contact_distance_type == zdFilament) { + if (m_object_config->support_material_contact_distance_type.value == zdFilament) { bridging_height = 0.; for (const LayerRegion *region : layer.regions()) bridging_height += region->region()->bridging_height_avg(*m_print_config); @@ -1513,7 +1513,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // According to Jindrich the bottom surfaces work well. //FIXME test the bridging flow instead? m_support_material_interface_flow.nozzle_diameter; - layer_new.height_block = ((m_object_config->support_material_contact_distance_type == zdPlane) ? object.layers()[layer_id + 1]->height : layer_new.height); + layer_new.height_block = ((m_object_config->support_material_contact_distance_type.value == zdPlane) ? object.layers()[layer_id + 1]->height : layer_new.height); layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : (layer.print_z + layer_new.height_block + this->m_slicing_params.gap_object_support); layer_new.bottom_z = layer.print_z; @@ -2135,7 +2135,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; for (LayerRegion *region : object_layer.regions()) { - coordf_t bridging_height = m_object_config->support_material_contact_distance_type == zdFilament + coordf_t bridging_height = m_object_config->support_material_contact_distance_type.value == zdFilament ? region->region()->bridging_height_avg(*this->m_print_config) : object_layer.height; if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) diff --git a/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp index 0610199f5..c5bafdde9 100644 --- a/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -50,6 +50,18 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vectorAdd(sys_colour, -1, wxALIGN_CENTRE_VERTICAL); grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + auto default_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the last saved preset, but is not the system value"))); + default_label->SetForegroundColour(wxGetApp().get_label_clr_default()); + auto default_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_default()); + default_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([default_colour, default_label](wxCommandEvent e) + { + default_label->SetForegroundColour(default_colour->GetColour()); + default_label->Refresh(); + })); + grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(default_colour, -1, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(default_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset"))); mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified()); auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_modified()); @@ -61,15 +73,29 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vectorAdd(0, -1, wxALIGN_CENTRE_VERTICAL); grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL); grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + + auto phony_label = new wxStaticText(this, wxID_ANY, _(L("Value isn't taken into account, it's computed over an other field."))); + phony_label->SetForegroundColour(wxGetApp().get_label_clr_phony()); + auto phony_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_phony()); + phony_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([phony_colour, phony_label](wxCommandEvent e) + { + phony_label->SetForegroundColour(phony_colour->GetColour()); + phony_label->Refresh(); + })); + grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(phony_colour, -1, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(phony_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL); main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); wxButton* btn = static_cast(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) { + btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, default_colour, phony_colour, this](wxCommandEvent&) { wxGetApp().set_label_clr_sys(sys_colour->GetColour()); wxGetApp().set_label_clr_modified(mod_colour->GetColour()); + wxGetApp().set_label_clr_default(default_colour->GetColour()); + wxGetApp().set_label_clr_phony(phony_colour->GetColour()); EndModal(wxID_OK); }); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3563443c5..6073e3d0f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1033,6 +1033,7 @@ void GUI_App::init_label_colours() m_color_label_sys = wxColour(26, 132, 57); } m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + m_color_label_phony = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); } void GUI_App::update_label_colours_from_appconfig() @@ -1048,6 +1049,18 @@ void GUI_App::update_label_colours_from_appconfig() if (str != "") m_color_label_modified = wxColour(str); } + + if (app_config->has("label_clr_default")) { + auto str = app_config->get("label_clr_default"); + if (str != "") + m_color_label_default = wxColour(str); + } + + if (app_config->has("label_clr_phony")) { + auto str = app_config->get("label_clr_phony"); + if (str != "") + m_color_label_phony = wxColour(str); + } } void GUI_App::init_fonts() @@ -1099,6 +1112,22 @@ void GUI_App::set_label_clr_sys(const wxColour& clr) { app_config->save(); } +void GUI_App::set_label_clr_default(const wxColour& clr) { + m_color_label_default = clr; + auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); + std::string str = clr_str.ToStdString(); + app_config->set("label_clr_default", str); + app_config->save(); +} + +void GUI_App::set_label_clr_phony(const wxColour& clr) { + m_color_label_phony = clr; + auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); + std::string str = clr_str.ToStdString(); + app_config->set("label_clr_phony", str); + app_config->save(); +} + wxSize GUI_App::get_min_size() const { return wxSize(76*m_em_unit, 49 * m_em_unit); @@ -1154,7 +1183,7 @@ void GUI_App::check_printer_presets() for (const std::string& preset_name : preset_names) msg_text += "\n \"" + from_u8(preset_name) + "\","; msg_text.RemoveLast(); - msg_text += "\n\n" + _L("But since this version of PrusaSlicer we don't show this information in Printer Settings anymore.\n" + msg_text += "\n\n" + _L("But since this version of " SLIC3R_APP_NAME " we don't show this information in Printer Settings anymore.\n" "Settings will be available in physical printers settings.") + "\n\n" + _L("By default new Printer devices will be named as \"Printer N\" during its creation.\n" "Note: This name can be changed later from the physical printers settings"); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index dc245713b..ba764badb 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -116,6 +116,7 @@ private: wxColour m_color_label_modified; wxColour m_color_label_sys; wxColour m_color_label_default; + wxColour m_color_label_phony; wxFont m_small_font; wxFont m_bold_font; @@ -171,10 +172,13 @@ public: void update_fonts(const MainFrame *main_frame = nullptr); void set_label_clr_modified(const wxColour& clr); void set_label_clr_sys(const wxColour& clr); + void set_label_clr_default(const wxColour& clr); + void set_label_clr_phony(const wxColour& clr); const wxColour& get_label_clr_modified(){ return m_color_label_modified; } const wxColour& get_label_clr_sys() { return m_color_label_sys; } const wxColour& get_label_clr_default() { return m_color_label_default; } + const wxColour& get_label_clr_phony() { return m_color_label_phony; } const wxFont& small_font() { return m_small_font; } const wxFont& bold_font() { return m_bold_font; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bb2c6f20c..3991def96 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -251,6 +251,8 @@ void Tab::create_preset_tab() for (Tab *tab : wxGetApp().tabs_list) { tab->m_sys_label_clr = wxGetApp().get_label_clr_sys(); tab->m_modified_label_clr = wxGetApp().get_label_clr_modified(); + tab->m_default_text_clr = wxGetApp().get_label_clr_default(); + tab->m_phony_text_clr = wxGetApp().get_label_clr_phony(); tab->update_labels_colour(); } } @@ -260,7 +262,8 @@ void Tab::create_preset_tab() // Colors for ui "decoration" m_sys_label_clr = wxGetApp().get_label_clr_sys(); m_modified_label_clr = wxGetApp().get_label_clr_modified(); - m_default_text_clr = wxGetApp().get_label_clr_default(); + m_default_text_clr = wxGetApp().get_label_clr_default(); + m_phony_text_clr = wxGetApp().get_label_clr_phony(); // Sizer with buttons for mode changing m_mode_sizer = new ModeSizer(panel); @@ -513,6 +516,15 @@ void Tab::update_labels_colour() else color = &m_modified_label_clr; } + if ((opt.second & osCurrentPhony) != 0) + color = &m_phony_text_clr; + else { + if ((opt.second & osInitPhony) != 0) + color = &m_modified_label_clr; + else if ((opt.second & osSystemPhony) != 0) + color = &m_default_text_clr; + } + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || opt.first == "compatible_prints" || opt.first == "compatible_printers" ) { if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end()) @@ -591,7 +603,17 @@ void Tab::decorate() icon = &m_bmp_white_bullet; tt = &m_tt_white_bullet; } - + + //color for phony things + if ((opt.second & osCurrentPhony) != 0) + color = &m_phony_text_clr; + else { + if ((opt.second & osInitPhony) != 0) + color = &m_modified_label_clr; + else if ((opt.second & osSystemPhony) != 0) + color = &m_default_text_clr; + } + if (colored_label_clr) { *colored_label_clr = *color; continue; @@ -634,8 +656,28 @@ void Tab::update_changed_ui() for (auto& it : m_options_list) it.second = m_opt_status_value; - for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue; - for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue; + + const Preset& edited_preset = m_presets->get_edited_preset(); + const Preset& selected_preset = m_presets->get_selected_preset(); + const Preset* system_preset = m_presets->get_selected_preset_parent(); + for (auto& opt_key : m_presets->get_edited_preset().config.keys()) { + if (edited_preset.config.option(opt_key)->phony) + //ensure that osCurrentPhony is in the bitmask + m_options_list[opt_key] |= osCurrentPhony; + if (selected_preset.config.option(opt_key)->phony) + m_options_list[opt_key] |= osInitPhony; + if (system_preset && system_preset->config.option(opt_key)->phony) + m_options_list[opt_key] |= osSystemPhony; + } + + //don't let option that were phony be resetable. + for (auto opt_key : dirty_options) + if( (m_options_list[opt_key] & osInitPhony) == 0) + //ensure that osInitValue is not in the bitmask + m_options_list[opt_key] &= ~osInitValue; + for (auto opt_key : nonsys_options) + if ((m_options_list[opt_key] & osSystemPhony) == 0) + m_options_list[opt_key] &= ~osSystemValue; decorate(); @@ -1141,6 +1183,13 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) wxGetApp().plater()->on_extruders_change(boost::any_cast(value)); } + //wxGetApp().preset_bundle->value_changed(opt_key); + if (m_config->value_changed(opt_key, { wxGetApp().plater()->config() })) { + update_dirty(); + //# Initialize UI components with the config values. + reload_config(); + } + update(); } @@ -2956,6 +3005,7 @@ void Tab::load_current_preset() update_tab_ui(); // update show/hide tabs + //merill note: this is a bit of anti-inheritance pattern if (m_type == Slic3r::Preset::TYPE_PRINTER) { const PrinterTechnology printer_technology = m_presets->get_edited_preset().printer_technology(); if (printer_technology != static_cast(this)->m_printer_technology) @@ -3002,6 +3052,15 @@ void Tab::load_current_preset() on_presets_changed(); if (m_type == Preset::TYPE_SLA_PRINT || m_type == Preset::TYPE_PRINT) update_frequently_changed_parameters(); + + //update width/spacing links + if (m_type == Preset::TYPE_PRINT) { + //verify that spacings are set + if (m_config->update_phony({ wxGetApp().plater()->config() })) { + update_dirty(); + reload_config(); + } + } } m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index c75b11425..bb10b10db 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -170,6 +170,7 @@ protected: wxColour m_sys_label_clr; wxColour m_modified_label_clr; wxColour m_default_text_clr; + wxColour m_phony_text_clr; // Tooltip text for reset buttons (for whole options group) wxString m_ttg_value_lock; @@ -199,7 +200,13 @@ protected: bool m_show_incompatible_presets; std::vector m_dependent_tabs; - enum OptStatus { osSystemValue = 1, osInitValue = 2 }; + enum OptStatus { + osSystemValue = 1, + osInitValue = 2, + osSystemPhony = 4, + osInitPhony = 8, + osCurrentPhony = 16, + }; std::map m_options_list; int m_opt_status_value = 0;