diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 16f8fec4e..fbf10bf83 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -72,6 +72,19 @@ void Model::assign_new_unique_ids_recursive() model_object->assign_new_unique_ids_recursive(); } +void Model::update_links_bottom_up_recursive() +{ + for (std::pair &kvp : this->materials) + kvp.second->set_model(this); + for (ModelObject *model_object : this->objects) { + model_object->set_model(this); + for (ModelInstance *model_instance : model_object->instances) + model_instance->set_model_object(model_object); + for (ModelVolume *model_volume : model_object->volumes) + model_volume->set_model_object(model_object); + } +} + Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) { Model model; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 5fd958f86..398756fc9 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -457,6 +457,7 @@ public: protected: friend class Print; friend class SLAPrint; + friend class Model; friend class ModelObject; // Copies IDs of both the ModelVolume and its config. @@ -598,6 +599,7 @@ public: protected: friend class Print; friend class SLAPrint; + friend class Model; friend class ModelObject; explicit ModelInstance(const ModelInstance &rhs) = default; @@ -713,6 +715,7 @@ public: private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; void assign_new_unique_ids_recursive(); + void update_links_bottom_up_recursive(); friend class cereal::access; friend class UndoRedo::StackImpl; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4116ac34b..511c423e6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1191,6 +1191,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) : m_canvas(canvas) @@ -2350,6 +2352,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE)); break; + + +#ifdef __APPLE__ + case 'y': + case 'Y': +#else /* __APPLE__ */ + case WXK_CONTROL_Y: +#endif /* __APPLE__ */ + post_event(SimpleEvent(EVT_GLCANVAS_REDO)); + break; +#ifdef __APPLE__ + case 'z': + case 'Z': +#else /* __APPLE__ */ + case WXK_CONTROL_Z: +#endif /* __APPLE__ */ + post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); + break; + #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d39a910b3..d71817b34 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -126,6 +126,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); class GLCanvas3D { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f08480559..3e6f3763a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1552,6 +1552,8 @@ struct Plater::priv void take_snapshot(const std::string& snapshot_name) { this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } + void undo(); + void redo(); bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); @@ -1745,6 +1747,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); + view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); + view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); @@ -1785,6 +1789,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) camera.set_type(get_config("use_perspective_camera")); this->undo_redo_stack.initialize(model, view3D->get_canvas3d()->get_selection()); + this->take_snapshot(_(L("New Project"))); } void Plater::priv::update(bool force_full_scene_refresh) @@ -3490,6 +3495,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(); + } +} + +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?) +// wxGetApp().obj_list()->update_selections(); +// selection_changed(); + } +} + void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const { switch (btn_type) @@ -4001,15 +4026,10 @@ void Plater::send_gcode() } } -void Plater::take_snapshot(const std::string &snapshot_name) -{ - p->take_snapshot(snapshot_name); -} - -void Plater::take_snapshot(const wxString &snapshot_name) -{ - p->take_snapshot(snapshot_name); -} +void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::undo() { p->undo(); } +void Plater::redo() { p->redo(); } void Plater::on_extruders_change(int num_extruders) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9a6bcda7b..91f218f6c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -181,6 +181,8 @@ public: void take_snapshot(const std::string &snapshot_name); void take_snapshot(const wxString &snapshot_name); + void undo(); + void redo(); void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 74565a301..978bf149f 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -130,7 +130,7 @@ public: if (m_history.empty()) return false; auto it = std::lower_bound(m_history.begin(), m_history.end(), Interval(timestamp, timestamp)); - if (it == m_history.end() || it->begin() >= timestamp) { + if (it == m_history.end() || it->begin() > timestamp) { if (it == m_history.begin()) return false; -- it; @@ -265,7 +265,7 @@ public: std::string load(size_t timestamp) const { assert(! m_history.empty()); auto it = std::lower_bound(m_history.begin(), m_history.end(), MutableHistoryInterval(timestamp, timestamp)); - if (it == m_history.end() || it->begin() >= timestamp) { + if (it == m_history.end() || it->begin() > timestamp) { assert(it != m_history.begin()); -- it; } @@ -328,6 +328,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, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } @@ -566,7 +569,7 @@ void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Select // The initial time interval will be <0, 1) m_active_snapshot_time = SIZE_MAX; // let it overflow to zero in take_snapshot m_current_time = 0; - this->take_snapshot("New Project", model, selection); + this->take_snapshot("Internal - Initialized", model, selection); } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. @@ -584,7 +587,8 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo this->save_mutable_object(model); // this->save_mutable_object(selection); // Save the snapshot info - m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + 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(); } @@ -593,16 +597,38 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); - if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) + if (it_snapshot == m_snapshots.begin() || it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); + 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(selection.id(), selection); + model.update_links_bottom_up_recursive(); +// this->load_mutable_object(selection.id(), selection); this->m_active_snapshot_time = timestamp; } +bool StackImpl::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +{ + 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); + return true; +} + +bool StackImpl::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +{ + 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); + return true; +} + void StackImpl::collect_garbage() { // Purge objects with empty histories. @@ -623,6 +649,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, 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); } + const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } } // namespace UndoRedo diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index d452e777c..be024c282 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -44,6 +44,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, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + // Snapshot history (names with timestamps). const std::vector& snapshots() const;