Browse Source

Merge branch 'merill-merge'

remi durand 3 years ago
parent
commit
dfc9671dc6

+ 34 - 9
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

+ 29 - 5
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;
 }
 
@@ -798,13 +814,21 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
 	return num_key_value_pairs;
 }
 
-void ConfigBase::save(const std::string &file) const
+void ConfigBase::save(const std::string &file, bool to_prusa) const
 {
     boost::nowide::ofstream c;
     c.open(file, std::ios::out | std::ios::trunc);
     c << "# " << Slic3r::header_slic3r_generated() << std::endl;
-    for (const std::string &opt_key : this->keys())
-        c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
+    if (to_prusa)
+        for (std::string opt_key : this->keys()) {
+            std::string value = this->opt_serialize(opt_key);
+            this->to_prusa(opt_key, value);
+            if(!opt_key.empty())
+                c << opt_key << " = " << value << std::endl;
+        }
+    else
+        for (const std::string &opt_key : this->keys())
+            c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
     c.close();
 }
 

+ 18 - 2
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<const ConfigOptionSingle<T>*>(rhs));
         this->value = static_cast<const ConfigOptionSingle<T>*>(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<const ConfigOptionVector<T>*>(rhs));
         this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
+        this->phony = rhs->phony;
     }
 
     // Set from a vector of ConfigOptions. 
@@ -504,6 +513,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
 public:
     ConfigOptionFloat() : ConfigOptionSingle<double>(0) {}
     explicit ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {}
+    explicit ConfigOptionFloat(double _value, bool _phony) : ConfigOptionSingle<double>(_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<T>: Assigning an incompatible type");
         // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
         this->value = (T)rhs->getInt();
+        this->phony = rhs->phony;
     }
 
     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<T>
         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.
@@ -1907,7 +1923,7 @@ public:
     // Returns number of key/value pairs extracted.
     size_t load_from_gcode_string(const char* str);
     void load(const boost::property_tree::ptree &tree);
-    void save(const std::string &file) const;
+    void save(const std::string &file, bool to_prusa = false) const;
 
 	// Set all the nullable values to nils.
     void null_nullables();

+ 2 - 2
src/libslic3r/Fill/FillAdaptive.cpp

@@ -299,8 +299,8 @@ std::pair<double, double> 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,

+ 9 - 9
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<uint8_t>{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);
@@ -1819,14 +1819,14 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
         // Custom G-code does not set the extruder temperature. Do it now.
         if (!print.config().single_extruder_multi_material.value) {
             // Set temperatures of all the printing extruders.
-            for (unsigned int tool_id : print.extruders()) {
-                int temp = print.config().first_layer_temperature.get_at(tool_id);
+            for (const Extruder& tool : m_writer.extruders()) {
+                int temp = print.config().first_layer_temperature.get_at(tool.id());
                 if (temp == 0)
-                    temp = print.config().temperature.get_at(tool_id);
+                    temp = print.config().temperature.get_at(tool.id());
                 if (print.config().ooze_prevention.value)
                     temp += print.config().standby_temperature_delta.value;
                 if (temp > 0)
-                    _write(file, m_writer.set_temperature(temp, false, tool_id));
+                    _write(file, m_writer.set_temperature(temp, false, tool.id()));
             }
         }
         if (wait || print.config().single_extruder_multi_material.value) {

+ 3 - 3
src/libslic3r/GCodeWriter.cpp

@@ -6,9 +6,9 @@
 #include <map>
 #include <assert.h>
 
-#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()))

+ 4 - 4
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();

+ 20 - 4
src/libslic3r/Preset.cpp

@@ -535,9 +535,23 @@ const std::vector<std::string>& 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,

+ 1 - 1
src/libslic3r/Print.cpp

@@ -1535,7 +1535,7 @@ std::pair<PrintBase::PrintValidationError, std::string> 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.") };

+ 414 - 25
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<std::string>{"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<ConfigOptionFloatOrPercent>(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<typename TYPE>
+const TYPE* find_option(const t_config_option_key &opt_key, DynamicPrintConfig* default_config, const std::vector<const DynamicPrintConfig*> &other_config) {
+    const TYPE* option = default_config->option<TYPE>(opt_key);
+    if (option)
+        return option;
+    for (const DynamicPrintConfig* conf : other_config) {
+        option = conf->option<TYPE>(opt_key);
+        if (option)
+            return option;
+    }
+    return nullptr;
+}
+
+bool DynamicPrintConfig::update_phony(const std::vector<const DynamicPrintConfig*> 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<ConfigOptionFloatOrPercent>(key_width);
+        ConfigOptionFloatOrPercent* spacing_option = this->option<ConfigOptionFloatOrPercent>(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<const DynamicPrintConfig*> 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<ConfigOptionFloats>("nozzle_diameter", this, config_collection);
+        const ConfigOptionFloat* layer_height_option = find_option<ConfigOptionFloat>("layer_height", this, config_collection);
+        ConfigOptionFloatOrPercent* spacing_option = this->option<ConfigOptionFloatOrPercent>(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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionPercent>("perimeter_overlap", this, config_collection);
+                ConfigOptionFloatOrPercent* width_option = this->option<ConfigOptionFloatOrPercent>("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<ConfigOptionPercent>("external_perimeter_overlap", this, config_collection);
+                ConfigOptionFloatOrPercent* width_option = this->option<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloats>("nozzle_diameter", this, config_collection);
+        const ConfigOptionFloat* layer_height_option = find_option<ConfigOptionFloat>("layer_height", this, config_collection);
+        ConfigOptionFloatOrPercent* width_option = this->option<ConfigOptionFloatOrPercent>(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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionPercent>("perimeter_overlap", this, config_collection);
+                ConfigOptionFloatOrPercent* spacing_option = this->option<ConfigOptionFloatOrPercent>("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<ConfigOptionPercent>("external_perimeter_overlap", this, config_collection);
+                ConfigOptionFloatOrPercent* spacing_option = this->option<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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;
         }

Some files were not shown because too many files changed in this diff