diff --git a/resources/icons/white/colorchange_add_m.svg b/resources/icons/white/colorchange_add_m.svg
new file mode 100644
index 000000000..2266560da
--- /dev/null
+++ b/resources/icons/white/colorchange_add_m.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="hex_x5F_plus">
+	<g>
+		<path fill="#FFFFFF" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
+	</g>
+	<g id="plus_1_">
+		<g>
+			<path fill="#ED6B21" d="M8,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S8.71,4.61,8.71,5v6
+				C8.71,11.39,8.39,11.71,8,11.71z"/>
+		</g>
+		<g>
+			<path fill="#ED6B21" d="M11,8.71H5C4.61,8.71,4.29,8.39,4.29,8S4.61,7.29,5,7.29h6c0.39,0,0.71,0.32,0.71,0.71
+				S11.39,8.71,11,8.71z"/>
+		</g>
+	</g>
+</g>
+</svg>
diff --git a/resources/icons/white/edit_gcode.svg b/resources/icons/white/edit_gcode.svg
new file mode 100644
index 000000000..85836c0b4
--- /dev/null
+++ b/resources/icons/white/edit_gcode.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="edit_x5F_Gcode">
+	<g>
+		<path fill="#FFFFFF" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
+	</g>
+	<g>
+		<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36
+			s2.69,0.67,2.69,2.36H9.21c0-0.74-0.18-1.11-1.28-1.11c-1.02,0-1.22,0.46-1.22,1.18v3.09c0,0.75,0.19,1.18,1.22,1.18
+			c1.02,0,1.38-0.43,1.38-1.21V8.75H7.97V7.47z"/>
+	</g>
+</g>
+</svg>
diff --git a/resources/icons/white/error_tick.svg b/resources/icons/white/error_tick.svg
new file mode 100644
index 000000000..f3de981f0
--- /dev/null
+++ b/resources/icons/white/error_tick.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="error_tick">
+	<path fill="#FFFFFF" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>	
+	
+    <path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />
+		
+	<circle fill="#ED6B21" cx="8" cy="12" r="1"/>	
+</g>
+</svg>
diff --git a/resources/icons/white/pause_print.svg b/resources/icons/white/pause_print.svg
new file mode 100644
index 000000000..73f747fff
--- /dev/null
+++ b/resources/icons/white/pause_print.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="pause_x5F_print">
+	<g>
+		<path fill="#FFFFFF" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
+	</g>
+	<g>
+		<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6
+			C6.71,11.39,6.39,11.71,6,11.71z"/>
+	</g>
+	<g>
+		<path fill="#ED6B21" d="M10,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S10.71,4.61,10.71,5v6
+			C10.71,11.39,10.39,11.71,10,11.71z"/>
+	</g>
+</g>
+</svg>
diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp
index 02cc126a3..f698a3558 100644
--- a/src/libslic3r/ExPolygon.cpp
+++ b/src/libslic3r/ExPolygon.cpp
@@ -132,8 +132,7 @@ ExPolygon::has_boundary_point(const Point &point) const
     return false;
 }
 
-bool
-ExPolygon::overlaps(const ExPolygon &other) const
+bool ExPolygon::overlaps(const ExPolygon &other) const
 {
     #if 0
     BoundingBox bbox = get_extents(other);
@@ -150,6 +149,7 @@ ExPolygon::overlaps(const ExPolygon &other) const
     #endif
     if (! pl_out.empty())
         return true; 
+    //FIXME ExPolygon::overlaps() shall be commutative, it is not!
     return ! other.contour.points.empty() && this->contains_b(other.contour.points.front());
 }
 
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index ba12a1edb..ff9e439ba 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -5,6 +5,7 @@
 
 #include <cmath>
 #include <cassert>
+#include <chrono>
 
 namespace Slic3r {
 
@@ -230,8 +231,128 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
     return out;
 }
 
+enum class FuzzyShape {
+    Triangle,
+    Sawtooth,
+    Random
+};
+
+static void fuzzy_polygon(Polygon &poly, FuzzyShape shape, double fuzzy_skin_thickness, double fuzzy_skin_point_dist)
+{
+#if 0
+    Point last = poly.points.at(poly.points.size() - 1);
+    Point last_processed = last;
+
+    double max_length = scale_(2);
+    double min_length = scale_(1);
+
+    if (poly.length() < scale_(5))
+        return;
+
+    deepness *= 3;
+
+    bool triangle_or_sawtooth = shape == FuzzyShape::Sawtooth;
+    double length_sum = 0;
+    Points::iterator it = poly.points.begin();
+    while (it != poly.points.end()) {
+        Point &pt = *it;
+
+        Line line(last, pt);
+        double length = line.length();
+
+        // split long line
+        if (length > max_length) {
+            auto parts = int(ceil(length / max_length));
+            if (parts == 2) {
+                Point point_to_insert(line.midpoint());
+                it = poly.points.insert(it, point_to_insert);
+            }
+            else {
+                Vector part_vector = line.vector() / parts;
+
+                Points points_to_insert;
+                Point point_to_insert(last);
+                while (--parts) {
+                    point_to_insert += part_vector;
+                    Point point_to_insert_2(point_to_insert);
+                    points_to_insert.push_back(point_to_insert_2);
+                }
+
+                it = poly.points.insert(it, points_to_insert.begin(), points_to_insert.end());
+            }
+            continue;
+        }
+
+        length_sum += length;
+
+        // join short lines
+        if (length_sum < min_length) {
+            last = pt;
+            it = poly.points.erase(it);
+            continue;
+        }
+
+        line = Line(last_processed, pt);
+        last = pt;
+        last_processed = pt;
+
+        if (shape == FuzzyShape::Random) {
+            triangle_or_sawtooth = !(rand() % 2);
+        }
+
+        Point point_to_insert(triangle_or_sawtooth ? pt : line.midpoint());
+
+        int scale = (rand() % deepness) + 1;
+
+        Vec2d normal = line.normal().cast<double>();
+        normal /= line.length() / scale_(1.) / ((double)scale / 20.);
+
+        it = poly.points.insert(it, point_to_insert + normal.cast<coord_t>()) + 2;
+
+        length_sum = 0;
+    }
+
+#else
+    const double min_dist_between_points = fuzzy_skin_point_dist * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
+    const double range_random_point_dist = fuzzy_skin_point_dist / 2.;
+    double dist_left_over = double(rand()) * (min_dist_between_points / 2) / double(RAND_MAX); // the distance to be traversed on the line before making the first new point
+    Point* p0 = &poly.points.back();
+    Points out;
+    out.reserve(poly.points.size());
+    for (Point &p1 : poly.points)
+    { // 'a' is the (next) new point between p0 and p1
+        Vec2d  p0p1      = (p1 - *p0).cast<double>();
+        double p0p1_size = p0p1.norm();
+        // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
+        double dist_last_point = dist_left_over + p0p1_size * 2.;
+        for (double p0pa_dist = dist_left_over; p0pa_dist < p0p1_size;
+            p0pa_dist += min_dist_between_points + double(rand()) * range_random_point_dist / double(RAND_MAX))
+        {
+            double r = double(rand()) * (fuzzy_skin_thickness * 2.) / double(RAND_MAX) - fuzzy_skin_thickness;
+            out.emplace_back(*p0 + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>());
+            dist_last_point = p0pa_dist;
+        }
+        dist_left_over = p0p1_size - dist_last_point;
+        p0 = &p1;
+    }
+    while (out.size() < 3) {
+        size_t point_idx = poly.size() - 2;
+        out.emplace_back(poly[point_idx]);
+        if (point_idx == 0)
+            break;
+        -- point_idx;
+    }
+    if (out.size() >= 3)
+        poly.points = std::move(out);
+#endif
+}
+
 void PerimeterGenerator::process()
 {
+    // nasty hack! initialize random generator
+    auto time_us = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch()).count();
+    srand(this->layer_id * time_us);
+
     // other perimeters
     m_mm3_per_mm               		= this->perimeter_flow.mm3_per_mm();
     coord_t perimeter_width         = this->perimeter_flow.scaled_width();
@@ -272,6 +393,32 @@ void PerimeterGenerator::process()
         m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
     }
 
+    // fuzzy skin configuration
+    double fuzzy_skin_thickness;
+    double fuzzy_skin_point_dist;
+    FuzzyShape fuzzy_skin_shape;
+    if (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::None) {
+        switch (this->object_config->fuzzy_skin_shape) {
+        case FuzzySkinShape::Triangle1:
+        case FuzzySkinShape::Triangle2:
+        case FuzzySkinShape::Triangle3:
+            fuzzy_skin_shape = FuzzyShape::Triangle;
+            break;
+        case FuzzySkinShape::Sawtooth1:
+        case FuzzySkinShape::Sawtooth2:
+        case FuzzySkinShape::Sawtooth3:
+            fuzzy_skin_shape = FuzzyShape::Sawtooth;
+            break;
+        case FuzzySkinShape::Random1:
+        case FuzzySkinShape::Random2:
+        case FuzzySkinShape::Random3:
+            fuzzy_skin_shape = FuzzyShape::Random;
+            break;
+        }
+        fuzzy_skin_thickness  = scale_(this->object_config->fuzzy_skin_thickness);
+        fuzzy_skin_point_dist = scale_(this->object_config->fuzzy_skin_point_dist);
+    }
+
     // we need to process each island separately because we might have different
     // extra perimeters for each one
     for (const Surface &surface : this->slices->surfaces) {
@@ -352,13 +499,35 @@ void PerimeterGenerator::process()
                     // If i > loop_number, we were looking just for gaps.
                     break;
                 }
-                for (const ExPolygon &expolygon : offsets) {
+                for (ExPolygon &expolygon : offsets) {
 	                // Outer contour may overlap with an inner contour,
 	                // inner contour may overlap with another inner contour,
 	                // outer contour may overlap with itself.
 	                //FIXME evaluate the overlaps, annotate each point with an overlap depth,
-	                // compensate for the depth of intersection.
-                    contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true));
+
+                    bool skip_polygon = false;
+
+                    if (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::None) {
+                        if (i == 0 && (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::ExternalSkipFirst || this->layer_id > 0)) {
+                            if (
+                                this->object_config->fuzzy_skin_perimeter_mode == FuzzySkinPerimeterMode::External ||
+                                this->object_config->fuzzy_skin_perimeter_mode ==  FuzzySkinPerimeterMode::ExternalSkipFirst
+                            ) {
+                                ExPolygon expolygon_fuzzy(expolygon);
+                                fuzzy_polygon(expolygon_fuzzy.contour, fuzzy_skin_shape, fuzzy_skin_thickness, fuzzy_skin_point_dist);
+                                // compensate for the depth of intersection.
+                                contours[i].emplace_back(PerimeterGeneratorLoop(expolygon_fuzzy.contour, i, true)); 
+                                skip_polygon = true;
+                            } else
+                                fuzzy_polygon(expolygon.contour, fuzzy_skin_shape, fuzzy_skin_thickness, fuzzy_skin_point_dist);
+                        }
+                    }
+
+                    if (!skip_polygon) {
+                        // compensate for the depth of intersection.
+                        contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true));
+                    }
+
                     if (! expolygon.holes.empty()) {
                         holes[i].reserve(holes[i].size() + expolygon.holes.size());
                         for (const Polygon &hole : expolygon.holes)
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index c24033df2..f75f653ef 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -411,6 +411,7 @@ const std::vector<std::string>& Preset::print_options()
         "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", 
     	"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
         "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
+        "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
 #ifdef HAS_PRESSURE_EQUALIZER
         "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
 #endif /* HAS_PRESSURE_EQUALIZER */
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index da2b65568..9247e69da 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -1007,6 +1007,68 @@ void PrintConfigDef::init_fff_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionInts { 0 });
 
+    def = this->add("fuzzy_skin_perimeter_mode", coEnum);
+    def->label = L("Fuzzy skin perimeter mode");
+    def->category = L("Fuzzy Skin");
+    def->tooltip = L("Fuzzy skin perimeter mode.");
+
+    def->enum_keys_map = &ConfigOptionEnum<FuzzySkinPerimeterMode>::get_enum_values();
+    def->enum_values.push_back("none");
+    def->enum_values.push_back("external_only");
+    def->enum_values.push_back("external_only_skip_first_layer");
+    def->enum_values.push_back("all");
+    def->enum_labels.push_back(L("None"));
+    def->enum_labels.push_back(L("External"));
+    def->enum_labels.push_back(L("External (skip first layer)"));
+    def->enum_labels.push_back(L("All perimeters"));
+    def->mode = comSimple;
+    def->set_default_value(new ConfigOptionEnum<FuzzySkinPerimeterMode>(FuzzySkinPerimeterMode::None));
+
+    def = this->add("fuzzy_skin_shape", coEnum);
+    def->label = L("Fuzzy skin shape");
+    def->category = L("Fuzzy Skin");
+    def->tooltip = L("Fuzzy skin shape.");
+
+    def->enum_keys_map = &ConfigOptionEnum<FuzzySkinShape>::get_enum_values();
+    def->enum_values.push_back("triangle1");
+    def->enum_values.push_back("triangle2");
+    def->enum_values.push_back("triangle3");
+    def->enum_values.push_back("sawtooth1");
+    def->enum_values.push_back("sawtooth2");
+    def->enum_values.push_back("sawtooth3");
+    def->enum_values.push_back("random1");
+    def->enum_values.push_back("random2");
+    def->enum_values.push_back("random3");
+    def->enum_labels.push_back(L("Triangle (1)"));
+    def->enum_labels.push_back(L("Triangle (2)"));
+    def->enum_labels.push_back(L("Triangle (3)"));
+    def->enum_labels.push_back(L("Sawtooth (1)"));
+    def->enum_labels.push_back(L("Sawtooth (2)"));
+    def->enum_labels.push_back(L("Sawtooth (3)"));
+    def->enum_labels.push_back(L("Random (1)"));
+    def->enum_labels.push_back(L("Random (2)"));
+    def->enum_labels.push_back(L("Random (3)"));
+    def->mode = comSimple;
+    def->set_default_value(new ConfigOptionEnum<FuzzySkinShape>(FuzzySkinShape::Triangle1));
+
+    def = this->add("fuzzy_skin_thickness", coFloat);
+    def->label = L("Fuzzy skin thickness");
+    def->category = L("Fuzzy Skin");
+    def->tooltip = L("");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionFloat(0.3));
+
+    def = this->add("fuzzy_skin_point_dist", coFloat);
+    def->label = L("Fuzzy skin point distance");
+    def->category = L("Fuzzy Skin");
+    def->tooltip = L("");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionFloat(0.8));
+
     def = this->add("gap_fill_speed", coFloat);
     def->label = L("Gap fill");
     def->category = L("Speed");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index fa09cfb17..40929c34b 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -43,6 +43,25 @@ enum AuthorizationType {
     atKeyPassword, atUserPassword
 };
 
+enum class FuzzySkinPerimeterMode {
+    None,
+    External,
+    ExternalSkipFirst,
+    All
+};
+
+enum class FuzzySkinShape {
+    Triangle1,
+    Triangle2,
+    Triangle3,
+    Sawtooth1,
+    Sawtooth2,
+    Sawtooth3,
+    Random1,
+    Random2,
+    Random3
+};
+
 enum InfillPattern : int {
     ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
     ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount,
@@ -140,6 +159,33 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<AuthorizationType
     return keys_map;
 }
 
+template<> inline const t_config_enum_values& ConfigOptionEnum<FuzzySkinPerimeterMode>::get_enum_values() {
+    static t_config_enum_values keys_map;
+    if (keys_map.empty()) {
+        keys_map["none"]                           = int(FuzzySkinPerimeterMode::None);
+        keys_map["external_only"]                  = int(FuzzySkinPerimeterMode::External);
+        keys_map["external_only_skip_first_layer"] = int(FuzzySkinPerimeterMode::ExternalSkipFirst);
+        keys_map["all"]                            = int(FuzzySkinPerimeterMode::All);
+    }
+    return keys_map;
+}
+
+template<> inline const t_config_enum_values& ConfigOptionEnum<FuzzySkinShape>::get_enum_values() {
+    static t_config_enum_values keys_map;
+    if (keys_map.empty()) {
+        keys_map["triangle1"]           = int(FuzzySkinShape::Triangle1);
+        keys_map["triangle2"]           = int(FuzzySkinShape::Triangle2);
+        keys_map["triangle3"]           = int(FuzzySkinShape::Triangle3);
+        keys_map["sawtooth1"]           = int(FuzzySkinShape::Sawtooth1);
+        keys_map["sawtooth2"]           = int(FuzzySkinShape::Sawtooth2);
+        keys_map["sawtooth3"]           = int(FuzzySkinShape::Sawtooth3);
+        keys_map["random1"]             = int(FuzzySkinShape::Random1);
+        keys_map["random2"]             = int(FuzzySkinShape::Random2);
+        keys_map["random3"]             = int(FuzzySkinShape::Random3);
+    }
+    return keys_map;
+}
+
 template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
@@ -432,6 +478,10 @@ public:
     ConfigOptionFloat               elefant_foot_compensation;
     ConfigOptionFloatOrPercent      extrusion_width;
     ConfigOptionFloatOrPercent      first_layer_height;
+    ConfigOptionEnum<FuzzySkinPerimeterMode>    fuzzy_skin_perimeter_mode;
+    ConfigOptionEnum<FuzzySkinShape>            fuzzy_skin_shape;
+    ConfigOptionFloat               fuzzy_skin_thickness;
+    ConfigOptionFloat               fuzzy_skin_point_dist;
     ConfigOptionBool                infill_only_where_needed;
     // Force the generation of solid shells between adjacent materials/volumes.
     ConfigOptionBool                interface_shells;
@@ -477,6 +527,10 @@ protected:
         OPT_PTR(elefant_foot_compensation);
         OPT_PTR(extrusion_width);
         OPT_PTR(first_layer_height);
+        OPT_PTR(fuzzy_skin_perimeter_mode);
+        OPT_PTR(fuzzy_skin_shape);
+        OPT_PTR(fuzzy_skin_thickness);
+        OPT_PTR(fuzzy_skin_point_dist);
         OPT_PTR(infill_only_where_needed);
         OPT_PTR(interface_shells);
         OPT_PTR(layer_height);
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 99b4130f4..dcf94fe9a 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -521,6 +521,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
             || opt_key == "gap_fill_speed"
             || opt_key == "overhangs"
             || opt_key == "first_layer_extrusion_width"
+            || opt_key == "fuzzy_skin_perimeter_mode"
+            || opt_key == "fuzzy_skin_shape"
+            || opt_key == "fuzzy_skin_thickness"
+            || opt_key == "fuzzy_skin_point_dist"
             || opt_key == "perimeter_extrusion_width"
             || opt_key == "infill_overlap"
             || opt_key == "thin_walls"
diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp
index ebb8cc373..d7588e3ba 100644
--- a/src/libslic3r/SLA/SupportPointGenerator.hpp
+++ b/src/libslic3r/SLA/SupportPointGenerator.hpp
@@ -84,6 +84,7 @@ public:
         float                                   overhangs_area = 0.f;
         
         bool overlaps(const Structure &rhs) const { 
+            //FIXME ExPolygon::overlaps() shall be commutative, it is not!
             return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); 
         }
         float overlap_area(const Structure &rhs) const { 
diff --git a/src/miniz/README-Prusa.txt b/src/miniz/README-Prusa.txt
new file mode 100644
index 000000000..7ed99c534
--- /dev/null
+++ b/src/miniz/README-Prusa.txt
@@ -0,0 +1,27 @@
+This library is based on miniz 2.1.0 - amalgamated version.
+
+----------------------------------------------------------------
+
+Merged with https://github.com/richgel999/miniz/pull/147
+to support writing a zipped file using a callback without
+knowing the size of the file up front.
+
+Vojtech made the following comments to the pull request above:
+
+The pull request looks good to me in general. We need such a functionality at https://github.com/prusa3d/PrusaSlicer so we are going to merge it into our project. Namely, we are exporting potentially huge model files, which are XML encoded, thus the compression ration is quite high and keeping the XML encoded model file in memory is not really practical.
+
+I am a bit uneasy about two things though:
+
+mz_zip_writer_create_zip64_extra_data() call at the start of the file block stores uncompressed and compressed size. Naturally the uncompressed size will be over estimated, while the compressed size will be zero (as it was before). I suppose it does not make any difference, as usually the decompressors do not read this block, but they rely on the copy of this block in the central directory at the end of the ZIP file. I used https://en.wikipedia.org/wiki/ZIP_(file_format) as a source and there is no definition of the ZIP64 extra data block, though the "Data descriptor" section says:
+If the bit at offset 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data:
+
+Thus I suppose it is all right if the file size at the start of the file block is not correct.
+
+There are weird conditions in the original code as if (uncomp_size <= 3), if (uncomp_size == 0). I am not quite sure what will happen if one called your modified mz_zip_writer_add_read_buf_callback() with maximum size >= 4, but the callback provided less than 4 bytes. This is not a problem in our application, but in general it could be an issue.
+
+Is it an issue if the maximum size mandates a 64bit format, while the callback provides less than 4GB of data? I suppose there will only be a tiny bit of performance and file size penalty, but the code will work and the exported ZIP file will be valid.
+
+Are you using your modification in a production code? How well is your modification tested?
+
+----------------------------------------------------------------
+
diff --git a/src/miniz/miniz.c b/src/miniz/miniz.c
index 9ded4c75a..66afb94c2 100644
--- a/src/miniz/miniz.c
+++ b/src/miniz/miniz.c
@@ -6370,13 +6370,13 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n
     return MZ_TRUE;
 }
 
-mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
                                 const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
 {
     mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;
     mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
     mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
-    mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0;
+    mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
     size_t archive_name_size;
     mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
     mz_uint8 *pExtra_data = NULL;
@@ -6398,7 +6398,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
 
     pState = pZip->m_pState;
 
-    if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX))
+    if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX))
     {
         /* Source file is too large for non-zip64 */
         /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
@@ -6455,7 +6455,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
     }
 #endif
 
-    if (uncomp_size <= 3)
+    if (max_size <= 3)
         level = 0;
 
     if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))
@@ -6471,7 +6471,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
         MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
     }
 
-    if (uncomp_size && level)
+    if (max_size && level)
     {
         method = MZ_DEFLATED;
     }
@@ -6479,11 +6479,11 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
     MZ_CLEAR_OBJ(local_dir_header);
     if (pState->m_zip64)
     {
-        if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)
+        if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)
         {
             pExtra_data = extra_data;
-            extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
-                                                               (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+            extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+                                                               (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
         }
 
         if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date))
@@ -6534,9 +6534,8 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
         cur_archive_file_ofs += user_extra_data_len;
     }
 
-    if (uncomp_size)
+    if (max_size)
     {
-        mz_uint64 uncomp_remaining = uncomp_size;
         void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
         if (!pRead_buf)
         {
@@ -6545,19 +6544,27 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
 
         if (!level)
         {
-            while (uncomp_remaining)
+            while (1)
             {
-                mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
-                if ((read_callback(callback_opaque, file_ofs, pRead_buf, n) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
+                size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);
+                if (n == 0)
+                    break;
+
+                if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))
                 {
                     pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
                     return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
                 }
-				file_ofs += n;
+                if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)
+                {
+                    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+                    return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+                }
+                file_ofs += n;
                 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
-                uncomp_remaining -= n;
                 cur_archive_file_ofs += n;
             }
+            uncomp_size = file_ofs;
             comp_size = uncomp_size;
         }
         else
@@ -6584,24 +6591,26 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
 
             for (;;)
             {
-                size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
                 tdefl_status status;
                 tdefl_flush flush = TDEFL_NO_FLUSH;
 
-                if (read_callback(callback_opaque, file_ofs, pRead_buf, in_buf_size)!= in_buf_size)
+                size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);
+                if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))
                 {
                     mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
                     break;
                 }
 
-				file_ofs += in_buf_size;
-                uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
-                uncomp_remaining -= in_buf_size;
+                file_ofs += n;
+                uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
 
                 if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque))
                     flush = TDEFL_FULL_FLUSH;
 
-                status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH);
+                if (n == 0)
+                    flush = TDEFL_FINISH;
+
+                status = tdefl_compress_buffer(pComp, pRead_buf, n, flush);
                 if (status == TDEFL_STATUS_DONE)
                 {
                     result = MZ_TRUE;
@@ -6622,6 +6631,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
                 return MZ_FALSE;
             }
 
+            uncomp_size = file_ofs;
             comp_size = state.m_comp_size;
             cur_archive_file_ofs = state.m_cur_archive_file_ofs;
         }
diff --git a/src/miniz/miniz.h b/src/miniz/miniz.h
index 7db62811e..63e526787 100644
--- a/src/miniz/miniz.h
+++ b/src/miniz/miniz.h
@@ -1282,7 +1282,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n
 
 /* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */
 /* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/
-mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 size_to_add,
+mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size,
 	const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
 	const char *user_extra_data_central, mz_uint user_extra_data_central_len);
 
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 8a2257274..7ee5579e7 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -160,6 +160,26 @@ void Control::msw_rescale()
     GetParent()->Layout();
 }
 
+void Control::sys_color_changed()
+{
+    m_bmp_add_tick_on .msw_rescale();
+    m_bmp_add_tick_off.msw_rescale();
+    m_bmp_del_tick_on .msw_rescale();
+    m_bmp_del_tick_off.msw_rescale();
+    m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth();
+
+    m_bmp_one_layer_lock_on   .msw_rescale();
+    m_bmp_one_layer_lock_off  .msw_rescale();
+    m_bmp_one_layer_unlock_on .msw_rescale();
+    m_bmp_one_layer_unlock_off.msw_rescale();
+    m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth();
+
+    m_bmp_revert.msw_rescale();
+    m_revert_icon_dim = m_bmp_revert.GetBmpWidth();
+    m_bmp_cog.msw_rescale();
+    m_cog_icon_dim = m_bmp_cog.GetBmpWidth();
+}
+
 int Control::GetActiveValue() const
 {
     return m_selection == ssLower ?
diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp
index 56baef6cf..0d90367c0 100644
--- a/src/slic3r/GUI/DoubleSlider.hpp
+++ b/src/slic3r/GUI/DoubleSlider.hpp
@@ -192,6 +192,7 @@ public:
     ~Control() {}
 
     void    msw_rescale();
+    void    sys_color_changed();
 
     int     GetMinValue() const { return m_min_value; }
     int     GetMaxValue() const { return m_max_value; }
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 360a5e532..93e259e6a 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -1200,6 +1200,10 @@ boost::any& Choice::get_value()
 		}
 		else if (m_opt_id.compare("ironing_type") == 0)
 			m_value = static_cast<IroningType>(ret_enum);
+        else if (m_opt_id.compare("fuzzy_skin_perimeter_mode") == 0)
+            m_value = static_cast<FuzzySkinPerimeterMode>(ret_enum);
+        else if (m_opt_id.compare("fuzzy_skin_shape") == 0)
+            m_value = static_cast<FuzzySkinShape>(ret_enum);
 		else if (m_opt_id.compare("gcode_flavor") == 0)
 			m_value = static_cast<GCodeFlavor>(ret_enum);
 		else if (m_opt_id.compare("machine_limits_usage") == 0)
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index dea226012..6b664c0ed 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -182,6 +182,10 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 				config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value))); 
 			else if (opt_key.compare("ironing_type") == 0)
 				config.set_key_value(opt_key, new ConfigOptionEnum<IroningType>(boost::any_cast<IroningType>(value))); 
+			else if (opt_key.compare("fuzzy_skin_perimeter_mode") == 0)
+				config.set_key_value(opt_key, new ConfigOptionEnum<FuzzySkinPerimeterMode>(boost::any_cast<FuzzySkinPerimeterMode>(value))); 
+			else if (opt_key.compare("fuzzy_skin_shape") == 0)
+				config.set_key_value(opt_key, new ConfigOptionEnum<FuzzySkinShape>(boost::any_cast<FuzzySkinShape>(value))); 
 			else if (opt_key.compare("gcode_flavor") == 0)
 				config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value))); 
 			else if (opt_key.compare("machine_limits_usage") == 0)
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 36c1960d3..71b1aa9ba 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -410,6 +410,12 @@ void Preview::msw_rescale()
     refresh_print();
 }
 
+void Preview::sys_color_changed()
+{
+    if (m_layers_slider != nullptr)
+        m_layers_slider->sys_color_changed();
+}
+
 void Preview::jump_layers_slider(wxKeyEvent& evt)
 {
     if (m_layers_slider) m_layers_slider->OnChar(evt);
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index 0060bfcff..372d44fa3 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -150,6 +150,7 @@ public:
     void refresh_print();
 
     void msw_rescale();
+    void sys_color_changed();
     void jump_layers_slider(wxKeyEvent& evt);
     void move_layers_slider(wxKeyEvent& evt);
     void edit_layers_slider(wxKeyEvent& evt);
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index 3235d6e9e..dbb200f83 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -108,6 +108,7 @@ public:
 
 //        recalc_font();
 
+#ifndef __WXOSX__
 #if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
         this->Bind(wxEVT_DPI_CHANGED, [this](wxDPIChangedEvent& evt) {
 	            m_scale_factor = (float)evt.GetNewDPI().x / (float)DPI_DEFAULT;
@@ -128,6 +129,7 @@ public:
                 rescale(evt.rect);
             });
 #endif // wxVERSION_EQUAL_OR_GREATER_THAN
+#endif // no __WXOSX__
 
         this->Bind(wxEVT_MOVE_START, [this](wxMoveEvent& event)
         {
diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp
index 07b96755d..1e581e70b 100644
--- a/src/slic3r/GUI/OG_CustomCtrl.cpp
+++ b/src/slic3r/GUI/OG_CustomCtrl.cpp
@@ -360,6 +360,9 @@ void OG_CustomCtrl::correct_widgets_position(wxSizer* widget, const Line& line,
 
 void OG_CustomCtrl::msw_rescale()
 {
+#ifdef __WXOSX__
+    return;
+#endif
     m_font      = wxGetApp().normal_font();
     m_em_unit   = em_unit(m_parent);
     m_v_gap     = lround(1.0 * m_em_unit);
@@ -381,7 +384,6 @@ void OG_CustomCtrl::msw_rescale()
 
 void OG_CustomCtrl::sys_color_changed()
 {
-    msw_rescale();
 }
 
 OG_CustomCtrl::CtrlLine::CtrlLine(  wxCoord         height,
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 10774abe3..ced780adb 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -870,6 +870,12 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 		else if (opt_key == "ironing_type") {
 			ret = static_cast<int>(config.option<ConfigOptionEnum<IroningType>>(opt_key)->value);
 		}
+        else if (opt_key == "fuzzy_skin_perimeter_mode") {
+            ret = static_cast<int>(config.option<ConfigOptionEnum<FuzzySkinPerimeterMode>>(opt_key)->value);
+        }
+        else if (opt_key == "fuzzy_skin_shape") {
+            ret = static_cast<int>(config.option<ConfigOptionEnum<FuzzySkinShape>>(opt_key)->value);
+        }
 		else if (opt_key == "gcode_flavor") {
 			ret = static_cast<int>(config.option<ConfigOptionEnum<GCodeFlavor>>(opt_key)->value);
 		}
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 41e9c8ff5..f7f56f570 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -6209,6 +6209,7 @@ void Plater::msw_rescale()
 
 void Plater::sys_color_changed()
 {
+    p->preview->sys_color_changed();
     p->sidebar->sys_color_changed();
 
     // msw_rescale_menu updates just icons, so use it
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index aec2f8754..974b709f7 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1434,6 +1434,18 @@ void TabPrint::build()
         optgroup->append_single_option_line("seam_position", category_path + "seam-position");
         optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first");
 
+        optgroup = page->new_optgroup(L("Fuzzy skin (experimental)"));
+        Option option = optgroup->get_option("fuzzy_skin_perimeter_mode");
+        option.opt.width = 30;
+        optgroup->append_single_option_line(option);
+#if 0
+        option = optgroup->get_option("fuzzy_skin_shape");
+        option.opt.width = 30;
+        optgroup->append_single_option_line(option);
+#endif
+        optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_thickness"));
+        optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_point_dist"));
+
     page = add_options_page(L("Infill"), "infill");
         category_path = "infill_42#";
         optgroup = page->new_optgroup(L("Infill"));
@@ -1597,7 +1609,7 @@ void TabPrint::build()
         optgroup = page->new_optgroup(L("Output file"));
         optgroup->append_single_option_line("gcode_comments");
         optgroup->append_single_option_line("gcode_label_objects");
-        Option option = optgroup->get_option("output_filename_format");
+        option = optgroup->get_option("output_filename_format");
         option.opt.full_width = true;
         optgroup->append_single_option_line(option);