From 1b3b4c5694815ebb9a5db754b2cecdd1592d5f4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= <hejl.lukas@gmail.com>
Date: Mon, 22 Mar 2021 10:30:49 +0100
Subject: [PATCH 01/12] Added missing include (GCC 9.3)

---
 src/slic3r/GUI/GCodeViewer.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 17e3ac68c..ed887d062 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -25,6 +25,7 @@
 #include <boost/algorithm/string/split.hpp>
 #endif // ENABLE_GCODE_WINDOW
 #include <boost/nowide/cstdio.hpp>
+#include <boost/nowide/fstream.hpp>
 #include <wx/progdlg.h>
 #include <wx/numformatter.h>
 

From f5d3866847b6a292709a0c6fba9d05b09fc4eaa4 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 22 Mar 2021 11:40:46 +0100
Subject: [PATCH 02/12] Follow-up
 https://github.com/prusa3d/PrusaSlicer/commit/995512f2807d1e026533c814f6fabf655a7610ab
 DoubleSlider improvements: Fixed a case when wipe tower is used to the end of
 print and there is one layer which is not marked in layers_times statistics

---
 src/slic3r/GUI/DoubleSlider.cpp | 9 ++++++++-
 src/slic3r/GUI/DoubleSlider.hpp | 2 +-
 src/slic3r/GUI/GUI_Preview.cpp  | 6 ++++--
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index de7a944f2..26a4f75a1 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -387,7 +387,7 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z)
     Update();
 }
 
-void Control::SetLayersTimes(const std::vector<float>& layers_times)
+void Control::SetLayersTimes(const std::vector<float>& layers_times, float total_time)
 { 
     m_layers_times.clear();
     if (layers_times.empty())
@@ -405,11 +405,18 @@ void Control::SetLayersTimes(const std::vector<float>& layers_times)
         m_layers_values = m_values;
         sort(m_layers_values.begin(), m_layers_values.end());
         m_layers_values.erase(unique(m_layers_values.begin(), m_layers_values.end()), m_layers_values.end());
+
+        // When whipe tower is used to the end of print, there is one layer which is not marked in layers_times
+        // So, add this value from the total print time value
+        if (m_layers_values.size() != m_layers_times.size())
+            for (size_t i = m_layers_times.size(); i < m_layers_values.size(); i++)
+                m_layers_times.push_back(total_time);
     }
 }
 
 void Control::SetLayersTimes(const std::vector<double>& layers_times)
 { 
+    m_is_smart_wipe_tower = false;
     m_layers_times = layers_times;
     for (size_t i = 1; i < m_layers_times.size(); i++)
         m_layers_times[i] += m_layers_times[i - 1];
diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp
index de50bb0a2..24199b7ff 100644
--- a/src/slic3r/GUI/DoubleSlider.hpp
+++ b/src/slic3r/GUI/DoubleSlider.hpp
@@ -223,7 +223,7 @@ public:
 
     Info    GetTicksValues() const;
     void    SetTicksValues(const Info &custom_gcode_per_print_z);
-    void    SetLayersTimes(const std::vector<float>& layers_times);
+    void    SetLayersTimes(const std::vector<float>& layers_times, float total_time);
     void    SetLayersTimes(const std::vector<double>& layers_times);
 
     void    SetDrawMode(bool is_sla_print, bool is_sequential_print);
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 0042c0702..5e2f8e8ed 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -639,8 +639,10 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
     m_layers_slider->SetDrawMode(sla_print_technology, sequential_print);
     if (sla_print_technology)
         m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times);
-    else
-        m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times);
+    else {
+        auto print_mode_stat = m_gcode_result->time_statistics.modes.front();
+        m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time);
+    }
 
     // Suggest the auto color change, if model looks like sign
     if (m_layers_slider->IsNewPrint())

From 1c2d26457092f0d0e92bf0d40e6c7796505b6eb0 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 22 Mar 2021 15:46:02 +0100
Subject: [PATCH 03/12] Fix for
 https://github.com/prusa3d/PrusaSlicer/commit/f5d3866847b6a292709a0c6fba9d05b09fc4eaa4

---
 src/slic3r/GUI/DoubleSlider.cpp | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 26a4f75a1..87eb38925 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -659,7 +659,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc)
     }
 }
 
-static std::string short_and_splitted_time(const std::string& time)
+static wxString short_and_splitted_time(const std::string& time)
 {
     // Parse the dhms time format.
     int days = 0;
@@ -697,7 +697,7 @@ static std::string short_and_splitted_time(const std::string& time)
     }
     else
         ::sprintf(buffer, "%ds", seconds);
-    return buffer;
+    return wxString::FromUTF8(buffer);
 }
 
 wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const
@@ -719,9 +719,13 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
     auto get_layer_number = [this](int value) {
         double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max<int>(value - 1, 0) : value];
         auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon());
-        if (it == m_layers_values.end())
-            return -1;
-        return int(it - m_layers_values.begin());
+        if (it == m_layers_values.end()) {
+            it = std::lower_bound(m_values.begin(), m_values.end(), layer_print_z - epsilon());
+            if (it == m_values.end())
+                return size_t(-1);
+            return m_layers_values.size();
+        }
+        return size_t(it - m_layers_values.begin());
     };
 
 #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
@@ -737,10 +741,10 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
     else {
         if (label_type == ltEstimatedTime) {
             if (m_is_smart_wipe_tower) {
-                int layer_number = get_layer_number(value);
-                return layer_number < 0 ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number]));
+                size_t layer_number = get_layer_number(value);
+                return layer_number == size_t(-1) ? wxEmptyString : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number]));
             }
-            return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
+            return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : wxEmptyString;
         }
         wxString str = m_values.empty() ?
             wxString::Format("%.*f", 2, m_label_koef * value) :
@@ -748,7 +752,7 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
         if (label_type == ltHeight)
             return str;
         if (label_type == ltHeightWithLayer) {
-            size_t layer_number = m_is_smart_wipe_tower ? (size_t)get_layer_number(value) : (m_values.empty() ? value : value + 1);
+            size_t layer_number = m_is_smart_wipe_tower ? get_layer_number(value) : (m_values.empty() ? value : value + 1);
             return format_wxstr("%1%\n(%2%)", str, layer_number);
         }
     }

From 00295919bf5323262fbc9118c9b9724a0e5129e8 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 23 Mar 2021 09:59:08 +0100
Subject: [PATCH 04/12] Fixes of MutablePolygon

---
 src/libslic3r/MutablePolygon.cpp | 47 ++++++++++++++++++++++++++------
 src/libslic3r/MutablePolygon.hpp |  8 +++---
 2 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/src/libslic3r/MutablePolygon.cpp b/src/libslic3r/MutablePolygon.cpp
index 104b4c6ee..54ce10b49 100644
--- a/src/libslic3r/MutablePolygon.cpp
+++ b/src/libslic3r/MutablePolygon.cpp
@@ -122,7 +122,7 @@ static bool clip_narrow_corner(
         } else {
             assert(backward == Free);
             p02 = it0.prev()->cast<int64_t>();
-            if (cross2(p0 - p2, p02 - p2) > 0)
+            if (cross2(p02 - p2, p0 - p2) > 0)
                 backward = Blocked;
             else {
                 // New clipping edge lenght.
@@ -141,14 +141,37 @@ static bool clip_narrow_corner(
         }
     }
 
+    assert(dist2_current <= shortcut_length2);
+    assert(polygon.size() >= 2);
+    assert(polygon.size() == 2 || forward  == Blocked || forward  == Far);
+    assert(polygon.size() == 2 || backward == Blocked || backward == Far);
+
     if (polygon.size() <= 3) {
         // A hole degenerated to an empty polygon, or a tiny triangle remained.
-        assert(polygon.size() < 3 || (forward == Blocked && backward == Blocked) || (forward == Far && backward == Far));
-        if (polygon.size() < 3 || forward == Far) {
-            assert(polygon.size() < 3 || dist2_current <= shortcut_length2);
+        bool blocked = forward == Blocked || backward == Blocked;
+        assert(polygon.size() < 3 || 
+            // Remaining triangle is CCW oriented. Both sides must be "blocked", but the other side may have not been
+            // updated after the the p02 / p22 became united into a single point.
+            blocked ||
+            // Remaining triangle is concave, however both of its arms are long.
+            (forward == Far && backward == Far));
+#ifndef _NDEBUG
+        if (polygon.size() == 3) {
+            // Verify that the remaining triangle is CCW or CW.
+            p02 = it0.prev()->cast<int64_t>();
+            p22 = it2.next()->cast<int64_t>();
+            assert(p02 == p22);
+            auto orient1 = cross2(p02 - p2, p0 - p2);
+            auto orient2 = cross2(p2 - p0, p22 - p0);
+            assert(orient1 > 0 == blocked);
+            assert(orient2 > 0 == blocked);
+        }
+#endif // _NDEBUG
+        if (polygon.size() < 3 || (forward == Far && backward == Far)) {
             polygon.clear();
         } else {
             // The remaining triangle is CCW oriented, keep it.
+            assert(forward == Blocked || backward == Blocked);
         }
         return true;
     }
@@ -168,7 +191,7 @@ static bool clip_narrow_corner(
     } else if (forward == Blocked || backward == Blocked) {
         // One side is far, the other blocked.
         assert(forward == Far || backward == Far);
-        if (backward == Far) {
+        if (forward == Far) {
             // Sort, so we will clip the 1st edge.
             std::swap(p0,  p2);
             std::swap(p02, p22);
@@ -177,6 +200,10 @@ static bool clip_narrow_corner(
         // Circle intersects a line at two points, however because |p2 - p0| < shortcut_length,
         // only the second intersection is valid. Because |p2 - p02| > shortcut_length, such
         // intersection should always be found on (p0, p02).
+#ifndef NDEBUG
+        auto dfar2 = (p02 - p2).squaredNorm();
+        assert(dfar2 >= shortcut_length2);
+#endif // NDEBUG
         const Vec2d     v = (p02 - p0).cast<double>();
         const Vec2d     d = (p0 - p2).cast<double>();
         const double    a = v.squaredNorm();
@@ -273,15 +300,17 @@ void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled)
                             Point           pt_new = p1 + (t * v2d).cast<coord_t>();
 #ifndef NDEBUG
                             double d2new = (pt_new - (swap ? p2 : p0)).cast<double>().squaredNorm();
-                            assert(std::abs(d2new - clip_dist_scaled2) < sqr(10. * SCALED_EPSILON));
+                            assert(std::abs(d2new - clip_dist_scaled2) < 1e-5 * clip_dist_scaled2);
 #endif // NDEBUG
                             it2.insert(pt_new);
                         } else {
                             // Cut the corner with a line perpendicular to the bisector.
                             double t  = sqrt(0.25 * clip_dist_scaled2 / d2);
-                            assert(t > 0. && t < 1.);
-                            Point  p0 = p1 + (v1d * t).cast<coord_t>();
-                            Point  p2 = p1 + (v2d * (t * lv2 / lv1)).cast<coord_t>();
+                            double t2 = t * lv1 / lv2;
+                            assert(t  > 0. && t  < 1.);
+                            assert(t2 > 0. && t2 < 1.);
+                            Point  p0 = p1 + (v1d * t ).cast<coord_t>();
+                            Point  p2 = p1 + (v2d * t2).cast<coord_t>();
                             if (swap)
                                 std::swap(p0, p2);
                             it2.insert(p2).insert(p0);
diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp
index a601f19e3..ef2b61221 100644
--- a/src/libslic3r/MutablePolygon.hpp
+++ b/src/libslic3r/MutablePolygon.hpp
@@ -85,7 +85,7 @@ public:
         }
 
         void advance_front() {
-            assert(!this->empty());
+            assert(! this->empty());
             if (m_begin == m_end)
                 this->make_empty();
             else
@@ -93,7 +93,7 @@ public:
         }
 
         void retract_back() {
-            assert(!this->empty());
+            assert(! this->empty());
             if (m_begin == m_end)
                 this->make_empty();
             else
@@ -101,13 +101,13 @@ public:
         }
 
         MutablePolygon::iterator remove_front(MutablePolygon::iterator it) {
-            if (m_begin == it)
+            if (! this->empty() && m_begin == it)
                 this->advance_front();
             return it.remove();
         }
 
         MutablePolygon::iterator remove_back(MutablePolygon::iterator it) {
-            if (m_end == it)
+            if (! this->empty() && m_end == it)
                 this->retract_back();
             return it.remove();
         }

From af9c7c967f08fcf22ffa628c324c90eda65230f1 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 23 Mar 2021 11:06:31 +0100
Subject: [PATCH 05/12] Implementing a new switch for the shape of support
 towers: expanded to a grid (the old way) vs. snug (like the upstream Slic3r,
 Cura or Ideamaker).

Snug supports suffered from the degeneracies when merging overhang islands
over a large number of layers when projecting the support towers down.
We borrowed the idea & a bit of code from Cura by simplifying the support
polygons by closing the concave cracks, see the smooth_outward() function
and the MutablePolygon class.

Fixes Support problems with models with hole in the walls. #555
Fixes Support in the Air #740
Fixes [Bug] Supports generated beyond bed edges (X<0 and X>250) and where none are needed. #902
Fixes Unable to remove support material/can't change support "inflation distance" #2708
Fixes FR: support inflation and support conform to boundary #4783
Fixes Support blocker not working on this model #1346
Fixes Unnecessary support material #1993
Fixes support blocker enforcer issue #6240
---
 src/libslic3r/Preset.cpp              |   2 +-
 src/libslic3r/PrintConfig.cpp         |  14 ++
 src/libslic3r/PrintConfig.hpp         |  15 ++
 src/libslic3r/PrintObject.cpp         |   1 +
 src/libslic3r/SupportMaterial.cpp     | 318 ++++++++++++++------------
 src/slic3r/GUI/ConfigManipulation.cpp |   2 +-
 src/slic3r/GUI/Field.cpp              |   2 +
 src/slic3r/GUI/GUI.cpp                |   2 +
 src/slic3r/GUI/GUI_Factories.cpp      |   2 +-
 src/slic3r/GUI/Tab.cpp                |   1 +
 10 files changed, 208 insertions(+), 151 deletions(-)

diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 7dac34342..ecb20a18e 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options()
         "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
         "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
         "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
-        "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
+        "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style",
         "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
         "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", 
         "support_material_contact_distance", "support_material_bottom_contact_distance",
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 692b4c47a..2c6918acf 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2419,6 +2419,20 @@ void PrintConfigDef::init_fff_params()
     def->mode = comAdvanced;
     def->set_default_value(new ConfigOptionFloat(60));
 
+    def = this->add("support_material_style", coEnum);
+    def->label = L("Style");
+    def->category = L("Support material");
+    def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid "
+                     "will create more stable supports, while snug support towers will save material and reduce "
+                     "object scarring.");
+    def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
+    def->enum_values.push_back("grid");
+    def->enum_values.push_back("snug");
+    def->enum_labels.push_back(L("Grid"));
+    def->enum_labels.push_back(L("Snug"));
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
+
     def = this->add("support_material_synchronize_layers", coBool);
     def->label = L("Synchronize with object layers");
     def->category = L("Support material");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index abeb29d4b..10f3b11d5 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -65,6 +65,10 @@ enum SupportMaterialPattern {
     smpRectilinear, smpRectilinearGrid, smpHoneycomb,
 };
 
+enum SupportMaterialStyle {
+    smsGrid, smsSnug,
+};
+
 enum SupportMaterialInterfacePattern {
     smipAuto, smipRectilinear, smipConcentric,
 };
@@ -211,6 +215,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPa
     return keys_map;
 }
 
+template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialStyle>::get_enum_values() {
+    static t_config_enum_values keys_map;
+    if (keys_map.empty()) {
+        keys_map["grid"]    = smsGrid;
+        keys_map["snug"]    = smsSnug;
+    }
+    return keys_map;
+}
+
 template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
@@ -519,6 +532,7 @@ public:
     // Spacing between support material lines (the hatching distance).
     ConfigOptionFloat               support_material_spacing;
     ConfigOptionFloat               support_material_speed;
+    ConfigOptionEnum<SupportMaterialStyle> support_material_style;
     ConfigOptionBool                support_material_synchronize_layers;
     // Overhang angle threshold.
     ConfigOptionInt                 support_material_threshold;
@@ -570,6 +584,7 @@ protected:
         OPT_PTR(support_material_interface_pattern);
         OPT_PTR(support_material_spacing);
         OPT_PTR(support_material_speed);
+        OPT_PTR(support_material_style);
         OPT_PTR(support_material_synchronize_layers);
         OPT_PTR(support_material_xy_spacing);
         OPT_PTR(support_material_threshold);
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index e86eb2c9c..1989b1884 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -588,6 +588,7 @@ bool PrintObject::invalidate_state_by_config_options(
             || opt_key == "support_material_interface_extruder"
             || opt_key == "support_material_interface_spacing"
             || opt_key == "support_material_pattern"
+            || opt_key == "support_material_style"
             || opt_key == "support_material_xy_spacing"
             || opt_key == "support_material_spacing"
             || opt_key == "support_material_synchronize_layers"
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index c01da8f91..56ae04bce 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -6,6 +6,7 @@
 #include "Fill/FillBase.hpp"
 #include "Geometry.hpp"
 #include "Point.hpp"
+#include "MutablePolygon.hpp"
 
 #include <cmath>
 #include <memory>
@@ -667,17 +668,19 @@ Polygons collect_slices_outer(const Layer &layer)
 
 struct SupportGridParams {
     SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) :
+        style(object_config.support_material_style.value),
         grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()),
         support_angle(Geometry::deg2rad(object_config.support_material_angle.value)),
         extrusion_width(support_material_flow.spacing()),
         expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)),
         expansion_to_propagate(-3) {}
 
-    double      grid_resolution;
-    double      support_angle;
-    double      extrusion_width;
-    coord_t     expansion_to_slice;
-    coord_t     expansion_to_propagate;
+    SupportMaterialStyle    style;
+    double                  grid_resolution;
+    double                  support_angle;
+    double                  extrusion_width;
+    coord_t                 expansion_to_slice;
+    coord_t                 expansion_to_propagate;
 };
 
 class SupportGridPattern
@@ -689,75 +692,88 @@ public:
         // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
         const Polygons          *trimming_polygons,
         const SupportGridParams &params) :
+        m_style(params.style),
         m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons),
         m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle)
     {
-        if (m_support_angle != 0.) {
-            // Create a copy of the rotated contours.
-            m_support_polygons_rotated  = *support_polygons;
-            m_trimming_polygons_rotated = *trimming_polygons;
-            m_support_polygons  = &m_support_polygons_rotated;
-            m_trimming_polygons = &m_trimming_polygons_rotated;
-            polygons_rotate(m_support_polygons_rotated, - params.support_angle);
-            polygons_rotate(m_trimming_polygons_rotated, - params.support_angle);
-        }
-
-        // Resolution of the sparse support grid.
-        coord_t grid_resolution = coord_t(scale_(m_support_spacing));
-        BoundingBox bbox = get_extents(*m_support_polygons);
-        bbox.offset(20);
-        // Align the bounding box with the sparse support grid.
-        bbox.align_to_grid(grid_resolution);
-
-#ifdef SUPPORT_USE_AGG_RASTERIZER
-        m_bbox       = bbox;
-        // Oversample the grid to avoid leaking of supports through or around the object walls.
-        int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
-        m_pixel_size = scale_(m_support_spacing / oversampling);
-        assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
-        // Add one empty column / row boundaries.
-        m_bbox.offset(m_pixel_size);
-        // Grid size fitting the support polygons plus one pixel boundary around the polygons.
-        Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)),
-                            int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size)));
-        // Overlay macro blocks of (oversampling x oversampling) over the grid.
-        Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, 
-                          (grid_size_raw.y() + oversampling - 1 - 2) / oversampling);
-        // and resize the grid to fit the macro blocks + one pixel boundary.
-        m_grid_size = grid_blocks * oversampling + Vec2i(2, 2);
-        assert(m_grid_size.x() >= grid_size_raw.x());
-        assert(m_grid_size.y() >= grid_size_raw.y());
-        m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons);
-
-        seed_fill_block(m_grid2, m_grid_size,
-            dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size),
-            grid_blocks, oversampling);
-
-#ifdef SLIC3R_DEBUG
+        switch (m_style) {
+        case smsGrid:
         {
-            static int irun;
-            Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4);
-        }
-#endif // SLIC3R_DEBUG
+            // Prepare the grid data, it will be reused when extracting support structures.
+            if (m_support_angle != 0.) {
+                // Create a copy of the rotated contours.
+                m_support_polygons_rotated  = *support_polygons;
+                m_trimming_polygons_rotated = *trimming_polygons;
+                m_support_polygons  = &m_support_polygons_rotated;
+                m_trimming_polygons = &m_trimming_polygons_rotated;
+                polygons_rotate(m_support_polygons_rotated, - params.support_angle);
+                polygons_rotate(m_trimming_polygons_rotated, - params.support_angle);
+            }
 
-#else // SUPPORT_USE_AGG_RASTERIZER
-        // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
-        m_grid.set_bbox(bbox);
-        m_grid.create(*m_support_polygons, grid_resolution);
-#if 0
-        if (m_grid.has_intersecting_edges()) {
-            // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
-            m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
-            m_support_polygons = &m_support_polygons_rotated;
+            // Resolution of the sparse support grid.
+            coord_t grid_resolution = coord_t(scale_(m_support_spacing));
+            BoundingBox bbox = get_extents(*m_support_polygons);
+            bbox.offset(20);
+            // Align the bounding box with the sparse support grid.
+            bbox.align_to_grid(grid_resolution);
+
+    #ifdef SUPPORT_USE_AGG_RASTERIZER
+            m_bbox       = bbox;
+            // Oversample the grid to avoid leaking of supports through or around the object walls.
+            int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
+            m_pixel_size = scale_(m_support_spacing / oversampling);
+            assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
+            // Add one empty column / row boundaries.
+            m_bbox.offset(m_pixel_size);
+            // Grid size fitting the support polygons plus one pixel boundary around the polygons.
+            Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)),
+                                int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size)));
+            // Overlay macro blocks of (oversampling x oversampling) over the grid.
+            Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, 
+                              (grid_size_raw.y() + oversampling - 1 - 2) / oversampling);
+            // and resize the grid to fit the macro blocks + one pixel boundary.
+            m_grid_size = grid_blocks * oversampling + Vec2i(2, 2);
+            assert(m_grid_size.x() >= grid_size_raw.x());
+            assert(m_grid_size.y() >= grid_size_raw.y());
+            m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons);
+
+            seed_fill_block(m_grid2, m_grid_size,
+                dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size),
+                grid_blocks, oversampling);
+
+    #ifdef SLIC3R_DEBUG
+            {
+                static int irun;
+                Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4);
+            }
+    #endif // SLIC3R_DEBUG
+
+    #else // SUPPORT_USE_AGG_RASTERIZER
+            // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
             m_grid.set_bbox(bbox);
             m_grid.create(*m_support_polygons, grid_resolution);
-//            assert(! m_grid.has_intersecting_edges());
-            printf("SupportGridPattern: fixing polygons with intersection %s\n",
-                m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
+    #if 0
+            if (m_grid.has_intersecting_edges()) {
+                // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
+                m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
+                m_support_polygons = &m_support_polygons_rotated;
+                m_grid.set_bbox(bbox);
+                m_grid.create(*m_support_polygons, grid_resolution);
+    //            assert(! m_grid.has_intersecting_edges());
+                printf("SupportGridPattern: fixing polygons with intersection %s\n",
+                    m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
+            }
+    #endif
+            m_grid.calculate_sdf();
+    #endif // SUPPORT_USE_AGG_RASTERIZER
+            break;
+        }
+
+        case smsSnug:
+        default:
+            // nothing to prepare
+            break;
         }
-#endif
-        m_grid.calculate_sdf();
-#endif // SUPPORT_USE_AGG_RASTERIZER
     }
 
     // Extract polygons from the grid, offsetted by offset_in_grid,
@@ -770,97 +786,102 @@ public:
 #endif
         )
     {
-#ifdef SUPPORT_USE_AGG_RASTERIZER
-        Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes);
-#else // SUPPORT_USE_AGG_RASTERIZER
-        // Generate islands, so each island may be tested for overlap with island_samples.
-        assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
-        Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
-#endif // SUPPORT_USE_AGG_RASTERIZER
+        switch (m_style) {
+        case smsGrid:
+        {
+    #ifdef SUPPORT_USE_AGG_RASTERIZER
+            Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes);
+    #else // SUPPORT_USE_AGG_RASTERIZER
+            // Generate islands, so each island may be tested for overlap with island_samples.
+            assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
+            Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
+    #endif // SUPPORT_USE_AGG_RASTERIZER
 
-        ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
+            ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
 
-        // Extract polygons, which contain some of the island_samples.
-        Polygons out;
-#if 0
-        out = to_polygons(std::move(islands));
-#else
+            // Extract polygons, which contain some of the island_samples.
+            Polygons out;
 
-        // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
-        // polygons if ever these polygons get split into parts by the trimming polygons.
-        // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands.
-        // Trim ti with islands.
-        Points samples = island_samples(
-            offset_in_grid > 0 ? 
-                // Expanding, thus m_support_polygons are all inside islands.
-                union_ex(*m_support_polygons) :
-                // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
-                intersection_ex(*m_support_polygons, to_polygons(islands)));
+            // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+            // polygons if ever these polygons get split into parts by the trimming polygons.
+            // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands.
+            // Trim ti with islands.
+            Points samples = island_samples(
+                offset_in_grid > 0 ? 
+                    // Expanding, thus m_support_polygons are all inside islands.
+                    union_ex(*m_support_polygons) :
+                    // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
+                    intersection_ex(*m_support_polygons, to_polygons(islands)));
 
-        std::vector<std::pair<Point,bool>> samples_inside;
-        for (ExPolygon &island : islands) {
-            BoundingBox bbox = get_extents(island.contour);
-            // Samples are sorted lexicographically.
-            auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1)));
-            auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1)));
-            samples_inside.clear();
-            for (auto it = it_lower; it != it_upper; ++ it)
-                if (bbox.contains(*it))
-                    samples_inside.push_back(std::make_pair(*it, false));
-            if (! samples_inside.empty()) {
-                // For all samples_inside count the boundary crossing.
-                for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) {
-                    Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1];
-                    Points::const_iterator i = contour.points.begin();
-                    Points::const_iterator j = contour.points.end() - 1;
-                    for (; i != contour.points.end(); j = i ++) {
-                        //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
-                        // Does the ray with y == point(1) intersect this line segment?
-                        for (auto &sample_inside : samples_inside) {
-                            if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) {
-                                double x1 = (double)sample_inside.first(0);
-                                double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1));
-                                if (x1 < x2)
-                                    sample_inside.second = !sample_inside.second;
+            std::vector<std::pair<Point,bool>> samples_inside;
+            for (ExPolygon &island : islands) {
+                BoundingBox bbox = get_extents(island.contour);
+                // Samples are sorted lexicographically.
+                auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1)));
+                auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1)));
+                samples_inside.clear();
+                for (auto it = it_lower; it != it_upper; ++ it)
+                    if (bbox.contains(*it))
+                        samples_inside.push_back(std::make_pair(*it, false));
+                if (! samples_inside.empty()) {
+                    // For all samples_inside count the boundary crossing.
+                    for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) {
+                        Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1];
+                        Points::const_iterator i = contour.points.begin();
+                        Points::const_iterator j = contour.points.end() - 1;
+                        for (; i != contour.points.end(); j = i ++) {
+                            //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
+                            // Does the ray with y == point(1) intersect this line segment?
+                            for (auto &sample_inside : samples_inside) {
+                                if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) {
+                                    double x1 = (double)sample_inside.first(0);
+                                    double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1));
+                                    if (x1 < x2)
+                                        sample_inside.second = !sample_inside.second;
+                                }
                             }
                         }
                     }
+                    // If any of the sample is inside this island, add this island to the output.
+                    for (auto &sample_inside : samples_inside)
+                        if (sample_inside.second) {
+                            polygons_append(out, std::move(island));
+                            island.clear();
+                            break;
+                        }
                 }
-                // If any of the sample is inside this island, add this island to the output.
-                for (auto &sample_inside : samples_inside)
-                    if (sample_inside.second) {
-                        polygons_append(out, std::move(island));
-                        island.clear();
-                        break;
-                    }
             }
+
+    #ifdef SLIC3R_DEBUG
+            BoundingBox bbox = get_extents(*m_trimming_polygons);
+            if (! islands.empty())
+                bbox.merge(get_extents(islands));
+            if (!out.empty())
+                bbox.merge(get_extents(out));
+            if (!support_polygons_simplified.empty())
+                bbox.merge(get_extents(support_polygons_simplified));
+            SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox);
+            svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
+            svg.draw(islands, "red", 0.5f);
+            svg.draw(union_ex(out), "green", 0.5f);
+            svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
+            svg.draw_outline(islands, "red", "red", scale_(0.05));
+            svg.draw_outline(union_ex(out), "green", "green", scale_(0.05));
+            svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05));
+            for (const Point &pt : samples)
+                svg.draw(pt, "black", coord_t(scale_(0.15)));
+            svg.Close();
+    #endif /* SLIC3R_DEBUG */
+
+            if (m_support_angle != 0.)
+                polygons_rotate(out, m_support_angle);
+            return out;
+        }
+        case smsSnug:
+            // Just close the gaps.
+            float thr = scaled<float>(0.5);
+            return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
         }
-#endif
-
-#ifdef SLIC3R_DEBUG
-        BoundingBox bbox = get_extents(*m_trimming_polygons);
-        if (! islands.empty())
-            bbox.merge(get_extents(islands));
-        if (!out.empty())
-            bbox.merge(get_extents(out));
-        if (!support_polygons_simplified.empty())
-            bbox.merge(get_extents(support_polygons_simplified));
-        SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox);
-        svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
-        svg.draw(islands, "red", 0.5f);
-        svg.draw(union_ex(out), "green", 0.5f);
-        svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
-        svg.draw_outline(islands, "red", "red", scale_(0.05));
-        svg.draw_outline(union_ex(out), "green", "green", scale_(0.05));
-        svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05));
-        for (const Point &pt : samples)
-            svg.draw(pt, "black", coord_t(scale_(0.15)));
-        svg.Close();
-#endif /* SLIC3R_DEBUG */
-
-        if (m_support_angle != 0.)
-            polygons_rotate(out, m_support_angle);
-        return out;
     }
 
 #if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
@@ -1096,6 +1117,7 @@ private:
         return pts;
     } 
 
+    SupportMaterialStyle    m_style;
     const Polygons         *m_support_polygons;
     const Polygons         *m_trimming_polygons;
     Polygons                m_support_polygons_rotated;
@@ -1525,7 +1547,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
                             ClipperLib::jtRound,
                             // round mitter limit
                             scale_(0.05)),
-                        slices_margin_cached);
+                        slices_margin.polygons);
                 }
 #else
                 diff_polygons = diff(diff_polygons, slices_margin.polygons);
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 6e0f13528..9334eea47 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -278,7 +278,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
     bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto");
     bool have_support_interface = config->opt_int("support_material_interface_layers") > 0;
     bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0;
-    for (auto el : { "support_material_pattern", "support_material_with_sheath",
+    for (auto el : { "support_material_style", "support_material_pattern", "support_material_with_sheath",
                     "support_material_spacing", "support_material_angle", 
                     "support_material_interface_pattern", "support_material_interface_layers",
                     "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index cbbe7f381..8e3fff06d 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -1224,6 +1224,8 @@ boost::any& Choice::get_value()
 			m_value = static_cast<SupportMaterialPattern>(ret_enum);
         else if (m_opt_id.compare("support_material_interface_pattern") == 0)
             m_value = static_cast<SupportMaterialInterfacePattern>(ret_enum);
+        else if (m_opt_id.compare("support_material_style") == 0)
+            m_value = static_cast<SupportMaterialStyle>(ret_enum);
 		else if (m_opt_id.compare("seam_position") == 0)
 			m_value = static_cast<SeamPosition>(ret_enum);
 		else if (m_opt_id.compare("host_type") == 0)
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 61ec4d233..1d3a85dc0 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -198,6 +198,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 				config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
 			else if (opt_key.compare("support_material_interface_pattern") == 0)
 				config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialInterfacePattern>(boost::any_cast<SupportMaterialInterfacePattern>(value)));
+			else if (opt_key.compare("support_material_style") == 0)
+				config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialStyle>(boost::any_cast<SupportMaterialStyle>(value)));
 			else if (opt_key.compare("seam_position") == 0)
 				config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
 			else if (opt_key.compare("host_type") == 0)
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index d8518a6c8..5ca8f8e2a 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -50,7 +50,7 @@ static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF =
     { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } },
     { L("Infill")               , { "fill_density", "fill_pattern" } },
     { L("Support material")     , { "support_material", "support_material_auto", "support_material_threshold",
-                                    "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only",
+                                    "support_material_pattern", "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only",
                                     "support_material_spacing" } },
     { L("Wipe options")         , { "wipe_into_infill", "wipe_into_objects" } }
 };
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index c68de4984..de2e84515 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1506,6 +1506,7 @@ void TabPrint::build()
         optgroup->append_single_option_line("raft_expansion");
 
         optgroup = page->new_optgroup(L("Options for support material and raft"));
+        optgroup->append_single_option_line("support_material_style", category_path + "style");
         optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance");
         optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance");
         optgroup->append_single_option_line("support_material_pattern", category_path + "pattern");

From cafa5b26a8b5900d87462237ee9401f9f823aafc Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 23 Mar 2021 00:19:45 +0100
Subject: [PATCH 06/12] Compilation fixes

---
 src/libslic3r/SupportMaterial.cpp | 2 ++
 src/slic3r/GUI/DoubleSlider.cpp   | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 56ae04bce..bfd879e52 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -882,6 +882,8 @@ public:
             float thr = scaled<float>(0.5);
             return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
         }
+        assert(false);
+        return Polygons();
     }
 
 #if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 87eb38925..80b74c437 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -742,9 +742,9 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
         if (label_type == ltEstimatedTime) {
             if (m_is_smart_wipe_tower) {
                 size_t layer_number = get_layer_number(value);
-                return layer_number == size_t(-1) ? wxEmptyString : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number]));
+                return layer_number == size_t(-1) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number]));
             }
-            return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : wxEmptyString;
+            return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
         }
         wxString str = m_values.empty() ?
             wxString::Format("%.*f", 2, m_label_koef * value) :

From a0feb0f652ecc1062a7d239bf9e4b7b6d998b3cb Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 23 Mar 2021 12:46:04 +0100
Subject: [PATCH 07/12] Preview and G-code viewer - Fixed synchronization
 between markers for pause print, color changes, custom g-code, retractions,
 deretractions and current line shown in g-code window

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 2 +-
 src/slic3r/GUI/GCodeViewer.cpp         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index bd4e7a3ad..a3877845f 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -2572,7 +2572,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
 {
     MoveVertex vertex = {
 #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
-        m_line_id,
+        (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id,
 #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
         type,
         m_extrusion_role,
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index ed887d062..66bdafb56 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -3151,7 +3151,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         const Path& path = buffer->paths[path_id];
 #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
         const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
-        if (m_sequential_view.current.last <= sub_path.first.s_id || sub_path.last.s_id <= m_sequential_view.current.first)
+        if (m_sequential_view.current.last < sub_path.first.s_id || sub_path.last.s_id < m_sequential_view.current.first)
             continue;
 
         Color color;

From 10c3e829178435203286a5843077af6f0467a5fb Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 23 Mar 2021 13:50:41 +0100
Subject: [PATCH 08/12] Updated version number, alpha stores configs in alpha
 directory.

---
 src/slic3r/GUI/GUI_App.cpp | 4 ++--
 version.inc                | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index ecc5f46a7..b22cd6009 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -690,8 +690,8 @@ bool GUI_App::init_opengl()
 void GUI_App::init_app_config()
 {
 	// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
-    SetAppName(SLIC3R_APP_KEY);
-//	SetAppName(SLIC3R_APP_KEY "-beta");
+//    SetAppName(SLIC3R_APP_KEY);
+	SetAppName(SLIC3R_APP_KEY "-beta");
 //	SetAppDisplayName(SLIC3R_APP_NAME);
 
 	// Set the Slic3r data directory at the Slic3r XS module.
diff --git a/version.inc b/version.inc
index 1a848cd6f..4e7aab235 100644
--- a/version.inc
+++ b/version.inc
@@ -3,7 +3,7 @@
 
 set(SLIC3R_APP_NAME "PrusaSlicer")
 set(SLIC3R_APP_KEY "PrusaSlicer")
-set(SLIC3R_VERSION "2.3.0")
+set(SLIC3R_VERSION "2.4.0-alpha0")
 set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
-set(SLIC3R_RC_VERSION "2,3,0,0")
-set(SLIC3R_RC_VERSION_DOTS "2.3.0.0")
+set(SLIC3R_RC_VERSION "2,4,0,0")
+set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")

From 991fa67fd1e71dad6d2d5d10fc6e98ba714f8a9f Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 24 Mar 2021 10:45:37 +0100
Subject: [PATCH 09/12] OptionsSearcher improvements: Fixed a key for option()
 and groups_and_categories. It contains "preset_type;opt_key" now. This key
 helps to avoid a collisions by using a same options key from different type
 presets. Example: Option "elefant_foot_compensation" is in Print presets and
 SLA_printer presets

---
 src/slic3r/GUI/OptionsGroup.cpp         |  2 +-
 src/slic3r/GUI/OptionsGroup.hpp         |  8 +++--
 src/slic3r/GUI/Plater.cpp               |  2 +-
 src/slic3r/GUI/Search.cpp               | 45 ++++++++++++++-----------
 src/slic3r/GUI/Search.hpp               | 15 +++++----
 src/slic3r/GUI/Tab.cpp                  |  6 ++--
 src/slic3r/GUI/UnsavedChangesDialog.cpp | 22 +++++++-----
 7 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index c81f4c644..f70d678d2 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -535,7 +535,7 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index
 	m_opt_map.emplace(opt_id, pair);
 
 	if (m_use_custom_ctrl) // fill group and category values just for options from Settings Tab
-	    wxGetApp().sidebar().get_searcher().add_key(opt_id, title, this->config_category());
+	    wxGetApp().sidebar().get_searcher().add_key(opt_id, static_cast<Preset::Type>(this->config_type()), title, this->config_category());
 
 	return Option(*m_config->def()->get(opt_key), opt_id);
 }
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index f19a3e033..11a52d648 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -224,10 +224,11 @@ public:
 	ConfigOptionsGroup(	wxWindow* parent) :
 		OptionsGroup(parent, wxEmptyString, true, nullptr) {}
 
-	const std::string& config_category() const throw() { return m_config_category; }
+	const wxString& config_category() const throw() { return m_config_category; }
+	int config_type() const throw() { return m_config_type; }
 	const t_opt_map&   opt_map() const throw() { return m_opt_map; }
 
-	void 		set_config_category(const std::string &category) { this->m_config_category = category; }
+	void 		set_config_category_and_type(const wxString &category, int type) { this->m_config_category = category; this->m_config_type = type; }
     void        set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; }
 	Option		get_option(const std::string& opt_key, int opt_index = -1);
 	Line		create_single_option_line(const std::string& title, const wxString& path = wxEmptyString, int idx = -1) /*const*/{
@@ -274,7 +275,8 @@ private:
     // If the config is modelconfig, then ModelConfig::touch() has to be called after value change.
     ModelConfig*				m_modelconfig { nullptr };
 	t_opt_map					m_opt_map;
-    std::string             	m_config_category;
+    wxString                    m_config_category;
+    int                         m_config_type;
 
     // Change an option on m_config, possibly call ModelConfig::touch().
 	void 	change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index d7224cedc..f8ec2584c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -992,7 +992,7 @@ void Sidebar::search()
 void Sidebar::jump_to_option(size_t selected)
 {
     const Search::Option& opt = p->searcher.get_option(selected);
-    wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category));
+    wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key(), boost::nowide::narrow(opt.category));
 
     // Switch to the Settings NotePad
 //    wxGetApp().mainframe->select_tab();
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 03aa11eb6..9ff4e82a1 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -63,24 +63,29 @@ void change_opt_key(std::string& opt_key, DynamicPrintConfig* config, int& cnt)
         opt_key += "#" + std::to_string(0);
 }
 
+static std::string get_key(const std::string& opt_key, Preset::Type type)
+{
+    return std::to_string(int(type)) + ";" + opt_key;
+}
+
 void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
 {
-    auto emplace = [this, type](const std::string opt_key, const wxString& label)
+    auto emplace = [this, type](const std::string key, const wxString& label)
     {
-        const GroupAndCategory& gc = groups_and_categories[opt_key];
+        const GroupAndCategory& gc = groups_and_categories[key];
         if (gc.group.IsEmpty() || gc.category.IsEmpty())
             return;
 
         wxString suffix;
         wxString suffix_local;
         if (gc.category == "Machine limits") {
-            suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal");
+            suffix = key.back()=='1' ? L("Stealth") : L("Normal");
             suffix_local = " " + _(suffix);
             suffix = " " + suffix;
         }
 
         if (!label.IsEmpty())
-            options.emplace_back(Option{ boost::nowide::widen(opt_key), type,
+            options.emplace_back(Option{ boost::nowide::widen(key), type,
                                         (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(),
                                         gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
                                         gc.category.ToStdWstring(), GUI::Tab::translate_category(gc.category, type).ToStdWstring() });
@@ -108,12 +113,13 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty
 
         wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
 
+        std::string key = get_key(opt_key, type);
         if (cnt == 0)
-            emplace(opt_key, label);
+            emplace(key, label);
         else
             for (int i = 0; i < cnt; ++i)
                 // ! It's very important to use "#". opt_key#n is a real option key used in GroupAndCategory
-                emplace(opt_key + "#" + std::to_string(i), label); 
+                emplace(key + "#" + std::to_string(i), label);
     }
 }
 
@@ -237,10 +243,10 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
         int score2;
         matches.clear();
         fuzzy_match(wsearch, label, score, matches);
-        if (fuzzy_match(wsearch, opt.opt_key, score2, matches2) && score2 > score) {
+        if (fuzzy_match(wsearch, opt.key, score2, matches2) && score2 > score) {
         	for (fts::pos_type &pos : matches2)
         		pos += label.size() + 1;
-        	label += L"(" + opt.opt_key + L")";
+        	label += L"(" + opt.key + L")";
         	append(matches, matches2);
         	score = score2;
         }
@@ -318,9 +324,9 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const
     return options[found[pos_in_filter].option_idx];
 }
 
-const Option& OptionsSearcher::get_option(const std::string& opt_key) const
+const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Type type) const
 {
-    auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) }));
+    auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(get_key(opt_key, type)) }));
     assert(it != options.end());
 
     return options[it - options.begin()];
@@ -342,7 +348,7 @@ static Option create_option(const std::string& opt_key, const wxString& label, P
         category = wxString::Format("%s %d", "Extruder", atoi(opt_idx.c_str()) + 1);
     }
 
-    return Option{ boost::nowide::widen(opt_key), type,
+    return Option{ boost::nowide::widen(get_key(opt_key, type)), type,
                 (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(),
                 gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
                 gc.category.ToStdWstring(), GUI::Tab::translate_category(category, type).ToStdWstring() };
@@ -350,15 +356,16 @@ static Option create_option(const std::string& opt_key, const wxString& label, P
 
 Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const
 {
-    auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) }));
-    if(it->opt_key == boost::nowide::widen(opt_key))
+    std::string key = get_key(opt_key, type);
+    auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(key) }));
+    if(it->key == boost::nowide::widen(key))
         return options[it - options.begin()];
-    if (groups_and_categories.find(opt_key) == groups_and_categories.end()) {
-        size_t pos = opt_key.find('#');
+    if (groups_and_categories.find(key) == groups_and_categories.end()) {
+        size_t pos = key.find('#');
         if (pos == std::string::npos)
             return options[it - options.begin()];
 
-        std::string zero_opt_key = opt_key.substr(0, pos + 1) + "0";
+        std::string zero_opt_key = key.substr(0, pos + 1) + "0";
 
         if(groups_and_categories.find(zero_opt_key) == groups_and_categories.end())
             return options[it - options.begin()];
@@ -366,16 +373,16 @@ Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& l
         return create_option(opt_key, label, type, groups_and_categories.at(zero_opt_key));
     }
 
-    const GroupAndCategory& gc = groups_and_categories.at(opt_key);
+    const GroupAndCategory& gc = groups_and_categories.at(key);
     if (gc.group.IsEmpty() || gc.category.IsEmpty())
         return options[it - options.begin()];
 
     return create_option(opt_key, label, type, gc);
 }
 
-void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category)
+void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category)
 {
-    groups_and_categories[opt_key] = GroupAndCategory{group, category};
+    groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category};
 }
 
 
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 1f2909564..30b969112 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -38,11 +38,11 @@ struct GroupAndCategory {
 
 struct Option {
 //    bool operator<(const Option& other) const { return other.label > this->label; }
-    bool operator<(const Option& other) const { return other.opt_key > this->opt_key; }
+    bool operator<(const Option& other) const { return other.key > this->key; }
 
     // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters,
     // though for some languages (Chinese?) it may not work correctly.
-    std::wstring    opt_key;
+    std::wstring    key;
     Preset::Type    type {Preset::TYPE_INVALID};
     std::wstring    label;
     std::wstring    label_local;
@@ -50,6 +50,8 @@ struct Option {
     std::wstring    group_local;
     std::wstring    category;
     std::wstring    category_local;
+
+    std::string     opt_key() const { return boost::nowide::narrow(key).substr(2); }
 };
 
 struct FoundOption {
@@ -110,13 +112,13 @@ public:
     bool search();
     bool search(const std::string& search, bool force = false);
 
-    void add_key(const std::string& opt_key, const wxString& group, const wxString& category);
+    void add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category);
 
     size_t size() const         { return found_size(); }
 
     const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; }
     const Option& get_option(size_t pos_in_filter) const;
-    const Option& get_option(const std::string& opt_key) const;
+    const Option& get_option(const std::string& opt_key, Preset::Type type) const;
     Option get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const;
 
     const std::vector<FoundOption>& found_options() { return found; }
@@ -125,10 +127,11 @@ public:
 
     void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; }
 
-    void sort_options_by_opt_key() {
+    void sort_options_by_key() {
         std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
-            return o1.opt_key < o2.opt_key; });
+            return o1.key < o2.key; });
     }
+    void sort_options_by_label() { sort_options(); }
 };
 
 
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index de2e84515..74b54478e 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3802,8 +3802,8 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
     {
         Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
         const Search::GroupAndCategory& gc = searcher.get_group_and_category("bed_shape");
-        searcher.add_key("bed_custom_texture", gc.group, gc.category);
-        searcher.add_key("bed_custom_model", gc.group, gc.category);
+        searcher.add_key("bed_custom_texture", m_type, gc.group, gc.category);
+        searcher.add_key("bed_custom_model", m_type, gc.group, gc.category);
     }
 
     return sizer;
@@ -4044,7 +4044,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 {
     //! config_ have to be "right"
     ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(m_parent, title, m_config, true);
-    optgroup->set_config_category(m_title.ToStdString());
     if (noncommon_label_width >= 0)
         optgroup->label_width = noncommon_label_width;
 
@@ -4053,6 +4052,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 #else
     auto tab = parent()->GetParent();// GetParent();
 #endif
+    optgroup->set_config_category_and_type(m_title, static_cast<Tab*>(tab)->type());
     optgroup->m_on_change = [tab](t_config_option_key opt_key, boost::any value) {
         //! This function will be called from OptionGroup.
         //! Using of CallAfter is redundant.
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index 7c0be76a4..0a0a3dc60 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -833,10 +833,10 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
         (*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); });
     };
 
-    const PresetCollection& printers = wxGetApp().preset_bundle->printers;
-    if (dependent_presets && (type == dependent_presets->type() ?
+    const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
+    if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
         dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
-        printers.get_edited_preset().printer_technology() == printers.find_preset(new_selected_preset)->printer_technology()))
+        switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
         add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer"));
     add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false);
     add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
@@ -1185,7 +1185,7 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent
 void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_)
 {
     Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
-    searcher.sort_options_by_opt_key();
+    searcher.sort_options_by_key();
 
     // list of the presets with unsaved changes
     std::vector<PresetCollection*> presets_list;
@@ -1227,8 +1227,8 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
         }
 
         for (const std::string& opt_key : dirty_options) {
-            const Search::Option& option = searcher.get_option(opt_key);
-            if (option.opt_key != boost::nowide::widen(opt_key)) {
+            const Search::Option& option = searcher.get_option(opt_key, type);
+            if (option.opt_key() != opt_key) {
                 // When founded option isn't the correct one.
                 // It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id",
                 // because of they don't exist in searcher
@@ -1239,6 +1239,9 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
                 get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), category_icon_map.at(option.category));
         }
     }
+
+    // Revert sort of searcher back
+    searcher.sort_options_by_label();
 }
 
 void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect)
@@ -1546,7 +1549,7 @@ void DiffPresetDialog::update_presets(Preset::Type type)
 void DiffPresetDialog::update_tree()
 {
     Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
-    searcher.sort_options_by_opt_key();
+    searcher.sort_options_by_key();
 
     m_tree->Clear();
     wxString bottom_info = "";
@@ -1617,7 +1620,7 @@ void DiffPresetDialog::update_tree()
             wxString right_val = get_string_value(opt_key, right_congig);
 
             Search::Option option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type);
-            if (option.opt_key != boost::nowide::widen(opt_key)) {
+            if (option.opt_key() != opt_key) {
                 // temporary solution, just for testing
                 m_tree->Append(opt_key, type, _L("Undef category"), _L("Undef group"), opt_key, left_val, right_val, "question");
                 // When founded option isn't the correct one.
@@ -1642,6 +1645,9 @@ void DiffPresetDialog::update_tree()
         Fit();
         Refresh();
     }
+
+    // Revert sort of searcher back
+    searcher.sort_options_by_label();
 }
 
 void DiffPresetDialog::on_dpi_changed(const wxRect&)

From 4496e2a8ce2371edfa9153e4f879b3b1e94a3b65 Mon Sep 17 00:00:00 2001
From: Oleksandra Yushchenko <yusanka@gmail.com>
Date: Wed, 24 Mar 2021 11:20:57 +0100
Subject: [PATCH 10/12] Follow-up of 908c48ae6a1950eec1cf76840a06ee84e83ea155
 -> Fixed update after switching tab after editing custom g-code in settings
 tabs (#6258)

---
 src/slic3r/GUI/MainFrame.cpp | 21 +++++++--------------
 src/slic3r/GUI/Tab.cpp       | 27 +++++++++------------------
 src/slic3r/GUI/Tab.hpp       |  1 +
 3 files changed, 17 insertions(+), 32 deletions(-)

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 858744229..a5a920f82 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -525,7 +525,13 @@ void MainFrame::init_tabpanel()
     m_tabpanel->Hide();
     m_settings_dialog.set_tabpanel(m_tabpanel);
 
-    m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) {
+    m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
+#if ENABLE_VALIDATE_CUSTOM_GCODE
+        Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(e.GetOldSelection()));
+        if (old_tab)
+            old_tab->validate_custom_gcodes();
+#endif // ENABLE_VALIDATE_CUSTOM_GCODE
+
         wxWindow* panel = m_tabpanel->GetCurrentPage();
         Tab* tab = dynamic_cast<Tab*>(panel);
 
@@ -544,19 +550,6 @@ void MainFrame::init_tabpanel()
             select_tab(size_t(0)); // select Plater
     });
 
-#if ENABLE_VALIDATE_CUSTOM_GCODE
-    m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, [this](wxBookCtrlEvent& evt) {
-        wxWindow* panel = m_tabpanel->GetCurrentPage();
-        if (panel != nullptr) {
-            Tab* tab = dynamic_cast<Tab*>(panel);
-            if (tab != nullptr)
-                tab->validate_custom_gcodes();
-//            if (tab != nullptr && !tab->validate_custom_gcodes())
-//                evt.Veto();
-        }
-        });
-#endif // ENABLE_VALIDATE_CUSTOM_GCODE
-
     m_plater = new Plater(this, this);
     m_plater->Hide();
 
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 74b54478e..11c4875eb 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1738,7 +1738,7 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode)
 }
 
 static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group, const t_config_option_key& opt_key, const boost::any& value) {
-    Tab::validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(value));
+    tab->validate_custom_gcodes_was_shown = !Tab::validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(value));
     tab->update_dirty();
     tab->on_value_change(opt_key, value);
 }
@@ -3837,27 +3837,18 @@ bool Tab::validate_custom_gcodes()
     if (m_active_page->title() != L("Custom G-code"))
         return true;
 
+    // When we switch Settings tab after editing of the custom g-code, then warning message could ba already shown after KillFocus event
+    // and then it's no need to show it again
+    if (validate_custom_gcodes_was_shown) {
+        validate_custom_gcodes_was_shown = false;
+        return true;
+    }
+
     bool valid = true;
     for (auto opt_group : m_active_page->m_optgroups) {
         assert(opt_group->opt_map().size() == 1);
         std::string key = opt_group->opt_map().begin()->first;
-        std::string value = boost::any_cast<std::string>(opt_group->get_value(key));
-        std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, 0u) : m_config->opt_string(key);
-        valid &= validate_custom_gcode(opt_group->title, value);
-        Field* field = opt_group->get_field(key);
-        TextCtrl* text_ctrl = dynamic_cast<TextCtrl*>(field);
-        if (text_ctrl != nullptr && text_ctrl->m_on_change != nullptr && !text_ctrl->m_disable_change_event) {
-            Slic3r::GUI::t_change callback = opt_group->m_on_change;
-            // temporary disable the opt_group->m_on_change callback to avoid multiple validations
-            opt_group->m_on_change = nullptr;
-            text_ctrl->m_on_change(key, value);
-            // restore the opt_group->m_on_change callback
-            opt_group->m_on_change = callback;
-
-            update_dirty();
-            on_value_change(key, value);
-        }
-
+        valid &= validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(opt_group->get_value(key)));
         if (!valid)
             break;
     }
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index fcbe27eb7..8cbc6585a 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -351,6 +351,7 @@ public:
 #if ENABLE_VALIDATE_CUSTOM_GCODE
 	static bool validate_custom_gcode(const wxString& title, const std::string& gcode);
 	bool        validate_custom_gcodes();
+    bool        validate_custom_gcodes_was_shown { false };
 #endif // ENABLE_VALIDATE_CUSTOM_GCODE
 
 protected:

From 8bc23c90fc59362374251967d2ba4c058d298aa0 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 24 Mar 2021 11:31:50 +0100
Subject: [PATCH 11/12] Suppress to show Search window on the Plater using
 Ctrl+F shortcut, when we are at Preview mode

---
 src/slic3r/GUI/MainFrame.cpp | 2 +-
 src/slic3r/GUI/Plater.cpp    | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index a5a920f82..35b1c16d8 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -1151,7 +1151,7 @@ void MainFrame::init_menubar_as_editor()
 
         editMenu->AppendSeparator();
         append_menu_item(editMenu, wxID_ANY, _L("Searc&h") + "\tCtrl+F",
-            _L("Search in settings"), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); },
+            _L("Search in settings"), [this](wxCommandEvent&) { m_plater->search(m_plater->IsShown()); },
             "search", nullptr, []() {return true; }, this);
     }
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index f8ec2584c..b4b025cd2 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -5858,6 +5858,8 @@ void Plater::paste_from_clipboard()
 void Plater::search(bool plater_is_active)
 {
     if (plater_is_active) {
+        if (is_preview_shown())
+            return;
         // plater should be focused for correct navigation inside search window 
         this->SetFocus();
 

From 9c951b31089b764c1735fcc0491781e89f8cc32d Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Wed, 24 Mar 2021 11:36:16 +0100
Subject: [PATCH 12/12] Fixed parsing of g-code files generated by newer
 versions of Simplify3D

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 56 +++++++++++++++++---------
 1 file changed, 38 insertions(+), 18 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index a3877845f..06de1b24e 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -1491,89 +1491,109 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
 {
     // extrusion roles
 
+    // in older versions the comments did not contain the key 'feature'
+    std::string_view cmt = comment;
+    size_t pos = cmt.find(" feature");
+    if (pos == 0)
+        cmt.remove_prefix(8);
+
     // ; skirt
-    size_t pos = comment.find(" skirt");
+    pos = cmt.find(" skirt");
     if (pos == 0) {
         m_extrusion_role = erSkirt;
         return true;
     }
     
     // ; outer perimeter
-    pos = comment.find(" outer perimeter");
+    pos = cmt.find(" outer perimeter");
     if (pos == 0) {
         m_extrusion_role = erExternalPerimeter;
         return true;
     }
 
     // ; inner perimeter
-    pos = comment.find(" inner perimeter");
+    pos = cmt.find(" inner perimeter");
     if (pos == 0) {
         m_extrusion_role = erPerimeter;
         return true;
     }
 
     // ; gap fill
-    pos = comment.find(" gap fill");
+    pos = cmt.find(" gap fill");
     if (pos == 0) {
         m_extrusion_role = erGapFill;
         return true;
     }
 
     // ; infill
-    pos = comment.find(" infill");
+    pos = cmt.find(" infill");
     if (pos == 0) {
         m_extrusion_role = erInternalInfill;
         return true;
     }
 
     // ; solid layer
-    pos = comment.find(" solid layer");
+    pos = cmt.find(" solid layer");
     if (pos == 0) {
-        m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+        m_extrusion_role = erSolidInfill;
         return true;
     }
 
     // ; bridge
-    pos = comment.find(" bridge");
+    pos = cmt.find(" bridge");
     if (pos == 0) {
         m_extrusion_role = erBridgeInfill;
         return true;
     }
 
     // ; support
-    pos = comment.find(" support");
+    pos = cmt.find(" support");
     if (pos == 0) {
         m_extrusion_role = erSupportMaterial;
         return true;
     }
 
+    // ; dense support
+    pos = cmt.find(" dense support");
+    if (pos == 0) {
+        m_extrusion_role = erSupportMaterialInterface;
+        return true;
+    }
+
     // ; prime pillar
-    pos = comment.find(" prime pillar");
+    pos = cmt.find(" prime pillar");
     if (pos == 0) {
         m_extrusion_role = erWipeTower;
         return true;
     }
 
     // ; ooze shield
-    pos = comment.find(" ooze shield");
+    pos = cmt.find(" ooze shield");
     if (pos == 0) {
-        m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+        m_extrusion_role = erNone; // Missing mapping
         return true;
     }
 
     // ; raft
-    pos = comment.find(" raft");
+    pos = cmt.find(" raft");
     if (pos == 0) {
-        m_extrusion_role = erSkirt;
+        m_extrusion_role = erSupportMaterial;
+        return true;
+    }
+
+    // ; internal single extrusion
+    pos = cmt.find(" internal single extrusion");
+    if (pos == 0) {
+        m_extrusion_role = erNone; // Missing mapping
         return true;
     }
 
     // geometry
     // ; tool
     std::string tag = " tool";
-    pos = comment.find(tag);
+    pos = cmt.find(tag);
     if (pos == 0) {
-        const std::string_view data = comment.substr(pos + tag.length());
+        const std::string_view data = cmt.substr(pos + tag.length());
         std::string h_tag = "H";
         size_t h_start = data.find(h_tag);
         size_t h_end = data.find_first_of(' ', h_start);
@@ -1594,10 +1614,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
 
     // ; layer
     tag = " layer";
-    pos = comment.find(tag);
+    pos = cmt.find(tag);
     if (pos == 0) {
         // skip lines "; layer end"
-        const std::string_view data = comment.substr(pos + tag.length());
+        const std::string_view data = cmt.substr(pos + tag.length());
         size_t end_start = data.find("end");
         if (end_start == data.npos)
             ++m_layer_id;