From cd4155a890d6a69e749e0079fb0d86c4d5a7a397 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Fri, 3 Sep 2021 10:19:06 +0200
Subject: [PATCH 01/32] Hints.cereal file existence check

---
 src/slic3r/GUI/HintNotification.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp
index 33ccdc4ae..fe5eeb67c 100644
--- a/src/slic3r/GUI/HintNotification.cpp
+++ b/src/slic3r/GUI/HintNotification.cpp
@@ -74,7 +74,12 @@ void write_used_binary(const std::vector<std::string>& ids)
 }
 void read_used_binary(std::vector<std::string>& ids)
 {
-	boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"));
+	boost::filesystem::path path(boost::filesystem::path(data_dir()) / "cache" / "hints.cereal");
+	if (!boost::filesystem::exists(path)) {
+		BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string();
+		return;
+	}
+	boost::filesystem::ifstream file(path);
 	cereal::BinaryInputArchive archive(file);
 	HintsCerealData cd;
 	try

From 79784d1a2e3772b51c661a2dc144f7ea142acbb5 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 11:45:01 +0200
Subject: [PATCH 02/32] Fix of "Support on build plate only" no longer
 overridden by support painting (#6863) This is a regression to a late
 PrusaSlicer 2.4.0-alpha0 change 
 8dfc0422a878c5e44d4233c6ce522c77a0c3280f     Faster and hopefully more
 reliable projection of paint-on support     blockers and enforcers on a
 sliced mesh.     Previous d89f01c71795e324f510939e2cece05b9586291c did not
 fix it.

---
 src/libslic3r/PrintObject.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 526b7b874..808bdd8a1 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -2301,7 +2301,14 @@ void PrintObject::project_and_append_custom_facets(
                 else {
                     std::vector<Polygons> projected;
                     slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
-                    append(out, std::move(projected));
+                    // Merge these projections with the output, layer by layer.
+                    assert(! projected.empty());
+                    assert(out.empty() || out.size() == projected.size());
+                    if (out.empty())
+                        out = std::move(projected);
+                    else
+                        for (size_t i = 0; i < out.size(); ++ i)
+                            append(out[i], std::move(projected[i]));
                 }
         }
 }

From ed7ebf15d97970597d871ea31b464e6538354529 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 3 Sep 2021 12:13:01 +0200
Subject: [PATCH 03/32] Added logging when bed texture/model is not found

---
 src/libslic3r/PrintObject.cpp | 3 ++-
 src/slic3r/GUI/3DBed.cpp      | 8 ++++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 808bdd8a1..d9a4f2670 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -2293,7 +2293,7 @@ void PrintObject::project_and_append_custom_facets(
             const indexed_triangle_set custom_facets = seam
                     ? mv->seam_facets.get_facets_strict(*mv, type)
                     : mv->supported_facets.get_facets_strict(*mv, type);
-            if (! custom_facets.indices.empty())
+            if (! custom_facets.indices.empty()) {
                 if (seam)
                     project_triangles_to_slabs(this->layers(), custom_facets,
                         (this->trafo_centered() * mv->get_matrix()).cast<float>(),
@@ -2310,6 +2310,7 @@ void PrintObject::project_and_append_custom_facets(
                         for (size_t i = 0; i < out.size(); ++ i)
                             append(out[i], std::move(projected[i]));
                 }
+            }
         }
 }
 
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp
index eaf75ba5b..5b7218c87 100644
--- a/src/slic3r/GUI/3DBed.cpp
+++ b/src/slic3r/GUI/3DBed.cpp
@@ -163,12 +163,16 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
     }
 
     std::string texture_filename = custom_texture.empty() ? texture : custom_texture;
-    if (!check_texture(texture_filename))
+    if (! texture_filename.empty() && ! check_texture(texture_filename)) {
+        BOOST_LOG_TRIVIAL(error) << "Unable to load bed texture: " << texture_filename;
         texture_filename.clear();
+    }
 
     std::string model_filename = custom_model.empty() ? model : custom_model;
-    if (!check_model(model_filename))
+    if (! model_filename.empty() && ! check_model(model_filename)) {
+        BOOST_LOG_TRIVIAL(error) << "Unable to load bed model: " << model_filename;
         model_filename.clear();
+    }
 
     if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
         // No change, no need to update the UI.

From 4c80d9ed01bc0fdcda9235224f50caeb77d86d77 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 27 Aug 2021 09:04:15 +0200
Subject: [PATCH 04/32] Fix taking entering/leaving snapshots in SLA gizmo:
 This was broken between 2.2.0 and 2.3.0. The 'entering' snapshot should be
 taken before the gizmo opens, not after. Otherwise it is in fact the same as
 the next snapshot.

---
 src/slic3r/GUI/Gizmos/GLGizmoBase.hpp        |  1 +
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 11 -------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |  2 ++
 src/slic3r/GUI/Gizmos/GLGizmosManager.cpp    | 34 +++++++++++---------
 4 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index 8b033ce73..1c6e00c07 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -135,6 +135,7 @@ public:
     bool is_activable() const { return on_is_activable(); }
     bool is_selectable() const { return on_is_selectable(); }
     CommonGizmosDataID get_requirements() const { return on_get_requirements(); }
+    virtual bool wants_enter_leave_snapshots() const { return false; }
     void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
 
     unsigned int get_sprite_id() const { return m_sprite_id; }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 8c90d20d3..c7faab3bf 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -901,15 +901,6 @@ void GLGizmoSlaSupports::on_set_state()
         return;
 
     if (m_state == On && m_old_state != On) { // the gizmo was just turned on
-        if (! m_parent.get_gizmos_manager().is_serializing()) {
-            // Only take the snapshot when the USER opens the gizmo. Common gizmos
-            // data are not yet available, the CallAfter will postpone taking the
-            // snapshot until they are. No, it does not feel right.
-            wxGetApp().CallAfter([]() {
-                Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo"));
-            });
-        }
-
         // Set default head diameter from config.
         const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
         m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
@@ -925,8 +916,6 @@ void GLGizmoSlaSupports::on_set_state()
         else {
             // we are actually shutting down
             disable_editing_mode(); // so it is not active next time the gizmo opens
-            Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo"));
-            m_normal_cache.clear();
             m_old_mo_id = -1;
         }
     }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index cb60c0e25..8e30aa6b8 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -67,6 +67,8 @@ public:
     bool has_backend_supports() const;
     void reslice_SLA_supports(bool postpone_error_messages = false) const;
 
+    bool wants_enter_leave_snapshots() const override { return true; }
+
 private:
     bool on_init() override;
     void on_update(const UpdateData& data) override;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 6d9a03977..080991859 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -22,6 +22,7 @@
 #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
 #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
 
+#include "libslic3r/format.hpp"
 #include "libslic3r/Model.hpp"
 #include "libslic3r/PresetBundle.hpp"
 
@@ -1198,26 +1199,29 @@ void GLGizmosManager::activate_gizmo(EType type)
     if (m_gizmos.empty() || m_current == type)
         return;
 
-    if (m_current != Undefined) {
-        m_gizmos[m_current]->set_state(GLGizmoBase::Off);
-        if (m_gizmos[m_current]->get_state() != GLGizmoBase::Off)
+    GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get();
+    GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get();
+
+    if (old_gizmo) {
+        old_gizmo->set_state(GLGizmoBase::Off);
+        if (old_gizmo->get_state() != GLGizmoBase::Off)
             return; // gizmo refused to be turned off, do nothing.
+
+        if (! m_parent.get_gizmos_manager().is_serializing()
+         && old_gizmo->wants_enter_leave_snapshots())
+            Plater::TakeSnapshot snapshot(wxGetApp().plater(),
+                Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name()));
     }
 
+    if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing()
+     && new_gizmo->wants_enter_leave_snapshots())
+        Plater::TakeSnapshot snapshot(wxGetApp().plater(),
+            Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name()));
+
     m_current = type;
 
-    // Updating common data should be left to the update_data function, which
-    // is always called after this one. activate_gizmo can be called by undo/redo,
-    // when selection is not yet deserialized, so the common data would update
-    // incorrectly (or crash if relying on unempty selection). Undo/redo stack
-    // will also call update_data, after selection is restored.
-
-    //m_common_gizmos_data->update(get_current()
-    //                       ? get_current()->get_requirements()
-    //                       : CommonGizmosDataID(0));
-
-    if (type != Undefined)
-        m_gizmos[type]->set_state(GLGizmoBase::On);
+    if (new_gizmo)
+        new_gizmo->set_state(GLGizmoBase::On);
 }
 
 

From a4300b8e6411d42ce73f3ff2cd23785a3f172d8f Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 27 Aug 2021 09:04:33 +0200
Subject: [PATCH 05/32] Naming of the entering/leaving snapshots is now more
 generic, it uses the actual name of the gizmo. Also, the keyboard shortcut is
 now appended to the name, instead of being duplicated in it.

---
 src/slic3r/GUI/Gizmos/GLGizmoBase.cpp            | 14 ++++++++++++++
 src/slic3r/GUI/Gizmos/GLGizmoBase.hpp            |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoCut.cpp             |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp     |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp         |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp          |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmoMove.cpp            |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp          |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoScale.cpp           |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp            |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp        |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp     |  4 ++--
 src/slic3r/GUI/Gizmos/GLGizmosManager.cpp        |  4 ++--
 14 files changed, 34 insertions(+), 20 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index cc089e26e..64479a39e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -232,6 +232,20 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
     }
 }
 
+
+
+std::string GLGizmoBase::get_name(bool include_shortcut) const
+{
+    int key = get_shortcut_key();
+    assert( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z);
+    std::string out = on_get_name();
+    if (include_shortcut)
+        out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]";
+    return out;
+}
+
+
+
 // Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
 // were not interpolated by alpha blending or multi sampling.
 unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index 1c6e00c07..05f6adb5e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -120,7 +120,7 @@ public:
     void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); }
     void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); }
 
-    std::string get_name() const { return on_get_name(); }
+    std::string get_name(bool include_shortcut = true) const;
 
     int get_group_id() const { return m_group_id; }
     void set_group_id(int id) { m_group_id = id; }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 641258ca4..3dcb9e2b1 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -49,7 +49,7 @@ bool GLGizmoCut::on_init()
 
 std::string GLGizmoCut::on_get_name() const
 {
-    return (_L("Cut") + " [C]").ToUTF8().data();
+    return _u8L("Cut");
 }
 
 void GLGizmoCut::on_set_state()
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 99c010bf1..1ebba4d11 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -28,7 +28,7 @@ void GLGizmoFdmSupports::on_shutdown()
 
 std::string GLGizmoFdmSupports::on_get_name() const
 {
-    return (_L("Paint-on supports") + " [L]").ToUTF8().data();
+    return _u8L("Paint-on supports");
 }
 
 
@@ -85,7 +85,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
     y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 
-    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
+    m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
index 3e5c12eec..b8c7d9f92 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
@@ -37,7 +37,7 @@ CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
 
 std::string GLGizmoFlatten::on_get_name() const
 {
-    return (_L("Place on face") + " [F]").ToUTF8().data();
+    return _u8L("Place on face");
 }
 
 bool GLGizmoFlatten::on_is_activable() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index 05e0be141..f405f457d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -505,7 +505,7 @@ RENDER_AGAIN:
     y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 
-    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
+    m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
     const float settings_sliders_left =
@@ -773,7 +773,7 @@ bool GLGizmoHollow::on_is_selectable() const
 
 std::string GLGizmoHollow::on_get_name() const
 {
-    return (_(L("Hollow and drill")) + " [H]").ToUTF8().data();
+    return _u8L("Hollow and drill");
 }
 
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
index a30ac377e..608e7bd1f 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
@@ -42,7 +42,7 @@ void GLGizmoMmuSegmentation::on_shutdown()
 std::string GLGizmoMmuSegmentation::on_get_name() const
 {
     // FIXME Lukas H.: Discuss and change shortcut
-    return (_L("Multimaterial painting") + " [N]").ToUTF8().data();
+    return _u8L("Multimaterial painting");
 }
 
 bool GLGizmoMmuSegmentation::on_is_selectable() const
@@ -239,7 +239,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
                             y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 
-    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
+    m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index 9a056adcb..e7b6a0ad6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -52,7 +52,7 @@ bool GLGizmoMove3D::on_init()
 
 std::string GLGizmoMove3D::on_get_name() const
 {
-    return (_L("Move") + " [M]").ToUTF8().data();
+    return _u8L("Move");
 }
 
 bool GLGizmoMove3D::on_is_activable() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
index 36759d2ec..54fcfc2c7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
@@ -463,7 +463,7 @@ bool GLGizmoRotate3D::on_init()
 
 std::string GLGizmoRotate3D::on_get_name() const
 {
-    return (_L("Rotate") + " [R]").ToUTF8().data();
+    return _u8L("Rotate");
 }
 
 bool GLGizmoRotate3D::on_is_activable() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
index 894d844d8..b97507166 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
@@ -76,7 +76,7 @@ bool GLGizmoScale3D::on_init()
 
 std::string GLGizmoScale3D::on_get_name() const
 {
-    return (_L("Scale") + " [S]").ToUTF8().data();
+    return _u8L("Scale");
 }
 
 bool GLGizmoScale3D::on_is_activable() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
index 5b84bbaba..a2ee56d9a 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
@@ -48,7 +48,7 @@ bool GLGizmoSeam::on_init()
 
 std::string GLGizmoSeam::on_get_name() const
 {
-    return (_L("Seam painting") + " [P]").ToUTF8().data();
+    return _u8L("Seam painting");
 }
 
 
@@ -79,7 +79,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
     const float approx_height = m_imgui->scaled(14.0f);
     y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
-    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
+    m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
index f70e3f93e..9f49f9734 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
@@ -44,7 +44,7 @@ bool GLGizmoSimplify::on_init()
 
 std::string GLGizmoSimplify::on_get_name() const
 {
-    return (_L("Simplify")).ToUTF8().data();
+    return _u8L("Simplify");
 }
 
 void GLGizmoSimplify::on_render() {}
@@ -97,7 +97,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
 
     int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
                ImGuiWindowFlags_NoCollapse;
-    m_imgui->begin(on_get_name(), flag);
+    m_imgui->begin(get_name(), flag);
 
     size_t triangle_count = m_volume->mesh().its.indices.size();
     // already reduced mesh
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index c7faab3bf..11446d6d7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -625,7 +625,7 @@ RENDER_AGAIN:
     //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
     //ImGui::SetNextWindowSize(ImVec2(window_size));
 
-    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
+    m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // adjust window position to avoid overlap the view toolbar
     float win_h = ImGui::GetWindowHeight();
@@ -863,7 +863,7 @@ bool GLGizmoSlaSupports::on_is_selectable() const
 
 std::string GLGizmoSlaSupports::on_get_name() const
 {
-    return (_L("SLA Support Points") + " [L]").ToUTF8().data();
+    return _u8L("SLA Support Points");
 }
 
 CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 080991859..dfe6de746 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -1210,13 +1210,13 @@ void GLGizmosManager::activate_gizmo(EType type)
         if (! m_parent.get_gizmos_manager().is_serializing()
          && old_gizmo->wants_enter_leave_snapshots())
             Plater::TakeSnapshot snapshot(wxGetApp().plater(),
-                Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name()));
+                Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name(false)));
     }
 
     if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing()
      && new_gizmo->wants_enter_leave_snapshots())
         Plater::TakeSnapshot snapshot(wxGetApp().plater(),
-            Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name()));
+            Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name(false)));
 
     m_current = type;
 

From 315663980bf1f567e110cdcb72a2f2b694f4c5e5 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 31 Aug 2021 13:48:22 +0200
Subject: [PATCH 06/32] Do not allow 'replace by stl' when a gizmo is active,
 some of the gizmos might not cope well. ALso avoided code duplication in all
 such cases (currently fix by Netfabb, simplify, replace by stl).

---
 src/slic3r/GUI/GUI_ObjectList.cpp         | 13 ++++------
 src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 29 ++++++++++++++++-------
 src/slic3r/GUI/Gizmos/GLGizmosManager.hpp |  3 ++-
 src/slic3r/GUI/Plater.cpp                 | 11 ++++-----
 4 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 096836f23..c99d9090c 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -4030,17 +4030,12 @@ void ObjectList::simplify()
 
     // Do not simplify when a gizmo is open. There might be issues with updates
     // and what is worse, the snapshot time would refer to the internal stack.
-    auto current_type = gizmos_mgr.get_current_type();
-    if (current_type == GLGizmosManager::Simplify) {
+    if (! gizmos_mgr.check_gizmos_closed_except(GLGizmosManager::EType::Simplify))
+        return;
+
+    if (gizmos_mgr.get_current_type() == GLGizmosManager::Simplify) {
         // close first
         gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
-    }else if (current_type != GLGizmosManager::Undefined) {
-        plater->get_notification_manager()->push_notification(
-            NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
-            NotificationManager::NotificationLevel::RegularNotification,
-            _u8L("ERROR: Please close all manipulators available from "
-                 "the left toolbar before start simplify the mesh."));
-        return;
     }
     gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
 }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index dfe6de746..93eba3042 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -164,10 +164,8 @@ void GLGizmosManager::refresh_on_off_state()
         return;
 
     if (m_current != Undefined
-    && ! m_gizmos[m_current]->is_activable()) {
-        activate_gizmo(Undefined);
+    && ! m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined))
         update_data();
-    }
 }
 
 void GLGizmosManager::reset_all_states()
@@ -182,14 +180,28 @@ void GLGizmosManager::reset_all_states()
 bool GLGizmosManager::open_gizmo(EType type)
 {
     int idx = int(type);
-    if (m_gizmos[idx]->is_activable()) {
-        activate_gizmo(m_current == idx ? Undefined : (EType)idx);
+    if (m_gizmos[idx]->is_activable()
+     && activate_gizmo(m_current == idx ? Undefined : (EType)idx)) {
         update_data();
         return true;
     }
     return false;
 }
 
+
+bool GLGizmosManager::check_gizmos_closed_except(EType type) const
+{
+    if (get_current_type() != type && get_current_type() != Undefined) {
+        wxGetApp().plater()->get_notification_manager()->push_notification(
+                    NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
+                    NotificationManager::NotificationLevel::RegularNotification,
+                    _u8L("ERROR: Please close all manipulators available from "
+                         "the left toolbar first"));
+        return false;
+    }
+    return true;
+}
+
 void GLGizmosManager::set_hover_id(int id)
 {
     if (!m_enabled || m_current == Undefined)
@@ -1194,10 +1206,10 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos)
     return name;
 }
 
-void GLGizmosManager::activate_gizmo(EType type)
+bool GLGizmosManager::activate_gizmo(EType type)
 {
     if (m_gizmos.empty() || m_current == type)
-        return;
+        return true;
 
     GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get();
     GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get();
@@ -1205,7 +1217,7 @@ void GLGizmosManager::activate_gizmo(EType type)
     if (old_gizmo) {
         old_gizmo->set_state(GLGizmoBase::Off);
         if (old_gizmo->get_state() != GLGizmoBase::Off)
-            return; // gizmo refused to be turned off, do nothing.
+            return false; // gizmo refused to be turned off, do nothing.
 
         if (! m_parent.get_gizmos_manager().is_serializing()
          && old_gizmo->wants_enter_leave_snapshots())
@@ -1222,6 +1234,7 @@ void GLGizmosManager::activate_gizmo(EType type)
 
     if (new_gizmo)
         new_gizmo->set_state(GLGizmoBase::On);
+    return true;
 }
 
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index 188c9e914..ed02574df 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -104,7 +104,7 @@ private:
     std::vector<size_t> get_selectable_idxs() const;
     size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const;
 
-    void activate_gizmo(EType type);
+    bool activate_gizmo(EType type);
 
     struct MouseCapture
     {
@@ -176,6 +176,7 @@ public:
     void reset_all_states();
     bool is_serializing() const { return m_serializing; }
     bool open_gizmo(EType type);
+    bool check_gizmos_closed_except(EType) const;
 
     void set_hover_id(int id);
     void enable_grabber(EType type, unsigned int id, bool enable);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 4236ee450..af8734184 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3186,6 +3186,9 @@ void Plater::priv::update_sla_scene()
 
 void Plater::priv::replace_with_stl()
 {
+    if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::EType::Undefined))
+        return;
+
     const Selection& selection = get_selection();
 
     if (selection.is_wipe_tower() || get_selection().get_volume_idxs().size() != 1)
@@ -3530,14 +3533,8 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
 
     // Do not fix anything when a gizmo is open. There might be issues with updates
     // and what is worse, the snapshot time would refer to the internal stack.
-    if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) {
-        notification_manager->push_notification(
-                    NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
-                    NotificationManager::NotificationLevel::RegularNotification,
-                    _u8L("ERROR: Please close all manipulators available from "
-                         "the left toolbar before fixing the mesh."));
+    if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
         return;
-    }
 
     // size_t snapshot_time = undo_redo_stack().active_snapshot_time();
     Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));

From 2e250c1463c5d8e8bc94d0769e3e82d648e99e3b Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 16:16:43 +0200
Subject: [PATCH 07/32] Speed-up in GCodeViewer vertex buffer generator

---
 src/slic3r/GUI/GCodeViewer.cpp | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 3fc624264..656a67406 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -70,18 +70,9 @@ static std::vector<std::array<float, 4>> decode_colors(const std::vector<std::st
     return output;
 }
 
-static float round_to_nearest(float value, unsigned int decimals)
+static float round_to_nearest_percent(float value)
 {
-    float res = 0.0f;
-    if (decimals == 0)
-        res = std::round(value);
-    else {
-        char buf[64];
-        // locales should not matter, both sprintf and stof are sensitive, so...
-        sprintf(buf, "%.*g", decimals, value);
-        res = std::stof(buf);
-    }
-    return res;
+    return std::round(value * 100.f) * 0.01f;
 }
 
 void GCodeViewer::VBuffer::reset()
@@ -146,7 +137,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
         // use rounding to reduce the number of generated paths
         return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
             move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
-            height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) &&
+            height == round_to_nearest_percent(move.height) && width == round_to_nearest_percent(move.width) &&
             matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
     }
     case EMoveType::Travel: {
@@ -183,7 +174,7 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
     Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
     // use rounding to reduce the number of generated paths
     paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
-        round_to_nearest(move.height, 2), round_to_nearest(move.width, 2),
+        round_to_nearest_percent(move.height), round_to_nearest_percent(move.width),
         move.feedrate, move.fan_speed, move.temperature,
         move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
 }
@@ -721,11 +712,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
         {
         case EMoveType::Extrude:
         {
-            m_extrusions.ranges.height.update_from(round_to_nearest(curr.height, 2));
-            m_extrusions.ranges.width.update_from(round_to_nearest(curr.width, 2));
+            m_extrusions.ranges.height.update_from(round_to_nearest_percent(curr.height));
+            m_extrusions.ranges.width.update_from(round_to_nearest_percent(curr.width));
             m_extrusions.ranges.fan_speed.update_from(curr.fan_speed);
             m_extrusions.ranges.temperature.update_from(curr.temperature);
-            m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest(curr.volumetric_rate(), 2));
+            m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest_percent(curr.volumetric_rate()));
             [[fallthrough]];
         }
         case EMoveType::Travel:

From d154752c38dcf01e3a8130017e4bad841d741c66 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 16:17:24 +0200
Subject: [PATCH 08/32] Commenting out untested and unused
 Polyline::simplify_by_visibility()

---
 src/libslic3r/Polyline.cpp |  4 ++-
 src/libslic3r/Polyline.hpp |  2 +-
 xs/t/09_polyline.t         | 70 +++++++++++++++++++-------------------
 xs/xsp/Polyline.xsp        |  2 --
 4 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp
index a6be64299..68c40fc20 100644
--- a/src/libslic3r/Polyline.cpp
+++ b/src/libslic3r/Polyline.cpp
@@ -120,7 +120,8 @@ void Polyline::simplify(double tolerance)
     this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
 }
 
-/* This method simplifies all *lines* contained in the supplied area */
+#if 0
+// This method simplifies all *lines* contained in the supplied area
 template <class T>
 void Polyline::simplify_by_visibility(const T &area)
 {
@@ -141,6 +142,7 @@ void Polyline::simplify_by_visibility(const T &area)
 }
 template void Polyline::simplify_by_visibility<ExPolygon>(const ExPolygon &area);
 template void Polyline::simplify_by_visibility<ExPolygonCollection>(const ExPolygonCollection &area);
+#endif
 
 void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
 {
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index 51dcf9d36..758fc38cd 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -72,7 +72,7 @@ public:
     void extend_start(double distance);
     Points equally_spaced_points(double distance) const;
     void simplify(double tolerance);
-    template <class T> void simplify_by_visibility(const T &area);
+//    template <class T> void simplify_by_visibility(const T &area);
     void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
     bool is_straight() const;
     bool is_closed() const { return this->points.front() == this->points.back(); }
diff --git a/xs/t/09_polyline.t b/xs/t/09_polyline.t
index 0a7b8dcb0..5203ec5ef 100644
--- a/xs/t/09_polyline.t
+++ b/xs/t/09_polyline.t
@@ -89,40 +89,40 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline';
 }
 
 # disabled because we now use a more efficient but incomplete algorithm
-if (0) {
-    my $polyline = Slic3r::Polyline->new(
-        map [$_,10], (0,10,20,30,40,50,60)
-    );
-    {
-        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
-            [25,0], [55,0], [55,30], [25,30],
-        ));
-        my $p = $polyline->clone;
-        $p->simplify_by_visibility($expolygon);
-        is_deeply $p->pp, [
-            map [$_,10], (0,10,20,30,50,60)
-        ], 'simplify_by_visibility()';
-    }
-    {
-        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
-            [-15,0], [75,0], [75,30], [-15,30],
-        ));
-        my $p = $polyline->clone;
-        $p->simplify_by_visibility($expolygon);
-        is_deeply $p->pp, [
-            map [$_,10], (0,60)
-        ], 'simplify_by_visibility()';
-    }
-    {
-        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
-            [-15,0], [25,0], [25,30], [-15,30],
-        ));
-        my $p = $polyline->clone;
-        $p->simplify_by_visibility($expolygon);
-        is_deeply $p->pp, [
-            map [$_,10], (0,20,30,40,50,60)
-        ], 'simplify_by_visibility()';
-    }
-}
+#if (0) {
+#    my $polyline = Slic3r::Polyline->new(
+#        map [$_,10], (0,10,20,30,40,50,60)
+#    );
+#    {
+#        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
+#            [25,0], [55,0], [55,30], [25,30],
+#        ));
+#        my $p = $polyline->clone;
+#        $p->simplify_by_visibility($expolygon);
+#        is_deeply $p->pp, [
+#            map [$_,10], (0,10,20,30,50,60)
+#        ], 'simplify_by_visibility()';
+#    }
+#    {
+#        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
+#            [-15,0], [75,0], [75,30], [-15,30],
+#        ));
+#        my $p = $polyline->clone;
+#        $p->simplify_by_visibility($expolygon);
+#        is_deeply $p->pp, [
+#            map [$_,10], (0,60)
+#        ], 'simplify_by_visibility()';
+#    }
+#    {
+#        my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
+#            [-15,0], [25,0], [25,30], [-15,30],
+#        ));
+#        my $p = $polyline->clone;
+#        $p->simplify_by_visibility($expolygon);
+#        is_deeply $p->pp, [
+#            map [$_,10], (0,20,30,40,50,60)
+#        ], 'simplify_by_visibility()';
+#    }
+#}
 
 __END__
diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp
index 10bbb263f..7846ea5f4 100644
--- a/xs/xsp/Polyline.xsp
+++ b/xs/xsp/Polyline.xsp
@@ -31,8 +31,6 @@
     void extend_end(double distance);
     void extend_start(double distance);
     void simplify(double tolerance);
-    void simplify_by_visibility(ExPolygon* expolygon)
-        %code{% THIS->simplify_by_visibility(*expolygon); %};
     void split_at(Point* point, Polyline* p1, Polyline* p2)
         %code{% THIS->split_at(*point, p1, p2); %};
     bool is_straight();

From dc7272391191a76b4315d1b7849ff2e570fd69fe Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 16:19:16 +0200
Subject: [PATCH 09/32] Optimization of GCodeReader: 1) Use std::from_chars()
 instead of strtod() 2) Own implementation of buffered readline()

---
 src/libslic3r/GCodeReader.cpp | 40 +++++++++++++++++++++++++++++------
 src/libslic3r/GCodeReader.hpp | 13 ++++++------
 2 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index 7dc3c9a8a..54e6802e6 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -2,6 +2,7 @@
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <boost/nowide/fstream.hpp>
+#include <charconv>
 #include <fstream>
 #include <iostream>
 #include <iomanip>
@@ -32,7 +33,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
     m_extrusion_axis = get_extrusion_axis_char(m_config);
 }
 
-const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
+const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command)
 {
     PROFILE_FUNC();
 
@@ -70,9 +71,9 @@ const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline,
             }
             if (axis != NUM_AXES_WITH_UNKNOWN) {
                 // Try to parse the numeric value.
-                char   *pend = nullptr;
-                double  v = strtod(++ c, &pend);
-                if (pend != nullptr && is_end_of_word(*pend)) {
+                double v;
+                auto [pend, ec] = std::from_chars(++ c, end, v);
+                if (pend != c && is_end_of_word(*pend)) {
                     // The axis value has been parsed correctly.
                     if (axis != UNKNOWN_AXIS)
 	                    gline.m_axis[int(axis)] = float(v);
@@ -128,10 +129,37 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
 void GCodeReader::parse_file(const std::string &file, callback_t callback)
 {
     boost::nowide::ifstream f(file);
+    f.sync_with_stdio(false);
+    std::vector<char> buffer(65536 * 10, 0);
     std::string line;
     m_parsing = true;
-    while (m_parsing && std::getline(f, line))
-        this->parse_line(line, callback);
+    GCodeLine gline;
+    bool eof = false;
+    while (m_parsing && ! eof) {
+        f.read(buffer.data(), buffer.size());
+        auto it        = buffer.begin();
+        auto it_bufend = buffer.begin() + f.gcount();
+        eof = ! f.good();
+        while (it != it_bufend) {
+            bool eol = false;
+            auto it_end = it;
+            for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
+            eol |= eof && it_end == it_bufend;
+            if (eol) {
+                gline.reset();
+                if (line.empty())
+                    this->parse_line(&(*it), &(*it_end), gline, callback);
+                else {
+                    line.insert(line.end(), it, it_end);
+                    this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
+                    line.clear();
+                }
+            } else
+                line.insert(line.end(), it, it_end);
+            // Skip all the empty lines.
+            for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
+        }
+    }
 }
 
 bool GCodeReader::GCodeLine::has(char axis) const
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 6b58608e6..88c9df284 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -83,11 +83,12 @@ public:
     void parse_buffer(const std::string &buffer, Callback callback)
     {
         const char *ptr = buffer.c_str();
+        const char *end = ptr + buffer.size();
         GCodeLine gline;
         m_parsing = true;
         while (m_parsing && *ptr != 0) {
             gline.reset();
-            ptr = this->parse_line(ptr, gline, callback);
+            ptr = this->parse_line(ptr, end, gline, callback);
         }
     }
 
@@ -95,18 +96,18 @@ public:
         { this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); }
 
     template<typename Callback>
-    const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback)
+    const char* parse_line(const char *ptr, const char *end, GCodeLine &gline, Callback &callback)
     {
         std::pair<const char*, const char*> cmd;
-        const char *end = parse_line_internal(ptr, gline, cmd);
+        const char *line_end = parse_line_internal(ptr, end, gline, cmd);
         callback(*this, gline);
         update_coordinates(gline, cmd);
-        return end;
+        return line_end;
     }
 
     template<typename Callback>
     void parse_line(const std::string &line, Callback callback)
-        { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
+        { GCodeLine gline; this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); }
 
     void parse_file(const std::string &file, callback_t callback);
     void quit_parsing() { m_parsing = false; }
@@ -127,7 +128,7 @@ public:
 //  void   set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
 
 private:
-    const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command);
+    const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command);
     void        update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
 
     static bool         is_whitespace(char c)           { return c == ' ' || c == '\t'; }

From 97d1fe35ad784d41f15dda896448d163e4d8e56a Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 17:22:28 +0200
Subject: [PATCH 10/32] G-code writer optimization: Don't use std::strstream,
 it is slow!

---
 src/libslic3r/GCodeWriter.cpp | 151 +++++++++++++++++++++++-----------
 1 file changed, 102 insertions(+), 49 deletions(-)

diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 24549fd89..3aecdfd1f 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -1,17 +1,21 @@
 #include "GCodeWriter.hpp"
 #include "CustomGCode.hpp"
 #include <algorithm>
+#include <charconv>
 #include <iomanip>
 #include <iostream>
 #include <map>
 #include <assert.h>
 
+#define XYZF_EXPORT_DIGITS 3
+#define E_EXPORT_DIGITS 5
+
 #define FLAVOR_IS(val) this->config.gcode_flavor == val
 #define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
 #define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
 #define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
-#define XYZF_NUM(val) PRECISION(val, 3)
-#define E_NUM(val) PRECISION(val, 5)
+#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
+#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
 
 namespace Slic3r {
 
@@ -288,16 +292,80 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
     return gcode.str();
 }
 
+class G1Writer {
+private:
+    static constexpr const size_t   buflen = 256;
+    char                            buf[buflen];
+    char                           *buf_end;
+    std::to_chars_result            ptr_err;
+
+public:
+    G1Writer() {
+        this->buf[0] = 'G';
+        this->buf[1] = '1';
+        this->buf_end = this->buf + buflen;
+        this->ptr_err.ptr = this->buf + 2;
+    }
+
+    void emit_axis(const char axis, const double v, size_t digits) {
+        *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis;
+        this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits);
+    }
+
+    void emit_xy(const Vec2d &point) {
+        this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+        this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+    }
+
+    void emit_xyz(const Vec3d &point) {
+        this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+        this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+        this->emit_z(point.z());
+    }
+
+    void emit_z(const double z) {
+        this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
+    }
+
+    void emit_e(const std::string &axis, double v) {
+        if (! axis.empty()) {
+            // not gcfNoExtrusion
+            this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
+        }
+    }
+
+    void emit_f(double speed) {
+        this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
+    }
+
+    void emit_string(const std::string &s) {
+        strncpy(ptr_err.ptr, s.c_str(), s.size());
+        ptr_err.ptr += s.size();
+    }
+
+    void emit_comment(bool allow_comments, const std::string &comment) {
+        if (allow_comments && ! comment.empty()) {
+            *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
+            this->emit_string(comment);
+        }
+    }
+
+    std::string string() {
+        *ptr_err.ptr ++ = '\n';
+        return std::string(this->buf, ptr_err.ptr - buf);
+    }
+};
+
 std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
 {
     assert(F > 0.);
     assert(F < 100000.);
-    std::ostringstream gcode;
-    gcode << "G1 F" << XYZF_NUM(F);
-    COMMENT(comment);
-    gcode << cooling_marker;
-    gcode << "\n";
-    return gcode.str();
+
+    G1Writer w;
+    w.emit_f(F);
+    w.emit_comment(this->config.gcode_comments, comment);
+    w.emit_string(cooling_marker);
+    return w.string();
 }
 
 std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
@@ -305,13 +373,11 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
     m_pos(0) = point(0);
     m_pos(1) = point(1);
     
-    std::ostringstream gcode;
-    gcode << "G1 X" << XYZF_NUM(point(0))
-          <<   " Y" << XYZF_NUM(point(1))
-          <<   " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
-    COMMENT(comment);
-    gcode << "\n";
-    return gcode.str();
+    G1Writer w;
+    w.emit_xy(point);
+    w.emit_f(this->config.travel_speed.value * 60.0);
+    w.emit_comment(this->config.gcode_comments, comment);
+    return w.string();
 }
 
 std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
@@ -340,14 +406,11 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
     m_lifted = 0;
     m_pos = point;
     
-    std::ostringstream gcode;
-    gcode << "G1 X" << XYZF_NUM(point(0))
-          <<   " Y" << XYZF_NUM(point(1))
-          <<   " Z" << XYZF_NUM(point(2))
-          <<   " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
-    COMMENT(comment);
-    gcode << "\n";
-    return gcode.str();
+    G1Writer w;
+    w.emit_xyz(point);
+    w.emit_f(this->config.travel_speed.value * 60.0);
+    w.emit_comment(this->config.gcode_comments, comment);
+    return w.string();
 }
 
 std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
@@ -377,12 +440,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
     if (speed == 0.)
         speed = this->config.travel_speed.value;
     
-    std::ostringstream gcode;
-    gcode << "G1 Z" << XYZF_NUM(z)
-          <<   " F" << XYZF_NUM(speed * 60.0);
-    COMMENT(comment);
-    gcode << "\n";
-    return gcode.str();
+    G1Writer w;
+    w.emit_z(z);
+    w.emit_f(speed * 60.0);
+    w.emit_comment(this->config.gcode_comments, comment);
+    return w.string();
 }
 
 bool GCodeWriter::will_move_z(double z) const
@@ -402,16 +464,12 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
     m_pos(0) = point(0);
     m_pos(1) = point(1);
     m_extruder->extrude(dE);
-    
-    std::ostringstream gcode;
-    gcode << "G1 X" << XYZF_NUM(point(0))
-          <<   " Y" << XYZF_NUM(point(1));
-    if (! m_extrusion_axis.empty())
-        // not gcfNoExtrusion
-        gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
-    COMMENT(comment);
-    gcode << "\n";
-    return gcode.str();
+
+    G1Writer w;
+    w.emit_xy(point);
+    w.emit_e(m_extrusion_axis, m_extruder->E());
+    w.emit_comment(this->config.gcode_comments, comment);
+    return w.string();
 }
 
 std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
@@ -420,16 +478,11 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
     m_lifted = 0;
     m_extruder->extrude(dE);
     
-    std::ostringstream gcode;
-    gcode << "G1 X" << XYZF_NUM(point(0))
-          <<   " Y" << XYZF_NUM(point(1))
-          <<   " Z" << XYZF_NUM(point(2));
-    if (! m_extrusion_axis.empty())
-        // not gcfNoExtrusion
-        gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
-    COMMENT(comment);
-    gcode << "\n";
-    return gcode.str();
+    G1Writer w;
+    w.emit_xyz(point);
+    w.emit_e(m_extrusion_axis, m_extruder->E());
+    w.emit_comment(this->config.gcode_comments, comment);
+    return w.string();
 }
 
 std::string GCodeWriter::retract(bool before_wipe)

From 86d06b0be98d0dc53f0afbbcf714475aa21f6e45 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Fri, 3 Sep 2021 17:22:53 +0200
Subject: [PATCH 11/32] G-code processor tiny change: In place initialization

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 0d71a19f5..73ee0d164 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -2916,7 +2916,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
         m_line_id + 1 :
         ((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
 
-    MoveVertex vertex = {
+    m_result.moves.push_back({
         m_last_line_id,
         type,
         m_extrusion_role,
@@ -2931,8 +2931,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
         m_fan_speed,
         m_extruder_temps[m_extruder_id],
         static_cast<float>(m_result.moves.size())
-    };
-    m_result.moves.emplace_back(vertex);
+    });
 
     // stores stop time placeholders for later use
     if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {

From d04ece92c379adc9eeb0bc4e42ee0d0610c30907 Mon Sep 17 00:00:00 2001
From: Jason Scurtu <scurtu@mail.de>
Date: Fri, 3 Sep 2021 22:20:12 +0200
Subject: [PATCH 12/32] folder not needed anymore.

---
 ...om.prusa3d.PrusaSlicer.GCodeViewer.desktop |  10 --
 .../flatpak/com.prusa3d.PrusaSlicer.desktop   |  20 ----
 .../com.prusa3d.PrusaSlicer.metainfo.xml      | 106 ------------------
 3 files changed, 136 deletions(-)
 delete mode 100644 src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop
 delete mode 100755 src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop
 delete mode 100755 src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml

diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop
deleted file mode 100644
index 317785473..000000000
--- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop
+++ /dev/null
@@ -1,10 +0,0 @@
-[Desktop Entry]
-Name=Prusa GCode viewer
-Name[de]=Prusa GCode Vorschau
-Exec=prusa-slicer --gcodeviewer %F
-Icon=com.prusa3d.PrusaSlicer.GCodeViewer
-Terminal=false
-Type=Application
-MimeType=text/x.gcode;
-Categories=Graphics;3DGraphics;
-Keywords=3D;Printing;Slicer;
diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop
deleted file mode 100755
index 1ea3e157e..000000000
--- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop
+++ /dev/null
@@ -1,20 +0,0 @@
-[Desktop Entry]
-Name=PrusaSlicer
-Name[de]=PrusaSlicer
-GenericName=3D Printing Software
-Icon=com.prusa3d.PrusaSlicer
-Exec=prusa-slicer %F
-Terminal=false
-Type=Application
-MimeType=model/stl;model/x-wavefront-obj;model/3mf;model/x-geomview-off;application/x-amf;
-Categories=Graphics;3DGraphics;Engineering;
-Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA
-StartupNotify=false
-StartupWMClass=prusa-slicer
-Actions=GCodeViewer;
-
-[Desktop Action GCodeViewer]
-Exec=prusa-slicer --gcodeviewer %F
-Name=G-Code Viewer
-Name[de]=G-Code Vorschau
-Icon=com.prusa3d.PrusaSlicer.GCodeViewer
diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml
deleted file mode 100755
index c37f43d69..000000000
--- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
-    <id>com.prusa3d.PrusaSlicer</id>
-    <launchable type="desktop-id">com.prusa3d.PrusaSlicer.desktop</launchable>
-    <provides>
-        <id>prusa-slicer.desktop</id>
-    </provides>
-    <name>PrusaSlicer</name>
-    <summary>Powerful 3D printing slicer optimized for Prusa printers</summary>
-    <metadata_license>0BSD</metadata_license>
-    <project_license>AGPL-3.0-only</project_license>
-    <description>
-        <p>
-            PrusaSlicer takes 3D models (STL, OBJ, AMF) and converts them into G-code
-            instructions for FFF printers or PNG layers for mSLA 3D printers. It's
-            compatible with any modern printer based on the RepRap toolchain, including all
-            those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works
-            with Mach3, LinuxCNC and Machinekit controllers.
-        </p>
-        <p>
-            PrusaSlicer is based on Slic3r by Alessandro Ranelucci and the RepRap community.
-        </p>
-        <p>
-            What are some of PrusaSlicer's main features?
-        </p>
-        <ul>
-            <li>multi-platform (Linux/Mac/Win) and packaged as standalone-app with no dependencies required</li>
-            <li>complete command-line interface to use it with no GUI</li>
-            <li>multi-material (multiple extruders) object printing</li>
-            <li>multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.)</li>
-            <li>ability to plate multiple objects having distinct print settings</li>
-            <li>multithread processing</li>
-            <li>STL auto-repair (tolerance for broken models)</li>
-            <li>wide automated unit testing</li>
-        </ul>
-    </description>
-    <url type="homepage">https://www.prusa3d.com/prusaslicer/</url>
-    <url type="help">https://help.prusa3d.com</url>
-    <url type="bugtracker">https://github.com/prusa3d/PrusaSlicer/issues</url>
-    <screenshots>
-        <screenshot type="default">
-            <image>https://user-images.githubusercontent.com/590307/78981854-24d07580-7b21-11ea-9441-77923534a659.png</image>
-        </screenshot>
-        <screenshot>
-            <image>https://user-images.githubusercontent.com/590307/78981860-2863fc80-7b21-11ea-8c2d-8ff79ced2578.png</image>
-        </screenshot>
-        <screenshot>
-            <image>https://user-images.githubusercontent.com/590307/78981862-28fc9300-7b21-11ea-9b0d-d03e16b709d3.png</image>
-        </screenshot>
-    </screenshots>
-    <content_rating type="oars-1.1" />
-    <releases>
-        <release version="2.4.0-alpha1" date="2021-09-02">
-            <description>
-                <p>This is the first alpha release of PrusaSlicer 2.4.0, introducing Multi-material painting tool, improved FDM supports and raft, 
-                Windows dark mode, Fuzzy Skin, per object Brim configuration, Negative volumes, automatic Color Print for signs, Shape Gallery, 
-                "Tip of the day" notifications, Model simplification, support for Marlin 2 acceleration control, support of 3DLabPrint airplane models, 
-                new 3rd party printer profiles and many more new features, improvements and bugfixes.</p>
-            </description>
-        </release>
-        <release version="2.3.3" date="2021-07-16">
-            <description>
-                <p>PrusaSlicer 2.3.3 is a patch release following PrusaSlicer 2.3.2 release, fixing an unfortunate bug in handling FDM multi-material 
-                project and configuration files #6711.</p>
-            </description>
-        </release>
-        <release version="2.3.2" date="2021-07-08">
-            <description>
-                <p>This is a final release of PrusaSlicer 2.3.2, following 2.3.2-beta and 2.3.2-rc. 
-                For the new features in the 2.3.2 series, please read the change logs of the beta and release candidate. 
-                The final release is functionally equal to the release candidate, with a single additional improvement:
-                Before installing profile updates, a configuration snapshot is taken. In rare circumstances when the current configuration is not consistent, 
-                taking a configuration snapshot fails. PrusaSlicer 2.3.2 newly informs about the issue and offers either to install the configuration updates 
-                even if taking the configuration snapshot failed or to abort update of the profiles.</p>
-            </description>
-        </release>
-        <release version="2.3.1" date="2021-04-19">
-            <description>
-                <p>This a final release of PrusaSlicer 2.3.1, introducing native builds for the new Apple Silicon MacBooks, Chrome OS support, performance improvements 
-                in G-code rendering, security fixes and new 3rd party printer profiles. For full changelog on PrusaSlicer 2.3.1 series, please check the PrusaSlicer 
-                2.3.1-rc change log.</p>
-            </description>
-        </release>
-        <release version="2.3.0" date="2021-01-11">
-            <description>
-                <p>This a final release of PrusaSlicer 2.3.0, introducing paint-on supports, ironing, monotonic infill, seam painting, adaptive and support cubic infill,
-                print time per feature analysis, standalone G-code viewer application, better auto-arrange with a customizable gap and rotation,
-                search function for the settings, reworked "Avoid crossing perimeters" function, physical printers (network settings),
-                many new 3rd party printer profiles and much, much more.</p>
-            </description>
-        </release>
-        <release version="2.2.0-1" date="2020-04-27">
-            <description>
-                <p>Development/Unstable Snapshot</p>
-            </description>
-        </release>
-        <release version="2.2.0" date="2020-03-21">
-            <description>
-                <p>This is final release of PrusaSlicer 2.2.0 introducing SLA hollowing and hole drilling, support for 3rd party printer vendors, 3Dconnexion support, 
-                automatic variable layer height, macOS dark mode support, greatly improved ColorPrint feature and much, much more.
-                Several bugs found in the previous release candidate are fixed in this final release. See the respective change logs of the previous releases for all the 
-                new features, improvements and bugfixes in the 2.2.0 series.</p>
-            </description>
-        </release>
-  </releases>
-</component>

From ad41c3f01c94f888e500462e4127574f1fc60766 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Mon, 6 Sep 2021 13:15:36 +0200
Subject: [PATCH 13/32] Tech ENABLE_SEAMS_USING_MODELS - Fixed crash when
 enabling visualization of tool change markers in preview

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

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 656a67406..8c2ef573f 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -2399,7 +2399,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
 
         buffer.model.instances.render_ranges.reset();
 
-        if (!buffer.visible)
+        if (!buffer.visible || buffer.model.instances.s_ids.empty())
             continue;
 
         buffer.model.instances.render_ranges.ranges.push_back({ 0, 0, 0, buffer.model.color });

From 7b4c98d72704a86acc99eb57837fd3bcb941f981 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Mon, 6 Sep 2021 14:31:10 +0200
Subject: [PATCH 14/32] #6828 - Clamping of toolpaths width performed only for
 gcodes files produced by 3rd part softwares (Tech
 ENABLE_CLAMP_TOOLPATHS_WIDTH)

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 6 ++++++
 src/libslic3r/Technologies.hpp         | 9 +++++++++
 2 files changed, 15 insertions(+)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 73ee0d164..c4f262675 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -2252,8 +2252,14 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
             // cross section: rectangle + 2 semicircles
             m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
 
+#if ENABLE_CLAMP_TOOLPATHS_WIDTH
+        if (m_producers_enabled && m_producer != EProducer::PrusaSlicer)
+            // clamp width to avoid artifacts which may arise from wrong values of m_height
+            m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
+#else
         // clamp width to avoid artifacts which may arise from wrong values of m_height
         m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
+#endif // ENABLE_CLAMP_TOOLPATHS_WIDTH
 
 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
         m_width_compare.update(m_width, m_extrusion_role);
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 6132430f1..405119fc0 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -57,4 +57,13 @@
 #define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA0)
 
 
+//====================
+// 2.4.0.alpha2 techs
+//====================
+#define ENABLE_2_4_0_ALPHA2 1
+
+// Enable clamping toolpaths width only for gcodes files produced by 3rd part softwares
+#define ENABLE_CLAMP_TOOLPATHS_WIDTH (1 && ENABLE_2_4_0_ALPHA2)
+
+
 #endif // _prusaslicer_technologies_h_

From ce0beabb434b80d8db9117a8db3f056e63bd8bd7 Mon Sep 17 00:00:00 2001
From: Jason Scurtu <scurtu@mail.de>
Date: Fri, 3 Sep 2021 11:56:04 +0200
Subject: [PATCH 15/32] add new mac icon and update MainFrame.cpp

---
 resources/icons/PrusaSlicer-mac_128px.png | Bin 0 -> 6890 bytes
 src/slic3r/GUI/MainFrame.cpp              |   2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 resources/icons/PrusaSlicer-mac_128px.png

diff --git a/resources/icons/PrusaSlicer-mac_128px.png b/resources/icons/PrusaSlicer-mac_128px.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff0b093beb1d3c6b8cf7e215397a654e03549cde
GIT binary patch
literal 6890
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

literal 0
HcmV?d00001

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index eb309b1af..687333f19 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -142,7 +142,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
     default:
     case GUI_App::EAppMode::Editor:
         m_taskbar_icon = std::make_unique<PrusaSlicerTaskBarIcon>(wxTBI_DOCK);
-        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
+        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-mac_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
         break;
     case GUI_App::EAppMode::GCodeViewer:
         m_taskbar_icon = std::make_unique<GCodeViewerTaskBarIcon>(wxTBI_DOCK);

From 07bdb5fbe3df35f18e7c800b136d7da52c2a1fcc Mon Sep 17 00:00:00 2001
From: Jason Scurtu <scurtu@mail.de>
Date: Fri, 3 Sep 2021 12:08:24 +0200
Subject: [PATCH 16/32] add gcodeviewer

---
 .../icons/PrusaSlicer-gcodeviewer-mac-128px.png | Bin 0 -> 12218 bytes
 src/slic3r/GUI/MainFrame.cpp                    |   2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 resources/icons/PrusaSlicer-gcodeviewer-mac-128px.png

diff --git a/resources/icons/PrusaSlicer-gcodeviewer-mac-128px.png b/resources/icons/PrusaSlicer-gcodeviewer-mac-128px.png
new file mode 100644
index 0000000000000000000000000000000000000000..b62f3fddc84e662f1b011cc19aad7128781cb3ca
GIT binary patch
literal 12218
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
KAYLVE82BGiN2+}Q

literal 0
HcmV?d00001

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 687333f19..31acc1c57 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -146,7 +146,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
         break;
     case GUI_App::EAppMode::GCodeViewer:
         m_taskbar_icon = std::make_unique<GCodeViewerTaskBarIcon>(wxTBI_DOCK);
-        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
+        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer-mac-128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
         break;
     }
 #endif // __APPLE__

From b41e3214736ad4fdf8789f39db7a8e206a54807b Mon Sep 17 00:00:00 2001
From: Jason Scurtu <scurtu@mail.de>
Date: Fri, 3 Sep 2021 12:09:32 +0200
Subject: [PATCH 17/32] rename

---
 ...px.png => PrusaSlicer-gcodeviewer-mac_128px.png} | Bin
 src/slic3r/GUI/MainFrame.cpp                        |   2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename resources/icons/{PrusaSlicer-gcodeviewer-mac-128px.png => PrusaSlicer-gcodeviewer-mac_128px.png} (100%)

diff --git a/resources/icons/PrusaSlicer-gcodeviewer-mac-128px.png b/resources/icons/PrusaSlicer-gcodeviewer-mac_128px.png
similarity index 100%
rename from resources/icons/PrusaSlicer-gcodeviewer-mac-128px.png
rename to resources/icons/PrusaSlicer-gcodeviewer-mac_128px.png
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 31acc1c57..51dbc201d 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -146,7 +146,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
         break;
     case GUI_App::EAppMode::GCodeViewer:
         m_taskbar_icon = std::make_unique<GCodeViewerTaskBarIcon>(wxTBI_DOCK);
-        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer-mac-128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
+        m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer-mac_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
         break;
     }
 #endif // __APPLE__

From 428509ac00885348cf5455ae1b6eadfd0e96f6ca Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Mon, 6 Sep 2021 18:08:13 +0200
Subject: [PATCH 18/32] Pimped up
 GCodeProcessor::TimeProcessor::post_process(): replaced implicit lambda
 capture with explicit listing of captured context for readability and code
 correctness. Captured this as const.

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index c4f262675..e375179e2 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -476,11 +476,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
         g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
 
     // add lines M73 to exported gcode
-    auto process_line_G1 = [&]() {
+    auto process_line_G1 = [
+        // Lambdas, mostly for string formatting, all with an empty capture block.
+        time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
+        &self = std::as_const(*this),
+        // Caches, to be modified
+        &g1_times_cache_it, &last_exported_main, &last_exported_stop,
+        // String output
+        &export_line]
+        (const size_t g1_lines_counter) {
         unsigned int exported_lines_count = 0;
-        if (export_remaining_time_enabled) {
+        if (self.export_remaining_time_enabled) {
             for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
-                const TimeMachine& machine = machines[i];
+                const TimeMachine& machine = self.machines[i];
                 if (machine.enabled) {
                     // export pair <percent, remaining time>
                     // Skip all machine.g1_times_cache below g1_lines_counter.
@@ -581,8 +589,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
             parser.parse_line(gcode_line,
                 [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
                     if (line.cmd_is("G1")) {
-                        unsigned int extra_lines_count = process_line_G1();
-                        ++g1_lines_counter;
+                        unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++);
                         if (extra_lines_count > 0)
                             offsets.push_back({ line_id, extra_lines_count });
                     }

From 0bc77cef3ec30075b14a01702e3a3877964516a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= <hejl.lukas@gmail.com>
Date: Tue, 7 Sep 2021 07:33:57 +0200
Subject: [PATCH 19/32] Fixed build on Linux and macOS that was failing because
 of using std::to_chars and std::from_chars with floating-point values.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The old version of GCC and Clang support only integers to be passed to std::to_chars and std::from_chars. macOS older version of Clang doesn't support std::from_chars at all. So for Linux and macOS, it was replaced std::from_chars with strtod and temporarily was replace std::to_chars with snprintf.
---
 src/libslic3r/GCodeReader.cpp | 7 +++++++
 src/libslic3r/GCodeWriter.cpp | 9 +++++++++
 2 files changed, 16 insertions(+)

diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index 54e6802e6..aa5268539 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -71,9 +71,16 @@ const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, G
             }
             if (axis != NUM_AXES_WITH_UNKNOWN) {
                 // Try to parse the numeric value.
+#ifdef WIN32
                 double v;
                 auto [pend, ec] = std::from_chars(++ c, end, v);
                 if (pend != c && is_end_of_word(*pend)) {
+#else
+                // The older version of GCC and Clang support std::from_chars just for integers, so strtod we used it instead.
+                char   *pend = nullptr;
+                double  v = strtod(++ c, &pend);
+                if (pend != nullptr && is_end_of_word(*pend)) {
+#endif
                     // The axis value has been parsed correctly.
                     if (axis != UNKNOWN_AXIS)
 	                    gline.m_axis[int(axis)] = float(v);
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 3aecdfd1f..c97180982 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -309,7 +309,16 @@ public:
 
     void emit_axis(const char axis, const double v, size_t digits) {
         *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis;
+#ifdef WIN32
         this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits);
+#else
+        int buf_capacity = int(this->buf_end - this->ptr_err.ptr);
+        int ret          = snprintf(this->ptr_err.ptr, buf_capacity, "%.*lf", int(digits), v);
+        if (ret <= 0 || ret > buf_capacity)
+            ptr_err.ec = std::errc::value_too_large;
+        else
+            this->ptr_err.ptr = this->ptr_err.ptr + ret;
+#endif
     }
 
     void emit_xy(const Vec2d &point) {

From d35183921b0696effd194a0cb47cce83020f5efc Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 7 Sep 2021 09:41:14 +0200
Subject: [PATCH 20/32] Revert of 7b4c98d72704a86acc99eb57837fd3bcb941f981,
 clamp toolpaths widths increased to 2mm and added default values for
 toolpaths width and height

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 26 +++++++++++++-------------
 src/libslic3r/Technologies.hpp         |  9 ---------
 2 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index e375179e2..2c58c2a25 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -21,6 +21,9 @@
 
 #include <chrono>
 
+static const float DEFAULT_TOOLPATH_WIDTH = 0.4f;
+static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
+
 static const float INCHES_TO_MM = 25.4f;
 static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
 static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
@@ -2217,9 +2220,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
         return;
 
     EMoveType type = move_type(delta_pos);
-    if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f)
-        type = EMoveType::Travel;
-
     if (type == EMoveType::Extrude) {
         float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
         float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
@@ -2243,6 +2243,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
             }
         }
 
+        if (m_height == 0.0f)
+            m_height = DEFAULT_TOOLPATH_HEIGHT;
+
+        if (m_end_position[Z] == 0.0f)
+            m_end_position[Z] = m_height;
+
 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
         m_height_compare.update(m_height, m_extrusion_role);
 #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
@@ -2259,23 +2265,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
             // cross section: rectangle + 2 semicircles
             m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
 
-#if ENABLE_CLAMP_TOOLPATHS_WIDTH
-        if (m_producers_enabled && m_producer != EProducer::PrusaSlicer)
-            // clamp width to avoid artifacts which may arise from wrong values of m_height
-            m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
-#else
+        if (m_width == 0.0f)
+            m_width = DEFAULT_TOOLPATH_WIDTH;
+
         // clamp width to avoid artifacts which may arise from wrong values of m_height
-        m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
-#endif // ENABLE_CLAMP_TOOLPATHS_WIDTH
+        m_width = std::min(m_width, std::max(2.0f, 4.0f * m_height));
 
 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
         m_width_compare.update(m_width, m_extrusion_role);
 #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
     }
 
-    if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
-        type = EMoveType::Travel;
-
     // time estimate section
     auto move_length = [](const AxisCoords& delta_pos) {
         float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]);
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 405119fc0..6132430f1 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -57,13 +57,4 @@
 #define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA0)
 
 
-//====================
-// 2.4.0.alpha2 techs
-//====================
-#define ENABLE_2_4_0_ALPHA2 1
-
-// Enable clamping toolpaths width only for gcodes files produced by 3rd part softwares
-#define ENABLE_CLAMP_TOOLPATHS_WIDTH (1 && ENABLE_2_4_0_ALPHA2)
-
-
 #endif // _prusaslicer_technologies_h_

From 5c9bc26ed5efee227dad831e89702f331a5891b5 Mon Sep 17 00:00:00 2001
From: Justin Schuh <jschuh@users.noreply.github.com>
Date: Tue, 7 Sep 2021 00:45:10 -0700
Subject: [PATCH 21/32] Replace OpenProcess with GetCurrentProcess where
 appropriate (#6914)

GetCurrentProcess is more correct and resolves spurious AV warnings.
---
 src/libslic3r/BlacklistedLibraryCheck.cpp | 11 ++++-------
 src/libslic3r/utils.cpp                   | 15 +++++----------
 2 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp
index d9dea188f..76f675c70 100644
--- a/src/libslic3r/BlacklistedLibraryCheck.cpp
+++ b/src/libslic3r/BlacklistedLibraryCheck.cpp
@@ -33,22 +33,20 @@ std::wstring BlacklistedLibraryCheck::get_blacklisted_string()
 
 bool BlacklistedLibraryCheck::perform_check()
 {   
-    // Get a handle to the process.
-    HANDLE  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
-    if (NULL == hProcess)
-        return false;
+    // Get the pseudo-handle for the current process.
+    HANDLE  hCurrentProcess = GetCurrentProcess();
 
     // Get a list of all the modules in this process.
     HMODULE hMods[1024];
     DWORD   cbNeeded;
-    if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
+    if (EnumProcessModulesEx(hCurrentProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
     {
         //printf("Total Dlls: %d\n", cbNeeded / sizeof(HMODULE));
         for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); ++ i)
         {
             wchar_t szModName[MAX_PATH];
             // Get the full path to the module's file.
-            if (GetModuleFileNameExW(hProcess, hMods[i], szModName, MAX_PATH))
+            if (GetModuleFileNameExW(hCurrentProcess, hMods[i], szModName, MAX_PATH))
             {
                 // Add to list if blacklisted
                 if (BlacklistedLibraryCheck::is_blacklisted(szModName)) {
@@ -61,7 +59,6 @@ bool BlacklistedLibraryCheck::perform_check()
         }
     }
 
-    CloseHandle(hProcess);
     //printf("\n");
     return !m_found.empty();
 }
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index c330f34b2..c5dbdac9c 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -984,16 +984,11 @@ std::string log_memory_info(bool ignore_loglevel)
         } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX;
     #endif /* PROCESS_MEMORY_COUNTERS_EX */
 
-
-        HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
-        if (hProcess != nullptr) {
-            PROCESS_MEMORY_COUNTERS_EX pmc;
-            if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
-                out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
-            else
-                out += " Used memory: N/A";
-            CloseHandle(hProcess);
-        }
+        PROCESS_MEMORY_COUNTERS_EX pmc;
+        if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
+            out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
+        else
+            out += " Used memory: N/A";
 #elif defined(__linux__) or defined(__APPLE__)
         // Get current memory usage.
     #ifdef __APPLE__

From c1c8a60271083e3577a150b19042c052787a5891 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 7 Sep 2021 11:13:12 +0200
Subject: [PATCH 22/32] Changed color for toolpaths whose extrusion role is
 unknown

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

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 8c2ef573f..49fd17ce8 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -503,7 +503,7 @@ void GCodeViewer::SequentialView::render(float legend_height) const
 }
 
 const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
-    { 0.75f, 0.75f, 0.75f, 1.0f },   // erNone
+    { 0.90f, 0.70f, 0.70f, 1.0f },   // erNone
     { 1.00f, 0.90f, 0.30f, 1.0f },   // erPerimeter
     { 1.00f, 0.49f, 0.22f, 1.0f },   // erExternalPerimeter
     { 0.12f, 0.12f, 1.00f, 1.0f },   // erOverhangPerimeter

From 771a8927763f312401fa73b92cb950c31d2fd89b Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 11:18:07 +0200
Subject: [PATCH 23/32] Optimization of GCodeProcessor for speed.

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 306 +++++++++++++++++--------
 src/libslic3r/GCodeReader.cpp          |  12 +-
 src/libslic3r/GCodeReader.hpp          |  15 +-
 3 files changed, 232 insertions(+), 101 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 2c58c2a25..142dd015d 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -360,7 +360,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
         throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
 
     auto time_in_minutes = [](float time_in_seconds) {
-        return int(::roundf(time_in_seconds / 60.0f));
+        assert(time_in_seconds >= 0.f);
+        return int((time_in_seconds + 0.5f) / 60.0f);
     };
 
     auto time_in_last_minute = [](float time_in_seconds) {
@@ -392,7 +393,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
         return std::string(line_M73);
     };
 
-    GCodeReader parser;
     std::string gcode_line;
     size_t g1_lines_counter = 0;
     // keeps track of last exported pair <percent, remaining time>
@@ -411,11 +411,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
     std::string export_line;
 
     // replace placeholder lines with the proper final value
-    auto process_placeholders = [&](const std::string& gcode_line) {
+    // gcode_line is in/out parameter, to reduce expensive memory allocation
+    auto process_placeholders = [&](std::string& gcode_line) {
         unsigned int extra_lines_count = 0;
 
         // remove trailing '\n'
-        std::string line = gcode_line.substr(0, gcode_line.length() - 1);
+        auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
 
         std::string ret;
         if (line.length() > 1) {
@@ -456,7 +457,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
             }
         }
 
-        return std::tuple(!ret.empty(), ret.empty() ? gcode_line : ret, (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
+        if (! ret.empty())
+            // Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
+            gcode_line = ret;
+        return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
     };
 
     // check for temporary lines
@@ -569,39 +573,56 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
     unsigned int line_id = 0;
     std::vector<std::pair<unsigned int, unsigned int>> offsets;
 
-    while (std::getline(in, gcode_line)) {
-        if (!in.good()) {
-            fclose(out);
-            throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
-        }
+    {
+        // Read the input stream 64kB at a time, extract lines and process them.
+        in.sync_with_stdio(false);
+        std::vector<char> buffer(65536 * 10, 0);
+        // Line buffer.
+        assert(gcode_line.empty());
+        while (! in.eof()) {
+            in.read(buffer.data(), buffer.size());
+            if (! in.eof() && ! in.good()) {
+                fclose(out);
+                throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
+            }
+            auto it        = buffer.begin();
+            auto it_bufend = buffer.begin() + in.gcount();
+            while (it != it_bufend) {
+                // Find end of line.
+                bool eol    = false;
+                auto it_end = it;
+                for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
+                // End of line is indicated also if end of file was reached.
+                eol |= in.eof() && it_end == it_bufend;
+                gcode_line.insert(gcode_line.end(), it, it_end);
+                if (eol) {
+                    ++line_id;
 
-        ++line_id;
-
-        gcode_line += "\n";
-        // replace placeholder lines
-        auto [processed, result, lines_added_count] = process_placeholders(gcode_line);
-        if (processed && lines_added_count > 0)
-            offsets.push_back({ line_id, lines_added_count });
-        gcode_line = result;
-        if (!processed) {
-            // remove temporary lines
-            if (is_temporary_decoration(gcode_line))
-                continue;
-
-            // add lines M73 where needed
-            parser.parse_line(gcode_line,
-                [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
-                    if (line.cmd_is("G1")) {
+                    gcode_line += "\n";
+                    // replace placeholder lines
+                    auto [processed, lines_added_count] = process_placeholders(gcode_line);
+                    if (processed && lines_added_count > 0)
+                        offsets.push_back({ line_id, lines_added_count });
+                    if (! processed && ! is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
+                        // remove temporary lines, add lines M73 where needed
                         unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++);
                         if (extra_lines_count > 0)
                             offsets.push_back({ line_id, extra_lines_count });
                     }
-                });
-        }
 
-        export_line += gcode_line;
-        if (export_line.length() > 65535)
-            write_string(export_line);
+                    export_line += gcode_line;
+                    if (export_line.length() > 65535)
+                        write_string(export_line);
+                    gcode_line.clear();
+                }
+                // Skip EOL.
+                it = it_end; 
+                if (it != it_bufend && *it == '\r')
+                    ++ it;
+                if (it != it_bufend && *it == '\n')
+                    ++ it;
+            }
+        }
     }
 
     if (!export_line.empty())
@@ -1171,8 +1192,6 @@ void GCodeProcessor::reset()
 
 void GCodeProcessor::process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback)
 {
-    auto last_cancel_callback_time = std::chrono::high_resolution_clock::now();
-
     CNumericLocalesSetter locales_setter;
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -1184,7 +1203,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
     if (m_producers_enabled) {
         m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
             const std::string_view cmd = line.cmd();
-            if (cmd.length() == 0) {
+            if (cmd.empty()) {
                 const std::string_view comment = line.comment();
                 if (comment.length() > 1 && detect_producer(comment))
                     m_parser.quit_parsing();
@@ -1211,17 +1230,15 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
     m_result.id = ++s_result_id;
     // 1st move must be a dummy move
     m_result.moves.emplace_back(MoveVertex());
-    m_parser.parse_file(filename, [this, cancel_callback, &last_cancel_callback_time](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
-        if (cancel_callback != nullptr) {
-            // call the cancel callback every 100 ms
-            auto curr_time = std::chrono::high_resolution_clock::now();
-            if (std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - last_cancel_callback_time).count() > 100) {
-                cancel_callback();
-                last_cancel_callback_time = curr_time;
-            }
+    size_t parse_line_callback_cntr = 10000;
+    m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
+        if (-- parse_line_callback_cntr == 0) {
+            // Don't call the cancel_callback() too often, do it every at every 10000'th line.
+            parse_line_callback_cntr = 10000;
+            cancel_callback();
         }
-        process_gcode_line(line);
-        });
+        this->process_gcode_line(line);
+    });
 
     // update width/height of wipe moves
     for (MoveVertex& move : m_result.moves) {
@@ -1401,61 +1418,170 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
     const std::string_view cmd = line.cmd();
     if (cmd.length() > 1) {
         // process command lines
-        switch (::toupper(cmd[0]))
+        switch (cmd[0])
         {
+        case 'g':
         case 'G':
-            {
-                switch (::atoi(&cmd[1]))
-                {
-                case 0:  { process_G0(line); break; }  // Move
-                case 1:  { process_G1(line); break; }  // Move
-                case 10: { process_G10(line); break; } // Retract
-                case 11: { process_G11(line); break; } // Unretract
-                case 20: { process_G20(line); break; } // Set Units to Inches
-                case 21: { process_G21(line); break; } // Set Units to Millimeters
-                case 22: { process_G22(line); break; } // Firmware controlled retract
-                case 23: { process_G23(line); break; } // Firmware controlled unretract
-                case 28: { process_G28(line); break; } // Move to origin
-                case 90: { process_G90(line); break; } // Set to Absolute Positioning
-                case 91: { process_G91(line); break; } // Set to Relative Positioning
-                case 92: { process_G92(line); break; } // Set Position
-                default: { break; }
+            switch (cmd.size()) {
+            case 2:
+                switch (cmd[1]) {
+                case '0': { process_G0(line); break; }  // Move
+                case '1': { process_G1(line); break; }  // Move
+                default: break;
                 }
                 break;
+            case 3:
+                switch (cmd[1]) {
+                case '1':
+                    switch (cmd[2]) {
+                    case '0': { process_G10(line); break; } // Retract
+                    case '1': { process_G11(line); break; } // Unretract
+                    default: break;
+                    }
+                    break;
+                case '2':
+                    switch (cmd[2]) {
+                    case '0': { process_G20(line); break; } // Set Units to Inches
+                    case '1': { process_G21(line); break; } // Set Units to Millimeters
+                    case '2': { process_G22(line); break; } // Firmware controlled retract
+                    case '3': { process_G23(line); break; } // Firmware controlled unretract
+                    case '8': { process_G28(line); break; } // Move to origin
+                    default: break;
+                    }
+                    break;
+                case '9':
+                    switch (cmd[2]) {
+                    case '0': { process_G90(line); break; } // Set to Absolute Positioning
+                    case '1': { process_G91(line); break; } // Set to Relative Positioning
+                    case '2': { process_G92(line); break; } // Set Position
+                    default: break;
+                    }
+                    break;
+                }
+                break;
+            default:
+                break;
             }
+            break;
+        case 'm':
         case 'M':
-            {
-                switch (::atoi(&cmd[1]))
-                {
-                case 1:   { process_M1(line); break; }   // Sleep or Conditional stop
-                case 82:  { process_M82(line); break; }  // Set extruder to absolute mode
-                case 83:  { process_M83(line); break; }  // Set extruder to relative mode
-                case 104: { process_M104(line); break; } // Set extruder temperature
-                case 106: { process_M106(line); break; } // Set fan speed
-                case 107: { process_M107(line); break; } // Disable fan
-                case 108: { process_M108(line); break; } // Set tool (Sailfish)
-                case 109: { process_M109(line); break; } // Set extruder temperature and wait
-                case 132: { process_M132(line); break; } // Recall stored home offsets
-                case 135: { process_M135(line); break; } // Set tool (MakerWare)
-                case 201: { process_M201(line); break; } // Set max printing acceleration
-                case 203: { process_M203(line); break; } // Set maximum feedrate
-                case 204: { process_M204(line); break; } // Set default acceleration
-                case 205: { process_M205(line); break; } // Advanced settings
-                case 221: { process_M221(line); break; } // Set extrude factor override percentage
-                case 401: { process_M401(line); break; } // Repetier: Store x, y and z position
-                case 402: { process_M402(line); break; } // Repetier: Go to stored position
-                case 566: { process_M566(line); break; } // Set allowable instantaneous speed change
-                case 702: { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
-                default: { break; }
+            switch (cmd.size()) {
+            case 2:
+                switch (cmd[1]) {
+                case '1': { process_M1(line); break; }   // Sleep or Conditional stop
+                default: break;
                 }
                 break;
-            }
-        case 'T':
-            {
-                process_T(line); // Select Tool
+            case 3:
+                switch (cmd[1]) {
+                case '8':
+                    switch (cmd[2]) {
+                    case '2': { process_M82(line); break; }  // Set extruder to absolute mode
+                    case '3': { process_M83(line); break; }  // Set extruder to relative mode
+                    default: break;
+                    }
+                    break;
+                default:
+                    break;
+                }
+                break;
+            case 4:
+                switch (cmd[1]) {
+                case '1':
+                    switch (cmd[2]) {
+                    case '0':
+                        switch (cmd[3]) {
+                        case '4': { process_M104(line); break; } // Set extruder temperature
+                        case '6': { process_M106(line); break; } // Set fan speed
+                        case '7': { process_M107(line); break; } // Disable fan
+                        case '8': { process_M108(line); break; } // Set tool (Sailfish)
+                        case '9': { process_M109(line); break; } // Set extruder temperature and wait
+                        default: break;
+                        }
+                        break;
+                    case '3':
+                        switch (cmd[3]) {
+                        case '2': { process_M132(line); break; } // Recall stored home offsets
+                        case '5': { process_M135(line); break; } // Set tool (MakerWare)
+                        default: break;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case '2':
+                    switch (cmd[2]) {
+                    case '0':
+                        switch (cmd[3]) {
+                        case '1': { process_M201(line); break; } // Set max printing acceleration
+                        case '3': { process_M203(line); break; } // Set maximum feedrate
+                        case '4': { process_M204(line); break; } // Set default acceleration
+                        case '5': { process_M205(line); break; } // Advanced settings
+                        default: break;
+                        }
+                        break;
+                    case '2':
+                        switch (cmd[3]) {
+                        case '1': { process_M221(line); break; } // Set extrude factor override percentage
+                        default: break;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case '4':
+                    switch (cmd[2]) {
+                    case '0':
+                        switch (cmd[3]) {
+                        case '1': { process_M401(line); break; } // Repetier: Store x, y and z position
+                        case '2': { process_M402(line); break; } // Repetier: Go to stored position
+                        default: break;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case '5':
+                    switch (cmd[2]) {
+                    case '6':
+                        switch (cmd[3]) {
+                        case '6': { process_M566(line); break; } // Set allowable instantaneous speed change
+                        default: break;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case '7':
+                    switch (cmd[2]) {
+                    case '0':
+                        switch (cmd[3]) {
+                        case '2': { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
+                        default: break;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                default:
+                    break;
+                }
+                break;
+            default:
                 break;
             }
-        default: { break; }
+            break;
+        case 't':
+        case 'T':
+            process_T(line); // Select Tool
+            break;
+        default:
+            break;
         }
     }
     else {
diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index aa5268539..4c4bebee4 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -133,7 +133,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
     }
 }
 
-void GCodeReader::parse_file(const std::string &file, callback_t callback)
+bool GCodeReader::parse_file(const std::string &file, callback_t callback)
 {
     boost::nowide::ifstream f(file);
     f.sync_with_stdio(false);
@@ -141,17 +141,18 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
     std::string line;
     m_parsing = true;
     GCodeLine gline;
-    bool eof = false;
-    while (m_parsing && ! eof) {
+    while (m_parsing && ! f.eof()) {
         f.read(buffer.data(), buffer.size());
+        if (! f.eof() && ! f.good())
+            // Reading the input file failed.
+            return false;
         auto it        = buffer.begin();
         auto it_bufend = buffer.begin() + f.gcount();
-        eof = ! f.good();
         while (it != it_bufend) {
             bool eol = false;
             auto it_end = it;
             for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
-            eol |= eof && it_end == it_bufend;
+            eol |= f.eof() && it_end == it_bufend;
             if (eol) {
                 gline.reset();
                 if (line.empty())
@@ -167,6 +168,7 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
             for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
         }
     }
+    return true;
 }
 
 bool GCodeReader::GCodeLine::has(char axis) const
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 88c9df284..8c820b95d 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -44,11 +44,7 @@ public:
             float y = this->has(Y) ? (this->y() - reader.y()) : 0;
             return sqrt(x*x + y*y);
         }
-        bool cmd_is(const char *cmd_test) const {
-            const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
-            size_t len = strlen(cmd_test); 
-            return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
-        }
+        bool cmd_is(const char *cmd_test)          const { return cmd_is(m_raw, cmd_test); }
         bool extruding(const GCodeReader &reader)  const { return this->cmd_is("G1") && this->dist_E(reader) > 0; }
         bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; }
         bool travel()     const { return this->cmd_is("G1") && ! this->has(E); }
@@ -66,6 +62,12 @@ public:
         float e() const { return m_axis[E]; }
         float f() const { return m_axis[F]; }
 
+        static bool cmd_is(const std::string &gcode_line, const char *cmd_test) {
+            const char *cmd = GCodeReader::skip_whitespaces(gcode_line.c_str());
+            size_t len = strlen(cmd_test); 
+            return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
+        }
+
     private:
         std::string      m_raw;
         float            m_axis[NUM_AXES];
@@ -109,7 +111,8 @@ public:
     void parse_line(const std::string &line, Callback callback)
         { GCodeLine gline; this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); }
 
-    void parse_file(const std::string &file, callback_t callback);
+    // Returns false if reading the file failed.
+    bool parse_file(const std::string &file, callback_t callback);
     void quit_parsing() { m_parsing = false; }
 
     float& x()       { return m_position[X]; }

From af20419ed4997bc08f419b13dead78eb905a10b3 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Sep 2021 11:18:58 +0200
Subject: [PATCH 24/32] Fix for #6903 - Compare presets window titles
 inaccurate...

---
 src/slic3r/GUI/UnsavedChangesDialog.cpp | 16 +++++++++++-----
 src/slic3r/GUI/UnsavedChangesDialog.hpp |  3 ++-
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index 2b63bc79b..ef480f60f 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -696,7 +696,12 @@ void DiffViewCtrl::context_menu(wxDataViewEvent& event)
     auto it = m_items_map.find(item);
     if (it == m_items_map.end() || !it->second.is_long)
         return;
-    FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal();
+
+    size_t column_cnt = this->GetColumnCount();
+    const wxString old_value_header = this->GetColumn(column_cnt - 2)->GetTitle();
+    const wxString new_value_header = this->GetColumn(column_cnt - 1)->GetTitle();
+    FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val,
+                      old_value_header, new_value_header).ShowModal();
 
 #ifdef __WXOSX__
     wxWindow* parent = this->GetParent();
@@ -1281,7 +1286,8 @@ void UnsavedChangesDialog::on_sys_color_changed()
 //          FullCompareDialog
 //------------------------------------------
 
-FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value)
+FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value,
+                                     const wxString& old_value_header, const wxString& new_value_header)
     : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     wxGetApp().UpdateDarkUI(this);
@@ -1302,8 +1308,8 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
         grid_sizer->Add(text, 0, wxALL, border);
     };
 
-    add_header(_L("Old value"));
-    add_header(_L("New value"));
+    add_header(old_value_header);
+    add_header(new_value_header);
 
     auto get_set_from_val = [](wxString str) {
         if (str.Find("\n") == wxNOT_FOUND)
@@ -1327,7 +1333,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
     std::set_difference(new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(new_old_diff_set, new_old_diff_set.begin()));
 
     auto add_value = [grid_sizer, border, this](wxString label, const std::set<wxString>& diff_set, bool is_colored = false) {
-        wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_SIMPLE | wxTE_RICH);
+        wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_DEFAULT | wxTE_RICH);
         wxGetApp().UpdateDarkUI(text);
         text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont()));
 
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp
index 966743033..9dbaf6e99 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.hpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp
@@ -299,7 +299,8 @@ protected:
 class FullCompareDialog : public wxDialog
 {
 public:
-    FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value);
+    FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value,
+                      const wxString& old_value_header, const wxString& new_value_header);
     ~FullCompareDialog() {}
 };
 

From 761c06ed9282c76475ab9986cd0cd0a5c340022d Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 7 Sep 2021 12:25:30 +0200
Subject: [PATCH 25/32] GCodeViewer - Fixed crash when importing gcode
 generated with Simplify3D and switching to tool view

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

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 2c58c2a25..4b38020c1 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -961,10 +961,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
     }
 
     // replace missing values with default
-    std::string default_color = "#FF8000";
     for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
         if (m_result.extruder_colors[i].empty())
-            m_result.extruder_colors[i] = default_color;
+            m_result.extruder_colors[i] = "#FF8000";
     }
 
     m_extruder_colors.resize(m_result.extruder_colors.size());
@@ -1350,7 +1349,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
                 if (pos != cmt.npos) {
                     std::string data_str = cmt.substr(pos + 1);
                     std::vector<std::string> values_str;
-                    boost::split(values_str, data_str, boost::is_any_of("|"), boost::token_compress_on);
+                    boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
                     for (const std::string& s : values_str) {
                         out.emplace_back(static_cast<float>(string_to_double_decimal_point(s)));
                     }
@@ -1374,10 +1373,16 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
                 m_result.filament_densities.clear();
                 extract_floats(comment, "filamentDensities", m_result.filament_densities);
             }
+            else if (comment.find("extruderDiameter") != comment.npos) {
+                std::vector<float> extruder_diameters;
+                extract_floats(comment, "extruderDiameter", extruder_diameters);
+                m_result.extruders_count = extruder_diameters.size();
+            }
         }
         });
 
-    m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
+    if (m_result.extruders_count == 0)
+        m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
 
     if (bed_size.is_defined()) {
         m_result.bed_shape = {
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 49fd17ce8..979214fb5 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -695,8 +695,8 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
         // update tool colors
         m_tool_colors = decode_colors(str_tool_colors);
 
-    // ensure at least one (default) color is defined
-    if (m_tool_colors.empty())
+    // ensure there are enough colors defined
+    while (m_tool_colors.size() < std::max(size_t(1), gcode_result.extruders_count))
         m_tool_colors.push_back(decode_color("#FF8000"));
 
     // update ranges for coloring / legend

From e30ff22b8a2d586abb3b023519b40f715c44e1ad Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 14:20:16 +0200
Subject: [PATCH 26/32] GCodePostProcessor - use C files instead of C++
 streams, C files are faster. Also fixed a regression - crashes on undefined
 cancellation callback.

---
 src/libslic3r/GCode/GCodeProcessor.cpp | 48 +++++++++++++++-----------
 1 file changed, 28 insertions(+), 20 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index f7709e9d6..aa6912fc4 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -347,17 +347,25 @@ void GCodeProcessor::TimeProcessor::reset()
     machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
 }
 
+struct FilePtr {
+    FilePtr(FILE *f) : f(f) {}
+    ~FilePtr() { this->close(); }
+    void close() { if (f) ::fclose(f); }
+    FILE* f = nullptr;
+};
+
 void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves)
 {
-    boost::nowide::ifstream in(filename);
-    if (!in.good())
+    FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
+    if (in.f == nullptr)
         throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
 
     // temporary file to contain modified gcode
     std::string out_path = filename + ".postprocess";
-    FILE* out = boost::nowide::fopen(out_path.c_str(), "wb");
-    if (out == nullptr)
+    FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
+    if (out.f == nullptr) {
         throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
+    }
 
     auto time_in_minutes = [](float time_in_seconds) {
         assert(time_in_seconds >= 0.f);
@@ -559,11 +567,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
     };
 
     // helper function to write to disk
-    auto write_string = [&](const std::string& str) {
-        fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
-        if (ferror(out)) {
-            in.close();
-            fclose(out);
+    auto write_string = [&export_line, &out, &out_path](const std::string& str) {
+        fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
+        if (ferror(out.f)) {
+            out.close();
             boost::nowide::remove(out_path.c_str());
             throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
         }
@@ -575,25 +582,23 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
 
     {
         // Read the input stream 64kB at a time, extract lines and process them.
-        in.sync_with_stdio(false);
         std::vector<char> buffer(65536 * 10, 0);
         // Line buffer.
         assert(gcode_line.empty());
-        while (! in.eof()) {
-            in.read(buffer.data(), buffer.size());
-            if (! in.eof() && ! in.good()) {
-                fclose(out);
+        for (;;) {
+            size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
+            if (::ferror(in.f))
                 throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
-            }
+            bool eof       = cnt_read == 0;
             auto it        = buffer.begin();
-            auto it_bufend = buffer.begin() + in.gcount();
-            while (it != it_bufend) {
+            auto it_bufend = buffer.begin() + cnt_read;
+            while (it != it_bufend || (eof && ! gcode_line.empty())) {
                 // Find end of line.
                 bool eol    = false;
                 auto it_end = it;
                 for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
                 // End of line is indicated also if end of file was reached.
-                eol |= in.eof() && it_end == it_bufend;
+                eol |= eof && it_end == it_bufend;
                 gcode_line.insert(gcode_line.end(), it, it_end);
                 if (eol) {
                     ++line_id;
@@ -622,13 +627,15 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
                 if (it != it_bufend && *it == '\n')
                     ++ it;
             }
+            if (eof)
+                break;
         }
     }
 
     if (!export_line.empty())
         write_string(export_line);
 
-    fclose(out);
+    out.close();
     in.close();
 
     // updates moves' gcode ids which have been modified by the insertion of the M73 lines
@@ -1234,7 +1241,8 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
         if (-- parse_line_callback_cntr == 0) {
             // Don't call the cancel_callback() too often, do it every at every 10000'th line.
             parse_line_callback_cntr = 10000;
-            cancel_callback();
+            if (cancel_callback)
+                cancel_callback();
         }
         this->process_gcode_line(line);
     });

From 719c91514bd71cf04d4abec70a958fd3710f5f01 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Sep 2021 15:19:59 +0200
Subject: [PATCH 27/32] ObjectList:: Allow use BACKSPACE to delete selected
 items

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index c99d9090c..bf174cbf1 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1044,13 +1044,8 @@ void ObjectList::key_event(wxKeyEvent& event)
 {
     if (event.GetKeyCode() == WXK_TAB)
         Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
-    else if (event.GetKeyCode() == WXK_DELETE
-#ifdef __WXOSX__
-        || event.GetKeyCode() == WXK_BACK
-#endif //__WXOSX__
-        ) {
+    else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK )
         remove();
-    }
     else if (event.GetKeyCode() == WXK_F5)
         wxGetApp().plater()->reload_all_from_disk();
     else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))

From 32733b7db943ccf823a27f9fca83a17c8aaae350 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 15:42:50 +0200
Subject: [PATCH 28/32] GCodeProcessor collects positions of line ends for
 GCodeViewer, GCodeViewer no more parses G-code just to extract line end
 positions. Removed start_mapping_gcode_window(), void
 stop_mapping_gcode_window(), they are no more needed.

---
 src/libslic3r/GCode/GCodeProcessor.cpp      | 16 ++++--
 src/libslic3r/GCode/GCodeProcessor.hpp      |  4 +-
 src/slic3r/GUI/BackgroundSlicingProcess.cpp |  5 +-
 src/slic3r/GUI/GCodeViewer.cpp              | 62 ++++-----------------
 src/slic3r/GUI/GCodeViewer.hpp              |  9 +--
 src/slic3r/GUI/GLCanvas3D.cpp               | 10 ----
 src/slic3r/GUI/GLCanvas3D.hpp               |  3 -
 src/slic3r/GUI/Plater.cpp                   | 10 ----
 src/slic3r/GUI/Plater.hpp                   |  3 -
 9 files changed, 30 insertions(+), 92 deletions(-)

diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index aa6912fc4..ec586dbef 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -354,7 +354,7 @@ struct FilePtr {
     FILE* f = nullptr;
 };
 
-void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves)
+void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
 {
     FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
     if (in.f == nullptr)
@@ -567,13 +567,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
     };
 
     // helper function to write to disk
-    auto write_string = [&export_line, &out, &out_path](const std::string& str) {
+    size_t out_file_pos = 0;
+    lines_ends.clear();
+    auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
         fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
         if (ferror(out.f)) {
             out.close();
             boost::nowide::remove(out_path.c_str());
             throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
         }
+        for (size_t i = 0; i < export_line.size(); ++ i)
+            if (export_line[i] == '\n')
+                lines_ends.emplace_back(out_file_pos + i + 1);
+        out_file_pos += export_line.size();
         export_line.clear();
     };
 
@@ -736,7 +742,9 @@ void GCodeProcessor::Result::reset() {
 }
 #else
 void GCodeProcessor::Result::reset() {
-    moves = std::vector<GCodeProcessor::MoveVertex>();
+
+    moves.clear();
+    lines_ends.clear();
     bed_shape = Pointfs();
     settings_ids.reset();
     extruders_count = 0;
@@ -1270,7 +1278,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
 
     // post-process to add M73 lines into the gcode
     if (apply_postprocess)
-        m_time_processor.post_process(filename, m_result.moves);
+        m_time_processor.post_process(filename, m_result.moves, m_result.lines_ends);
 
 #if ENABLE_GCODE_VIEWER_DATA_CHECKING
     std::cout << "\n";
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 4fcdd8df3..bb9310888 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -306,7 +306,7 @@ namespace Slic3r {
 
             // post process the file with the given filename to add remaining time lines M73
             // and updates moves' gcode ids accordingly
-            void post_process(const std::string& filename, std::vector<MoveVertex>& moves);
+            void post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends);
         };
 
         struct UsedFilaments  // filaments per ColorChange
@@ -350,6 +350,8 @@ namespace Slic3r {
             std::string filename;
             unsigned int id;
             std::vector<MoveVertex> moves;
+            // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
+            std::vector<size_t> lines_ends;
             Pointfs bed_shape;
             SettingsIds settings_ids;
             size_t extruders_count;
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 8a58e5ec9..b4de4a509 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -574,11 +574,8 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
 		// Some FFF status was invalidated, and the G-code was not exported yet.
 		// Let the G-code preview UI know that the final G-code preview is not valid.
 		// In addition, this early memory deallocation reduces memory footprint.
-		if (m_gcode_result != nullptr) {
-			//FIXME calling platter from here is not a staple of a good architecture.
-			GUI::wxGetApp().plater()->stop_mapping_gcode_window();
+		if (m_gcode_result != nullptr)
 			m_gcode_result->reset();
-		}
 	}
 	return invalidated;
 }
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 979214fb5..946cbb275 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -284,45 +284,14 @@ void GCodeViewer::SequentialView::Marker::render() const
     ImGui::PopStyleVar();
 }
 
-void GCodeViewer::SequentialView::GCodeWindow::load_gcode()
+void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
 {
-    if (m_filename.empty())
-        return;
-
+    assert(! m_file.is_open());
     if (m_file.is_open())
         return;
 
-    try
-    {
-        // generate mapping for accessing data in file by line number
-        boost::nowide::ifstream f(m_filename);
-
-        f.seekg(0, f.end);
-        uint64_t file_length = static_cast<uint64_t>(f.tellg());
-        f.seekg(0, f.beg);
-
-        std::string line;
-        uint64_t offset = 0;
-        while (std::getline(f, line)) {
-            uint64_t line_length = static_cast<uint64_t>(line.length());
-            m_lines_map.push_back({ offset, line_length });
-            offset += static_cast<uint64_t>(line_length) + 1;
-        }
-
-        if (offset != file_length) {
-            // if the final offset does not match with file length, lines are terminated with CR+LF
-            // so update all offsets accordingly
-            for (size_t i = 0; i < m_lines_map.size(); ++i) {
-                m_lines_map[i].first += static_cast<uint64_t>(i);
-            }
-        }
-    }
-    catch (...)
-    {
-        BOOST_LOG_TRIVIAL(error) << "Unable to load data from " << m_filename << ". Cannot show G-code window.";
-        reset();
-        return;
-    }
+    m_filename   = filename;
+    m_lines_ends = std::move(lines_ends);
 
     m_selected_line_id = 0;
     m_last_lines_size = 0;
@@ -345,7 +314,9 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
         ret.reserve(end_id - start_id + 1);
         for (uint64_t id = start_id; id <= end_id; ++id) {
             // read line from file
-            std::string gline(m_file.data() + m_lines_map[id - 1].first, m_lines_map[id - 1].second);
+            const size_t start = id == 1 ? 0 : m_lines_ends[id - 2];
+            const size_t len   = m_lines_ends[id - 1] - start;
+            std::string gline(m_file.data() + start, len);
 
             std::string command;
             std::string parameters;
@@ -379,7 +350,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
     static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
     static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f };
 
-    if (!m_visible || m_filename.empty() || m_lines_map.empty() || curr_line_id == 0)
+    if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0)
         return;
 
     // window height
@@ -397,8 +368,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
     const uint64_t half_lines_count = lines_count / 2;
     uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0;
     uint64_t end_id = start_id + lines_count - 1;
-    if (end_id >= static_cast<uint64_t>(m_lines_map.size())) {
-        end_id = static_cast<uint64_t>(m_lines_map.size()) - 1;
+    if (end_id >= static_cast<uint64_t>(m_lines_ends.size())) {
+        end_id = static_cast<uint64_t>(m_lines_ends.size()) - 1;
         start_id = end_id - lines_count + 1;
     }
 
@@ -606,8 +577,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
     // release gpu memory, if used
     reset(); 
 
-    m_sequential_view.gcode_window.set_filename(gcode_result.filename);
-    m_sequential_view.gcode_window.load_gcode();
+    m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
 
 #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     if (wxGetApp().is_gcode_viewer())
@@ -1146,16 +1116,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     fclose(fp);
 }
 
-void GCodeViewer::start_mapping_gcode_window()
-{
-    m_sequential_view.gcode_window.load_gcode();
-}
-
-void GCodeViewer::stop_mapping_gcode_window()
-{
-    m_sequential_view.gcode_window.stop_mapping_file();
-}
-
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
     // max index buffer size, in bytes
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 429175fe6..b918dee23 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -639,18 +639,17 @@ public:
             std::string m_filename;
             boost::iostreams::mapped_file_source m_file;
             // map for accessing data in file by line number
-            std::vector<std::pair<uint64_t, uint64_t>> m_lines_map;
+            std::vector<size_t> m_lines_ends;
             // current visible lines
             std::vector<Line> m_lines;
 
         public:
             GCodeWindow() = default;
             ~GCodeWindow() { stop_mapping_file(); }
-            void set_filename(const std::string& filename) { m_filename = filename; }
-            void load_gcode();
+            void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
             void reset() {
                 stop_mapping_file();
-                m_lines_map.clear();
+                m_lines_ends.clear();
                 m_lines.clear();
                 m_filename.clear();
             }
@@ -777,8 +776,6 @@ public:
 
     void export_toolpaths_to_obj(const char* filename) const;
 
-    void start_mapping_gcode_window();
-    void stop_mapping_gcode_window();
     void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
 
 #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index e28189220..f50c7fe25 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1114,16 +1114,6 @@ int GLCanvas3D::check_volumes_outside_state() const
     return (int)state;
 }
 
-void GLCanvas3D::start_mapping_gcode_window()
-{
-    m_gcode_viewer.start_mapping_gcode_window();
-}
-
-void GLCanvas3D::stop_mapping_gcode_window()
-{
-    m_gcode_viewer.stop_mapping_gcode_window();
-}
-
 void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
 {
     m_render_sla_auxiliaries = visible;
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 3750e3a3e..799fa11c7 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -621,9 +621,6 @@ public:
     const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
     void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
 
-    void start_mapping_gcode_window();
-    void stop_mapping_gcode_window();
-
     void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
     void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
     void update_instance_printable_state_for_object(size_t obj_idx);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index af8734184..2c77dcb7a 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -6143,16 +6143,6 @@ BoundingBoxf Plater::bed_shape_bb() const
     return p->bed_shape_bb();
 }
 
-void Plater::start_mapping_gcode_window()
-{
-    p->preview->get_canvas3d()->start_mapping_gcode_window();
-}
-
-void Plater::stop_mapping_gcode_window()
-{
-    p->preview->get_canvas3d()->stop_mapping_gcode_window();
-}
-
 void Plater::arrange()
 {
     p->m_ui_jobs.arrange();
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 99f7e3cda..f1a493b0f 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -282,9 +282,6 @@ public:
     GLCanvas3D* get_current_canvas3D();
     BoundingBoxf bed_shape_bb() const;
     
-    void start_mapping_gcode_window();
-    void stop_mapping_gcode_window();
-
     void arrange();
     void find_new_position(const ModelInstancePtrs  &instances);
 

From b5a007a683124ec3b01b61e892ed8eb39c034248 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 16:23:43 +0200
Subject: [PATCH 29/32] WIP to G-code export parallelization through
 pipelining: New class GCodeOutputStream as a G-code consumer. In the
 following steps the GCodeOutputStream will be pipelined with GCodeProcessor.

---
 src/libslic3r/GCode.cpp | 152 ++++++++++++++++++++--------------------
 src/libslic3r/GCode.hpp |  48 ++++++++-----
 2 files changed, 107 insertions(+), 93 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 19909b2bc..54815188e 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -744,27 +744,27 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
     std::string path_tmp(path);
     path_tmp += ".tmp";
 
-    FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
-    if (file == nullptr)
+    GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"));
+    if (! file.is_open())
         throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
 
     try {
         m_placeholder_parser_failed_templates.clear();
         this->_do_export(*print, file, thumbnail_cb);
-        fflush(file);
-        if (ferror(file)) {
-            fclose(file);
+        file.flush();
+        if (file.is_error()) {
+            file.close();
             boost::nowide::remove(path_tmp.c_str());
             throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
         }
     } catch (std::exception & /* ex */) {
         // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
         // Close and remove the file.
-        fclose(file);
+        file.close();
         boost::nowide::remove(path_tmp.c_str());
         throw;
     }
-    fclose(file);
+    file.close();
 
     if (! m_placeholder_parser_failed_templates.empty()) {
         // G-code export proceeded, but some of the PlaceholderParser substitutions failed.
@@ -1046,7 +1046,7 @@ std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Pri
     return instances;
 }
 
-void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
+void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
 {
     PROFILE_FUNC();
 
@@ -1111,10 +1111,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
 #endif /* HAS_PRESSURE_EQUALIZER */
 
     // Write information on the generator.
-    _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
+    file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
 
     DoExport::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
-        [this, file](const char* sz) { this->_write(file, sz); },
+        [&file](const char* sz) { file.write(sz); },
         [&print]() { print.throw_if_canceled(); });
 
     // Write notes (content of the Print Settings tab -> Notes)
@@ -1125,10 +1125,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
             // Remove the trailing '\r' from the '\r\n' sequence.
             if (! line.empty() && line.back() == '\r')
                 line.pop_back();
-            _write_format(file, "; %s\n", line.c_str());
+            file.write_format("; %s\n", line.c_str());
         }
         if (! lines.empty())
-            _write(file, "\n");
+            file.write("\n");
     }
     print.throw_if_canceled();
 
@@ -1139,22 +1139,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
     const double       first_layer_height   = print.config().first_layer_height.value;
     for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
         const PrintRegion &region = print.get_print_region(region_id);
-        _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
-        _write_format(file, "; perimeters extrusion width = %.2fmm\n",          region.flow(*first_object, frPerimeter,         layer_height).width());
-        _write_format(file, "; infill extrusion width = %.2fmm\n",              region.flow(*first_object, frInfill,            layer_height).width());
-        _write_format(file, "; solid infill extrusion width = %.2fmm\n",        region.flow(*first_object, frSolidInfill,       layer_height).width());
-        _write_format(file, "; top infill extrusion width = %.2fmm\n",          region.flow(*first_object, frTopSolidInfill,    layer_height).width());
+        file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
+        file.write_format("; perimeters extrusion width = %.2fmm\n",          region.flow(*first_object, frPerimeter,         layer_height).width());
+        file.write_format("; infill extrusion width = %.2fmm\n",              region.flow(*first_object, frInfill,            layer_height).width());
+        file.write_format("; solid infill extrusion width = %.2fmm\n",        region.flow(*first_object, frSolidInfill,       layer_height).width());
+        file.write_format("; top infill extrusion width = %.2fmm\n",          region.flow(*first_object, frTopSolidInfill,    layer_height).width());
         if (print.has_support_material())
-            _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
+            file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
         if (print.config().first_layer_extrusion_width.value > 0)
-            _write_format(file, "; first layer extrusion width = %.2fmm\n",   region.flow(*first_object, frPerimeter, first_layer_height, true).width());
-        _write_format(file, "\n");
+            file.write_format("; first layer extrusion width = %.2fmm\n",   region.flow(*first_object, frPerimeter, first_layer_height, true).width());
+        file.write_format("\n");
     }
     print.throw_if_canceled();
 
     // adds tags for time estimators
     if (print.config().remaining_times.value)
-        _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
+        file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
 
     // Prepare the helper object for replacing placeholders in custom G-code and output filename.
     m_placeholder_parser = print.placeholder_parser();
@@ -1218,7 +1218,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
 
     // Disable fan.
     if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
-        _write(file, m_writer.set_fan(0, true));
+        file.write(m_writer.set_fan(0, true));
 
     // Let the start-up script prime the 1st printing tool.
     m_placeholder_parser.set("initial_tool", initial_extruder_id);
@@ -1261,10 +1261,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
     this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
 
     // adds tag for processor
-    _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
+    file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
 
     // Write the custom start G-code
-    _writeln(file, start_gcode);
+    file.writeln(start_gcode);
 
     // Process filament-specific gcode.
    /* if (has_wipe_tower) {
@@ -1272,14 +1272,14 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
     } else {
             DynamicConfig config;
             config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
-            _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
+            file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
     }
 */
     this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
     print.throw_if_canceled();
 
     // Set other general things.
-    _write(file, this->preamble());
+    file.write(this->preamble());
 
     // Calculate wiping points if needed
     DoExport::init_ooze_prevention(print, m_ooze_prevention);
@@ -1291,7 +1291,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
     if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
         // Set initial extruder only after custom start G-code.
         // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
-        _write(file, this->set_extruder(initial_extruder_id, 0.));
+        file.write(this->set_extruder(initial_extruder_id, 0.));
     }
 
     // Do all objects for each layer.
@@ -1317,8 +1317,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
                 // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
                 m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
                 m_avoid_crossing_perimeters.use_external_mp_once();
-                _write(file, this->retract());
-                _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
+                file.write(this->retract());
+                file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
                 m_enable_cooling_markers = true;
                 // Disable motion planner when traveling to first object point.
                 m_avoid_crossing_perimeters.disable_once();
@@ -1330,7 +1330,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
                 // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
                 this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
                 this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
-                _writeln(file, between_objects_gcode);
+                file.writeln(between_objects_gcode);
             }
             // Reset the cooling buffer internal state (the current position, feed rate, accelerations).
             m_cooling_buffer->reset();
@@ -1346,7 +1346,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
             }
 #ifdef HAS_PRESSURE_EQUALIZER
             if (m_pressure_equalizer)
-                _write(file, m_pressure_equalizer->process("", true));
+                file.write(m_pressure_equalizer->process("", true));
 #endif /* HAS_PRESSURE_EQUALIZER */
             ++ finished_objects;
             // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
@@ -1361,9 +1361,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
         // Prusa Multi-Material wipe tower.
         if (has_wipe_tower && ! layers_to_print.empty()) {
             m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
-            _write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
+            file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
             if (print.config().single_extruder_multi_material_priming) {
-                _write(file, m_wipe_tower->prime(*this));
+                file.write(m_wipe_tower->prime(*this));
                 // Verify, whether the print overaps the priming extrusions.
                 BoundingBoxf bbox_print(get_print_extrusions_extents(print));
                 coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
@@ -1375,15 +1375,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
                 bool overlap = bbox_prime.overlap(bbox_print);
 
                 if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) {
-                    _write(file, this->retract());
-                    _write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
+                    file.write(this->retract());
+                    file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
                     if (overlap) {
                         // Wait for the user to remove the priming extrusions.
-                        _write(file, "M1 Remove priming towers and click button.\n");
+                        file.write("M1 Remove priming towers and click button.\n");
                     } else {
                         // Just wait for a bit to let the user check, that the priming succeeded.
                         //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
-                        _write(file, "M1 S10\n");
+                        file.write("M1 S10\n");
                     }
                 } else {
                     // This is not Marlin, M1 command is probably not supported.
@@ -1410,19 +1410,19 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
         }
 #ifdef HAS_PRESSURE_EQUALIZER
         if (m_pressure_equalizer)
-            _write(file, m_pressure_equalizer->process("", true));
+            file.write(m_pressure_equalizer->process("", true));
 #endif /* HAS_PRESSURE_EQUALIZER */
         if (m_wipe_tower)
             // Purge the extruder, pull out the active filament.
-            _write(file, m_wipe_tower->finalize(*this));
+            file.write(m_wipe_tower->finalize(*this));
     }
 
     // Write end commands to file.
-    _write(file, this->retract());
-    _write(file, m_writer.set_fan(false));
+    file.write(this->retract());
+    file.write(m_writer.set_fan(false));
 
     // adds tag for processor
-    _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
+    file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
 
     // Process filament-specific gcode in extruder order.
     {
@@ -1434,48 +1434,48 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
             // Process the end_filament_gcode for the active filament only.
             int extruder_id = m_writer.extruder()->id();
             config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
-            _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
+            file.writeln(this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
         } else {
             for (const std::string &end_gcode : print.config().end_filament_gcode.values) {
                 int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front());
                 config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
-                _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
+                file.writeln(this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
             }
         }
-        _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
+        file.writeln(this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
     }
-    _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
-    _write(file, m_writer.postamble());
+    file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
+    file.write(m_writer.postamble());
 
     // adds tags for time estimators
     if (print.config().remaining_times.value)
-        _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
+        file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
 
     print.throw_if_canceled();
 
     // Get filament stats.
-    _write(file, DoExport::update_print_stats_and_format_filament_stats(
+    file.write(DoExport::update_print_stats_and_format_filament_stats(
     	// Const inputs
         has_wipe_tower, print.wipe_tower_data(),
         m_writer.extruders(),
         // Modifies
         print.m_print_statistics));
-    _write(file, "\n");
-    _write_format(file, "; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
-    _write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
+    file.write("\n");
+    file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
+    file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
     if (print.m_print_statistics.total_toolchanges > 0)
-    	_write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
-    _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
+    	file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
+    file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
 
     // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end.
     // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer.
     {
-        _write(file, "\n; prusaslicer_config = begin\n");
+        file.write("\n; prusaslicer_config = begin\n");
         std::string full_config;
         append_full_config(print, full_config);
         if (!full_config.empty())
-            _write(file, full_config);
-        _write(file, "; prusaslicer_config = end\n");
+            file.write(full_config);
+        file.write("; prusaslicer_config = end\n");
     }
     print.throw_if_canceled();
 }
@@ -1565,16 +1565,16 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
 
 // Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
 // Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
-void GCode::print_machine_envelope(FILE *file, Print &print)
+void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
 {
     if ((print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware)
      && print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) {
-        fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
+        file.write_format("M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
             int(print.config().machine_max_acceleration_x.values.front() + 0.5),
             int(print.config().machine_max_acceleration_y.values.front() + 0.5),
             int(print.config().machine_max_acceleration_z.values.front() + 0.5),
             int(print.config().machine_max_acceleration_e.values.front() + 0.5));
-        fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
+        file.write_format("M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
             int(print.config().machine_max_feedrate_x.values.front() + 0.5),
             int(print.config().machine_max_feedrate_y.values.front() + 0.5),
             int(print.config().machine_max_feedrate_z.values.front() + 0.5),
@@ -1587,18 +1587,18 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
         int travel_acc = print.config().gcode_flavor == gcfMarlinLegacy
                        ? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)
                        : int(print.config().machine_max_acceleration_travel.values.front() + 0.5);
-        fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
+        file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
             int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
             int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
             travel_acc);
 
         assert(is_decimal_separator_point());
-        fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
+        file.write_format("M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
             print.config().machine_max_jerk_x.values.front(),
             print.config().machine_max_jerk_y.values.front(),
             print.config().machine_max_jerk_z.values.front(),
             print.config().machine_max_jerk_e.values.front());
-        fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
+        file.write_format("M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
             int(print.config().machine_min_extruding_rate.values.front() + 0.5),
             int(print.config().machine_min_travel_rate.values.front() + 0.5));
     }
@@ -1608,7 +1608,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
 // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
 // M140 - Set Extruder Temperature
 // M190 - Set Extruder Temperature and Wait
-void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
+void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
 {
     // Initial bed temperature based on the first extruder.
     int  temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id);
@@ -1621,7 +1621,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
     // the custom start G-code emited these.
     std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
     if (! temp_set_by_gcode)
-        _write(file, set_temp_gcode);
+        file.write(set_temp_gcode);
 }
 
 // Write 1st layer extruder temperatures into the G-code.
@@ -1629,7 +1629,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
 // M104 - Set Extruder Temperature
 // M109 - Set Extruder Temperature and Wait
 // RepRapFirmware: G10 Sxx
-void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
+void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
 {
     // Is the bed temperature set by the provided custom G-code?
     int  temp_by_gcode = -1;
@@ -1646,7 +1646,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
             // Set temperature of the first printing extruder only.
             int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
             if (temp > 0)
-                _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
+                file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
         } else {
             // Set temperatures of all the printing extruders.
             for (unsigned int tool_id : print.extruders()) {
@@ -1654,7 +1654,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
                 if (print.config().ooze_prevention.value)
                     temp += print.config().standby_temperature_delta.value;
                 if (temp > 0)
-                    _write(file, m_writer.set_temperature(temp, wait, tool_id));
+                    file.write(m_writer.set_temperature(temp, wait, tool_id));
             }
         }
     }
@@ -1891,7 +1891,7 @@ namespace Skirt {
 // and performing the extruder specific extrusions together.
 void GCode::process_layer(
     // Write into the output file.
-    FILE                            		*file,
+    GCodeOutputStream                       &file,
     const Print                    			&print,
     // Set of object & print layers of the same PrintObject and with the same print_z.
     const std::vector<LayerToPrint> 		&layers,
@@ -2306,7 +2306,7 @@ void GCode::process_layer(
     // printf("G-code after filter:\n%s\n", out.c_str());
 #endif /* HAS_PRESSURE_EQUALIZER */
 
-    _write(file, gcode);
+    file.write(gcode);
     BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
     log_memory_info();
 }
@@ -2642,22 +2642,22 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
     return gcode;
 }
 
-void GCode::_write(FILE* file, const char *what)
+void GCode::GCodeOutputStream::write(const char *what)
 {
     if (what != nullptr) {
         const char* gcode = what;
         // writes string to file
-        fwrite(gcode, 1, ::strlen(gcode), file);
+        fwrite(gcode, 1, ::strlen(gcode), this->f);
     }
 }
 
-void GCode::_writeln(FILE* file, const std::string &what)
+void GCode::GCodeOutputStream::writeln(const std::string &what)
 {
     if (! what.empty())
-        _write(file, (what.back() == '\n') ? what : (what + '\n'));
+        this->write(what.back() == '\n' ? what : what + '\n');
 }
 
-void GCode::_write_format(FILE* file, const char* format, ...)
+void GCode::GCodeOutputStream::write_format(const char* format, ...)
 {
     va_list args;
     va_start(args, format);
@@ -2681,7 +2681,7 @@ void GCode::_write_format(FILE* file, const char* format, ...)
     char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
     int res = ::vsnprintf(bufptr, buflen, format, args);
     if (res > 0)
-        _write(file, bufptr);
+        this->write(bufptr);
 
     if (buffer_dynamic)
         free(bufptr);
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index d2d241054..06900410a 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -184,13 +184,39 @@ public:
     };
 
 private:
-    void            _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
+    class GCodeOutputStream {
+    public:
+        GCodeOutputStream(FILE *f) : f(f) {}
+        ~GCodeOutputStream() { this->close(); }
+
+        bool is_open() const { return f; }
+        bool is_error() const { return ::ferror(f); }
+        
+        void flush() { ::fflush(f); }
+        void close() { if (f) ::fclose(f); }
+
+        // Write a string into a file.
+        void write(const std::string& what) { this->write(what.c_str()); }
+        void write(const char* what);
+
+        // Write a string into a file. 
+        // Add a newline, if the string does not end with a newline already.
+        // Used to export a custom G-code section processed by the PlaceholderParser.
+        void writeln(const std::string& what);
+
+        // Formats and write into a file the given data. 
+        void write_format(const char* format, ...);
+
+    private:
+        FILE *f = nullptr;
+    };
+    void            _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb);
 
     static std::vector<LayerToPrint>        		                   collect_layers_to_print(const PrintObject &object);
     static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
     void            process_layer(
         // Write into the output file.
-        FILE                            *file,
+        GCodeOutputStream               &file,
         const Print                     &print,
         // Set of object & print layers of the same PrintObject and with the same print_z.
         const std::vector<LayerToPrint> &layers,
@@ -358,22 +384,10 @@ private:
     // Processor
     GCodeProcessor m_processor;
 
-    // Write a string into a file.
-    void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
-    void _write(FILE* file, const char *what);
-
-    // Write a string into a file. 
-    // Add a newline, if the string does not end with a newline already.
-    // Used to export a custom G-code section processed by the PlaceholderParser.
-    void _writeln(FILE* file, const std::string& what);
-
-    // Formats and write into a file the given data. 
-    void _write_format(FILE* file, const char* format, ...);
-
     std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
-    void print_machine_envelope(FILE *file, Print &print);
-    void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
-    void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
+    void print_machine_envelope(GCodeOutputStream &file, Print &print);
+    void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
+    void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
     // On the first printing layer. This flag triggers first layer speeds.
     bool                                on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
     // To control print speed of 1st object layer over raft interface.

From eb2debc2c6f212dfe3bed71f0a072bd674a20e5b Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Sep 2021 16:48:13 +0200
Subject: [PATCH 30/32] ObjectList: Fixed DnD of Volumes inside the Object

---
 src/slic3r/GUI/ObjectDataViewModel.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp
index 9417364ef..88527e9fd 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.cpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.cpp
@@ -1361,7 +1361,8 @@ wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume
     if (!node_parent)      // happens if item.IsOk()==false
         return ret_item;
 
-    const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0;
+    size_t shift;
+    for (shift = 0; shift < node_parent->GetChildCount() && node_parent->GetNthChild(shift)->GetType() != itVolume; shift ++);
 
     ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift);
     node_parent->GetChildren().Remove(deleted_node);

From 152e236ddad9170ac22afb7a82db455c357f66b8 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 17:25:53 +0200
Subject: [PATCH 31/32] Trying to fix the windows build

---
 src/libslic3r/GCode.cpp | 4 ++++
 src/libslic3r/GCode.hpp | 6 +++---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 54815188e..ff0076880 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -2642,6 +2642,10 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
     return gcode;
 }
 
+bool GCode::GCodeOutputStream::is_error() const { return ::ferror(f); }
+void GCode::GCodeOutputStream::flush() { ::fflush(f); }
+void GCode::GCodeOutputStream::close() { if (f) ::fclose(f); }
+
 void GCode::GCodeOutputStream::write(const char *what)
 {
     if (what != nullptr) {
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 06900410a..89e4570a9 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -190,10 +190,10 @@ private:
         ~GCodeOutputStream() { this->close(); }
 
         bool is_open() const { return f; }
-        bool is_error() const { return ::ferror(f); }
+        bool is_error() const;
         
-        void flush() { ::fflush(f); }
-        void close() { if (f) ::fclose(f); }
+        void flush();
+        void close();
 
         // Write a string into a file.
         void write(const std::string& what) { this->write(what.c_str()); }

From 0da0a7b2a0e2a45f55ce51434761f37d34937df6 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Tue, 7 Sep 2021 17:58:06 +0200
Subject: [PATCH 32/32] Fix of recent GCode / GCodeProcessor refactoring: Don't
 close a FILE twice.

---
 src/libslic3r/GCode.cpp                | 20 +++++++++++++++++---
 src/libslic3r/GCode/GCodeProcessor.cpp |  7 ++++++-
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index ff0076880..f2deba046 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -2642,9 +2642,23 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
     return gcode;
 }
 
-bool GCode::GCodeOutputStream::is_error() const { return ::ferror(f); }
-void GCode::GCodeOutputStream::flush() { ::fflush(f); }
-void GCode::GCodeOutputStream::close() { if (f) ::fclose(f); }
+bool GCode::GCodeOutputStream::is_error() const 
+{
+    return ::ferror(this->f);
+}
+
+void GCode::GCodeOutputStream::flush()
+{ 
+    ::fflush(this->f);
+}
+
+void GCode::GCodeOutputStream::close()
+{ 
+    if (this->f) {
+        ::fclose(this->f);
+        this->f = nullptr;
+    }
+}
 
 void GCode::GCodeOutputStream::write(const char *what)
 {
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index ec586dbef..60af399c0 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -350,7 +350,12 @@ void GCodeProcessor::TimeProcessor::reset()
 struct FilePtr {
     FilePtr(FILE *f) : f(f) {}
     ~FilePtr() { this->close(); }
-    void close() { if (f) ::fclose(f); }
+    void close() { 
+        if (this->f) {
+            ::fclose(this->f);
+            this->f = nullptr;
+        }
+    }
     FILE* f = nullptr;
 };