WIP Undo / Redo : serialization / deserialization of object selection.

This commit is contained in:
bubnikv 2019-07-04 14:35:04 +02:00
parent 5a2ace1a6e
commit 1798e2a84c
6 changed files with 85 additions and 58 deletions

View file

@ -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<class Archive> void serialize(Archive &ar) { ar(id); }
};

View file

@ -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<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();
}
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<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?)
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

View file

@ -311,6 +311,24 @@ void Selection::add_all()
this->set_bounding_boxes_dirty();
}
void Selection::set_deserialized(EMode mode, const std::vector<std::pair<ObjectID, ObjectID>> &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<ObjectID, ObjectID>(id.volume_id, id.instance_id)))
this->do_add_volume(i);
}
update_type();
this->set_bounding_boxes_dirty();
}
void Selection::clear()
{
if (!m_valid)

View file

@ -3,7 +3,6 @@
#include <set>
#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<unsigned int> 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<std::pair<ObjectID, ObjectID>> &volumes_and_instances);
// Update the selection based on the new instance IDs.
void instances_changed(const std::vector<size_t> &instance_ids_selected);
// Update the selection based on the map from old indices to new indices after m_volumes changed.

View file

@ -8,6 +8,7 @@
#include <cereal/types/polymorphic.hpp>
#include <cereal/types/map.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/archives/binary.hpp>
#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<Snapshot>& 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<typename T, typename T_AS> ObjectID save_mutable_object(const T &object);
template<typename T> ObjectID save_immutable_object(std::shared_ptr<const T> &object);
template<typename T> 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<StackImpl, cereal::BinaryInputArchive>;
@ -469,28 +469,6 @@ namespace cereal
ar(id);
ptr = stack.load_immutable_object<T>(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 <class T> 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 <libslic3r/Model.hpp>
@ -585,15 +563,22 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo
}
// Take new snapshots.
this->save_mutable_object<Slic3r::Model, Slic3r::Model>(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<Selection, Selection>(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<Slic3r::Model, Slic3r::Model>(ObjectID(it_snapshot->model_object_id), model);
model.update_links_bottom_up_recursive();
// this->load_mutable_object<Slic3r::GUI::Selection, Slic3r::GUI::Selection>(selection.id(), selection);
m_selection.volumes_and_instances.clear();
this->load_mutable_object<Selection, Selection>(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<Snapshot>& Stack::snapshots() const { return pimpl->snapshots(); }

View file

@ -4,6 +4,8 @@
#include <memory>
#include <string>
#include <libslic3r/ObjectID.hpp>
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<std::pair<ObjectID, ObjectID>> volumes_and_instances;
template<class Archive> 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<Snapshot>& 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<StackImpl> pimpl;