From 1798e2a84c57a9ecde31ffbeebdf6da0758bcd94 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 14:35:04 +0200 Subject: [PATCH] WIP Undo / Redo : serialization / deserialization of object selection. --- src/libslic3r/ObjectID.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 25 +++++++----- src/slic3r/GUI/Selection.cpp | 18 +++++++++ src/slic3r/GUI/Selection.hpp | 6 ++- src/slic3r/Utils/UndoRedo.cpp | 71 +++++++++++++++-------------------- src/slic3r/Utils/UndoRedo.hpp | 19 ++++++++-- 6 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 0988acf5a..c708e5687 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -24,6 +24,8 @@ class ObjectID { public: ObjectID(size_t id) : id(id) {} + // Default constructor constructs an invalid ObjectID. + ObjectID() : id(0) {} bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; } bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; } @@ -38,8 +40,6 @@ public: size_t id; private: - ObjectID() {} - friend class cereal::access; template void serialize(Archive &ar) { ar(id); } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3e6f3763a..a492584e6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1645,6 +1645,7 @@ private: void update_fff_scene(); void update_sla_scene(); + void update_after_undo_redo(); // path to project file stored with no extension wxString m_project_filename; @@ -3497,22 +3498,26 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const void Plater::priv::undo() { - if (this->undo_redo_stack.undo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { - this->update(false); - //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) -// wxGetApp().obj_list()->update_selections(); -// selection_changed(); - } + if (this->undo_redo_stack.undo(model)) + this->update_after_undo_redo(); } void Plater::priv::redo() { - if (this->undo_redo_stack.redo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { - this->update(false); - //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) + if (this->undo_redo_stack.redo(model)) + this->update_after_undo_redo(); +} + +void Plater::priv::update_after_undo_redo() +{ + this->view3D->get_canvas3d()->get_selection().clear(); + this->update(false); // update volumes from the deserializd model + //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) + this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances); // wxGetApp().obj_list()->update_selections(); // selection_changed(); - } + //FIXME what about the state of the manipulators? + //FIXME what about the focus? Cursor in the side panel? } void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 0784b70ff..2986d97dd 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -311,6 +311,24 @@ void Selection::add_all() this->set_bounding_boxes_dirty(); } +void Selection::set_deserialized(EMode mode, const std::vector> &volumes_and_instances) +{ + if (! m_valid) + return; + + m_mode = mode; + for (unsigned int i : m_list) + (*m_volumes)[i]->selected = false; + m_list.clear(); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) { + const GLVolume::CompositeID &id = (*m_volumes)[i]->composite_id; + if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), std::make_pair(id.volume_id, id.instance_id))) + this->do_add_volume(i); + } + update_type(); + this->set_bounding_boxes_dirty(); +} + void Selection::clear() { if (!m_valid) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 17ae72356..8168e5e88 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -3,7 +3,6 @@ #include #include "libslic3r/Geometry.hpp" -#include "libslic3r/ObjectID.hpp" #include "3DScene.hpp" #if ENABLE_RENDER_SELECTION_CENTER @@ -67,7 +66,7 @@ private: Enum m_value; }; -class Selection : public Slic3r::ObjectBase +class Selection { public: typedef std::set IndicesList; @@ -238,6 +237,9 @@ public: void add_all(); + // To be called after Undo or Redo once the volumes are updated. + void set_deserialized(EMode mode, const std::vector> &volumes_and_instances); + // Update the selection based on the new instance IDs. void instances_changed(const std::vector &instance_ids_selected); // Update the selection based on the map from old indices to new indices after m_volumes changed. diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 978bf149f..34d83c445 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #define CEREAL_FUTURE_EXPERIMENTAL @@ -326,20 +327,17 @@ public: // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model); + bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } -//protected: - void save_model(const Slic3r::Model &model, size_t snapshot_time); - void save_selection(const Slic3r::Model& model, const Slic3r::GUI::Selection &selection, size_t snapshot_time); - void load_model(const Slic3r::Model &model, size_t snapshot_time); - void load_selection(const Slic3r::GUI::Selection &selection, size_t snapshot_time); + const Selection& selection_deserialized() const { return m_selection; } +//protected: template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object); template T* load_mutable_object(const Slic3r::ObjectID id); @@ -372,6 +370,8 @@ private: size_t m_active_snapshot_time; // Logical time counter. m_current_time is being incremented with each snapshot taken. size_t m_current_time; + // Last selection serialized or deserialized. + Selection m_selection; }; using InputArchive = cereal::UserDataAdapter; @@ -469,28 +469,6 @@ namespace cereal ar(id); ptr = stack.load_immutable_object(Slic3r::ObjectID(id)); } - -#if 0 - void save(BinaryOutputArchive &ar, const Slic3r::GUI::Selection &selection) - { - size_t num = selection.get_volume_idxs().size(); - ar(num); - for (unsigned int volume_idx : selection.get_volume_idxs()) { - const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; - ar(id.object_id, id.volume_id, id.instance_id); - } - } - - template void load(BinaryInputArchive &ar, Slic3r::GUI::Selection &selection) - { - size_t num; - ar(num); - for (size_t i = 0; i < num; ++ i) { - Slic3r::GLVolume::CompositeID id; - ar(id.object_id, id.volume_id, id.instance_id); - } - } -#endif } #include @@ -585,15 +563,22 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo } // Take new snapshots. this->save_mutable_object(model); -// this->save_mutable_object(selection); - // Save the snapshot info + m_selection.volumes_and_instances.clear(); + m_selection.volumes_and_instances.reserve(selection.get_volume_idxs().size()); + m_selection.mode = selection.get_mode(); + for (unsigned int volume_idx : selection.get_volume_idxs()) { + const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; + m_selection.volumes_and_instances.emplace_back(id.volume_id, id.instance_id); + } + this->save_mutable_object(m_selection); + // Save the snapshot info. m_active_snapshot_time = m_current_time ++; m_snapshots.emplace_back(snapshot_name, m_active_snapshot_time, model.id().id); // Release empty objects from the history. this->collect_garbage(); } -void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) +void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); @@ -605,27 +590,30 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU model.clear_materials(); this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); model.update_links_bottom_up_recursive(); -// this->load_mutable_object(selection.id(), selection); + m_selection.volumes_and_instances.clear(); + this->load_mutable_object(m_selection.id(), m_selection); + // Sort the volumes so that we may use binary search. + std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end()); this->m_active_snapshot_time = timestamp; } -bool StackImpl::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +bool StackImpl::undo(Slic3r::Model &model) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); if (-- it_current == m_snapshots.begin()) return false; - this->load_snapshot(it_current->timestamp, model, selection); + this->load_snapshot(it_current->timestamp, model); return true; } -bool StackImpl::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +bool StackImpl::redo(Slic3r::Model &model) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); if (++ it_current == m_snapshots.end()) return false; - this->load_snapshot(it_current->timestamp, model, selection); + this->load_snapshot(it_current->timestamp, model); return true; } @@ -648,9 +636,10 @@ Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } -void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) { pimpl->load_snapshot(timestamp, model, selection); } -bool Stack::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } -bool Stack::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->redo(model, selection); } +void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model) { pimpl->load_snapshot(timestamp, model); } +bool Stack::undo(Slic3r::Model &model) { return pimpl->undo(model); } +bool Stack::redo(Slic3r::Model &model) { return pimpl->redo(model); } +const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index be024c282..0c97a0307 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Slic3r { class Model; @@ -27,6 +29,13 @@ struct Snapshot bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } }; +// Excerpt of Slic3r::GUI::Selection for serialization onto the Undo / Redo stack. +struct Selection : public Slic3r::ObjectBase { + unsigned char mode; + std::vector> volumes_and_instances; + template void serialize(Archive &ar) { ar(mode, volumes_and_instances); } +}; + class StackImpl; class Stack @@ -42,14 +51,18 @@ public: // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model); + bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). const std::vector& snapshots() const; + // After load_snapshot() / undo() / redo() the selection is deserialized into a list of ObjectIDs, which needs to be converted + // into the list of GLVolume pointers once the 3D scene is updated. + const Selection& selection_deserialized() const; + private: friend class StackImpl; std::unique_ptr pimpl;