WIP Undo / Redo: First Undo in the history of PrusaSlicer!

This commit is contained in:
bubnikv 2019-07-04 10:45:41 +02:00
parent e2a670218b
commit 5a2ace1a6e
8 changed files with 108 additions and 15 deletions

View file

@ -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<const t_model_material_id, ModelMaterial*> &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;

View file

@ -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;

View file

@ -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__ */

View file

@ -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
{

View file

@ -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<GUI::Selection&>(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<GUI::Selection&>(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)
{

View file

@ -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);

View file

@ -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<Snapshot>& 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<Slic3r::Model, Slic3r::Model>(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<Slic3r::Model, Slic3r::Model>(ObjectID(it_snapshot->model_object_id), model);
this->load_mutable_object<Slic3r::GUI::Selection, Slic3r::GUI::Selection>(selection.id(), selection);
model.update_links_bottom_up_recursive();
// this->load_mutable_object<Slic3r::GUI::Selection, Slic3r::GUI::Selection>(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<Snapshot>& Stack::snapshots() const { return pimpl->snapshots(); }
} // namespace UndoRedo

View file

@ -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<Snapshot>& snapshots() const;