From 5fe90599fcbba6e4160677b0abbe4e88ba792e3e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 31 Aug 2021 13:58:20 +0200
Subject: [PATCH 1/2] Painting gizmos no longer use a separate undo/redo stack

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp |  2 --
 src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 36 --------------------
 src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp |  2 +-
 3 files changed, 1 insertion(+), 39 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 01eeebe10..ea164ed2f 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -298,8 +298,6 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
         }
     }
 
-    activate_internal_undo_redo_stack(true);
-
     Plater::TakeSnapshot snapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
                                                     : _L("Add supports by angle"));
     update_model_object();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index be8fc331d..09dad81aa 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -26,35 +26,6 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
     m_vbo_sphere.finalize_geometry(true);
 }
 
-// port of 948bc382655993721d93d3b9fce9b0186fcfb211
-void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
-{
-    Plater* plater = wxGetApp().plater();
-
-    // Following is needed to prevent taking an extra snapshot when the activation of
-    // the internal stack happens when the gizmo is already active (such as open gizmo,
-    // close gizmo, undo, start painting). The internal stack does not activate on the
-    // undo, because that would obliterate all future of the main stack (user would
-    // have to close the gizmo himself, he has no access to main undo/redo after the
-    // internal stack opens). We don't want the "entering" snapshot taken in this case,
-    // because there already is one.
-    std::string last_snapshot_name;
-    plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name);
-
-    if (activate && !m_internal_stack_active) {
-        if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str)
-            Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::EnteringGizmo);
-        plater->enter_gizmos_stack();
-        m_internal_stack_active = true;
-    }
-    if (!activate && m_internal_stack_active) {
-        plater->leave_gizmos_stack();
-        if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str)
-            Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::LeavingGizmoWithAction);
-        m_internal_stack_active = false;
-    }
-}
-
 void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection)
 {
     if (m_state != On)
@@ -450,7 +421,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
       && m_button_down != Button::None) {
         // Take snapshot and update ModelVolume data.
         wxString action_name = this->handle_snapshot_action_name(shift_down, m_button_down);
-        activate_internal_undo_redo_stack(true);
         Plater::TakeSnapshot snapshot(wxGetApp().plater(), action_name);
         update_model_object();
 
@@ -548,16 +518,10 @@ void GLGizmoPainterBase::on_set_state()
 
     if (m_state == On && m_old_state != On) { // the gizmo was just turned on
         on_opening();
-        if (! m_parent.get_gizmos_manager().is_serializing()) {
-            wxGetApp().CallAfter([this]() {
-                activate_internal_undo_redo_stack(true);
-            });
-        }
     }
     if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
         // we are actually shutting down
         on_shutdown();
-        activate_internal_undo_redo_stack(false);
         m_old_mo_id = -1;
         //m_iva.release_geometry();
         m_triangle_selectors.clear();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
index 6a15ab2a5..016f604c3 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
@@ -88,7 +88,6 @@ protected:
     void render_cursor_sphere(const Transform3d& trafo) const;
     virtual void update_model_object() const = 0;
     virtual void update_from_model_object() = 0;
-    void activate_internal_undo_redo_stack(bool activate);
 
     virtual std::array<float, 4> get_cursor_sphere_left_button_color() const { return {0.f, 0.f, 1.f, 0.25f}; }
     virtual std::array<float, 4> get_cursor_sphere_right_button_color() const { return {1.f, 0.f, 0.f, 0.25f}; }
@@ -170,6 +169,7 @@ protected:
     void on_load(cereal::BinaryInputArchive& ar) override;
     void on_save(cereal::BinaryOutputArchive& ar) const override {}
     CommonGizmosDataID on_get_requirements() const override;
+    bool wants_enter_leave_snapshots() const override { return true; }
 
     virtual wxString handle_snapshot_action_name(bool shift_down, Button button_down) const = 0;
 

From b76f66d2d23f3e6e0c6fc6eaab875082a6d52a46 Mon Sep 17 00:00:00 2001
From: Vojtech Bubnik <bubnikv@gmail.com>
Date: Thu, 30 Sep 2021 12:18:36 +0200
Subject: [PATCH 2/2] Follow-up to 3ee259b602947b10f1b225124c67c4b750a3c391 1)
 Added parsing of 3MF PrusaSlicer generator semantic version 2) For 3MFs
 generated by >= "2.4.0-alpha1" and < "2.4.0-alpha3",    remove unreferenced
 vertices on loading. This should shrink the incorrectly    generated 3MFs
 back. 3) Added check for an empty mesh, which could have crashed PrusaSlicer
 on 3MF parsing.

---
 src/libslic3r/Format/3mf.cpp | 73 +++++++++++++++++++++---------------
 1 file changed, 42 insertions(+), 31 deletions(-)

diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 1ca20bcba..eca057a5b 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -6,6 +6,7 @@
 #include "../GCode.hpp"
 #include "../Geometry.hpp"
 #include "../GCode/ThumbnailData.hpp"
+#include "../Semver.hpp"
 #include "../Time.hpp"
 
 #include "../I18N.hpp"
@@ -411,6 +412,8 @@ namespace Slic3r {
         unsigned int m_version;
         bool m_check_version;
 
+        // Semantic version of PrusaSlicer, that generated this 3MF.
+        boost::optional<Semver> m_prusaslicer_generator_version;
         unsigned int m_fdm_supports_painting_version = 0;
         unsigned int m_seam_painting_version         = 0;
         unsigned int m_mm_painting_version           = 0;
@@ -1712,21 +1715,20 @@ namespace Slic3r {
                 const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
                 throw version_error(msg);
             }
-        }
-
-        if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
+        } else if (m_curr_metadata_name == "Application") {
+            // Generator application of the 3MF.
+            // SLIC3R_APP_KEY - SLIC3R_VERSION
+            if (boost::starts_with(m_curr_characters, "PrusaSlicer-"))
+                m_prusaslicer_generator_version = Semver::parse(m_curr_characters.substr(12));
+        } else if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
             m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
             check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
                 _(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
-        }
-
-        if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
+        } else if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
             m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
             check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
                 _(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
-        }
-
-        if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
+        } else if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
             m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
             check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
                 _(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
@@ -1890,7 +1892,6 @@ namespace Slic3r {
 
         unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
         unsigned int renamed_volumes_count = 0;
-        int processed_vertices_max_id = 0;
 
         for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
             if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
@@ -1910,33 +1911,43 @@ namespace Slic3r {
             }
 
             // splits volume out of imported geometry
-            std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
-            const size_t       triangles_count = faces.size();
+            indexed_triangle_set its;
+            its.indices.assign(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
+            const size_t triangles_count = its.indices.size();
+            if (triangles_count == 0) {
+                add_error("An empty triangle mesh found");
+                return false;
+            }
 
-            int min_id = faces.front()[0];
-            int max_id = faces.front()[0];
-            for (const Vec3i& face : faces) {
-                for (const int tri_id : face) {
-                    if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
-                        add_error("Found invalid vertex id");
-                        return false;
+            {
+                int min_id = its.indices.front()[0];
+                int max_id = min_id;
+                for (const Vec3i& face : its.indices) {
+                    for (const int tri_id : face) {
+                        if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
+                            add_error("Found invalid vertex id");
+                            return false;
+                        }
+                        min_id = std::min(min_id, tri_id);
+                        max_id = std::max(max_id, tri_id);
                     }
-                    min_id = std::min(min_id, tri_id);
-                    max_id = std::max(max_id, tri_id);
                 }
+                its.vertices.assign(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
+
+                // rebase indices to the current vertices list
+                for (Vec3i& face : its.indices)
+                    for (int& tri_id : face)
+                        tri_id -= min_id;
             }
 
-            // rebase indices to the current vertices list
-            for (Vec3i& face : faces) {
-                for (int& tri_id : face) {
-                    tri_id -= min_id;
-                }
-            }
+            if (m_prusaslicer_generator_version && 
+                *m_prusaslicer_generator_version >= *Semver::parse("2.4.0-alpha1") &&
+                *m_prusaslicer_generator_version < *Semver::parse("2.4.0-alpha3"))
+                // PrusaSlicer 2.4.0-alpha2 contained a bug, where all vertices of a single object were saved for each volume the object contained.
+                // Remove the vertices, that are not referenced by any face.
+                its_compactify_vertices(its, true);
 
-            processed_vertices_max_id = 1 + std::max(processed_vertices_max_id, max_id);
-
-            std::vector<Vec3f> vertices(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
-            TriangleMesh triangle_mesh(std::move(vertices), std::move(faces));
+            TriangleMesh triangle_mesh(std::move(its));
 
             if (m_version == 0) {
                 // if the 3mf was not produced by PrusaSlicer and there is only one instance,