From e586475bc32cb8ef8b2569ea60a5aa0d35a9bc09 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 17:14:15 +0200 Subject: [PATCH] WIP Undo / Redo: Optional debug print outs. --- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/Utils/UndoRedo.cpp | 151 ++++++++++++++++++++++++++++++---- src/slic3r/Utils/UndoRedo.hpp | 9 +- 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a492584e6..4a743aab1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3498,7 +3498,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const void Plater::priv::undo() { - if (this->undo_redo_stack.undo(model)) + if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection())) this->update_after_undo_redo(); } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 34d83c445..4a9af3b43 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -1,7 +1,10 @@ #include "UndoRedo.hpp" #include +#include +#include #include +#include #include #include @@ -18,6 +21,10 @@ #include +#ifndef NDEBUG +// #define SLIC3R_UNDOREDO_DEBUG +#endif /* NDEBUG */ + namespace Slic3r { namespace UndoRedo { @@ -63,8 +70,13 @@ public: // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. virtual void relese_after_timestamp(size_t timestamp) = 0; +#ifdef SLIC3R_UNDOREDO_DEBUG + // Human readable debug information. + virtual std::string format() = 0; +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - virtual bool validate() = 0; + virtual bool valid() = 0; #endif /* NDEBUG */ }; @@ -79,7 +91,7 @@ public: // Release all data after the given timestamp. The shared pointer is NOT released. void relese_after_timestamp(size_t timestamp) override { assert(! m_history.empty()); - assert(this->validate()); + assert(this->valid()); // it points to an interval which either starts with timestamp, or follows the timestamp. auto it = std::lower_bound(m_history.begin(), m_history.end(), T(timestamp, timestamp)); if (it == m_history.end()) { @@ -90,7 +102,7 @@ public: it_prev->trim_end(timestamp); } m_history.erase(it, m_history.end()); - assert(this->validate()); + assert(this->valid()); } protected: @@ -155,8 +167,20 @@ public: return m_shared_object; } +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() override { + std::string out = typeid(T).name(); + out += this->is_serialized() ? + std::string(" len:") + std::to_string(m_serialized.size()) : + std::string(" ptr:") + ptr_to_string(m_shared_object.get()); + for (const Interval &interval : m_history) + out += std::string(",<") + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; + return out; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - bool validate() override; + bool valid() override; #endif /* NDEBUG */ private: @@ -225,6 +249,13 @@ private: MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs); }; +static inline std::string ptr_to_string(const void* ptr) +{ + char buf[64]; + sprintf(buf, "%p", ptr); + return buf; +} + // Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig) // are mutable and there is not tracking of the changes, therefore a snapshot needs to be // taken every time and compared to the previous data at the Undo / Redo stack. @@ -274,14 +305,28 @@ public: return std::string(it->data(), it->data() + it->size()); } +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() override { + std::string out = typeid(T).name(); + bool first = true; + for (const MutableHistoryInterval &interval : m_history) { + if (! first) + out += ","; + out += std::string("ptr:") + ptr_to_string(interval.data()) + " len:" + std::to_string(interval.size()) + " <" + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; + first = false; + } + return out; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - bool validate() override; + bool valid() override; #endif /* NDEBUG */ }; #ifndef NDEBUG template -bool ImmutableObjectHistory::validate() +bool ImmutableObjectHistory::valid() { // The immutable object content is captured either by a shared object, or by its serialization, but not both. assert(! m_shared_object == ! m_serialized.empty()); @@ -295,7 +340,7 @@ bool ImmutableObjectHistory::validate() #ifndef NDEBUG template -bool MutableObjectHistory::validate() +bool MutableObjectHistory::valid() { // Verify that the history intervals are sorted and do not overlap, and that the data reference counters are correct. if (! m_history.empty()) { @@ -329,7 +374,9 @@ public: 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); - bool undo(Slic3r::Model &model); + bool has_undo_snapshot() const; + bool has_redo_snapshot() const; + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). @@ -344,6 +391,41 @@ public: template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); template void load_mutable_object(const Slic3r::ObjectID id, T &target); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() const { + std::string out = "Objects\n"; + for (const std::pair> &kvp : m_objects) + out += std::string("ObjectID:") + std::to_string(kvp.first.id) + " " + kvp.second->format() + "\n"; + out += "Snapshots\n"; + for (const Snapshot &snapshot : m_snapshots) { + if (snapshot.timestamp == m_active_snapshot_time) + out += ">>> "; + out += std::string("Name:") + snapshot.name + ", timestamp: " + std::to_string(snapshot.timestamp) + ", Model ID:" + std::to_string(snapshot.model_id) + "\n"; + } + if (m_active_snapshot_time > m_snapshots.back().timestamp) + out += ">>>\n"; + out += "Current time: " + std::to_string(m_current_time) + "\n"; + return out; + } + void print() const { + std::cout << "Undo / Redo stack" << std::endl; + std::cout << this->format() << std::endl; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + + +#ifndef NDEBUG + bool valid() const { + assert(! m_snapshots.empty()); + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + assert(it == m_snapshots.end() || (it != m_snapshots.begin() && it->timestamp == m_active_snapshot_time)); + assert(it != m_snapshots.end() || m_active_snapshot_time > m_snapshots.back().timestamp); + for (auto it = m_objects.begin(); it != m_objects.end(); ++ it) + assert(it->second->valid()); + return true; + } +#endif /* NDEBUG */ + private: template ObjectID immutable_object_id(const std::shared_ptr &ptr) { return this->immutable_object_id_impl((const void*)ptr.get()); @@ -554,7 +636,11 @@ void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Select void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { // Release old snapshot data. - ++ m_active_snapshot_time; + // The active snapshot may be above the last snapshot if there is no redo data available. + if (! m_snapshots.empty() && m_active_snapshot_time > m_snapshots.back().timestamp) + m_active_snapshot_time = m_snapshots.back().timestamp + 1; + else + ++ m_active_snapshot_time; for (auto &kvp : m_objects) kvp.second->relese_after_timestamp(m_active_snapshot_time); { @@ -572,10 +658,15 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo } 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); + m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + m_active_snapshot_time = m_current_time; // Release empty objects from the history. this->collect_garbage(); + assert(this->valid()); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After snapshot" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ } void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) @@ -588,32 +679,54 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) m_active_snapshot_time = timestamp; model.clear_objects(); model.clear_materials(); - this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); + this->load_mutable_object(ObjectID(it_snapshot->model_id), model); model.update_links_bottom_up_recursive(); 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; + assert(this->valid()); } -bool StackImpl::undo(Slic3r::Model &model) +bool StackImpl::has_undo_snapshot() const { + assert(this->valid()); + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + return -- it != m_snapshots.begin(); +} + +bool StackImpl::has_redo_snapshot() const +{ + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + return it != m_snapshots.end() && ++ it != m_snapshots.end(); +} + +bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +{ + assert(this->valid()); 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); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After undo" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ return true; } bool StackImpl::redo(Slic3r::Model &model) { + assert(this->valid()); 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()) + if (it_current == m_snapshots.end() || ++ it_current == m_snapshots.end()) return false; - this->load_snapshot(it_current->timestamp, model); + this->load_snapshot(it_current->timestamp, model); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After redo" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ return true; } @@ -637,7 +750,9 @@ 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) { pimpl->load_snapshot(timestamp, model); } -bool Stack::undo(Slic3r::Model &model) { return pimpl->undo(model); } +bool Stack::has_undo_snapshot() const { return pimpl->has_undo_snapshot(); } +bool Stack::has_redo_snapshot() const { return pimpl->has_redo_snapshot(); } +bool Stack::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } bool Stack::redo(Slic3r::Model &model) { return pimpl->redo(model); } const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 0c97a0307..e50f6ad63 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -19,11 +19,11 @@ namespace UndoRedo { struct Snapshot { Snapshot(size_t timestamp) : timestamp(timestamp) {} - Snapshot(const std::string &name, size_t timestamp, size_t model_object_id) : name(name), timestamp(timestamp), model_object_id(model_object_id) {} + Snapshot(const std::string &name, size_t timestamp, size_t model_id) : name(name), timestamp(timestamp), model_id(model_id) {} std::string name; size_t timestamp; - size_t model_object_id; + size_t model_id; bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } @@ -53,7 +53,10 @@ public: 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); - bool undo(Slic3r::Model &model); + bool has_undo_snapshot() const; + bool has_redo_snapshot() const; + // Undoing an action may need to take a snapshot of the current application state. + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps).