From 3a74e7ab69fab1080f4245bfbf972cfe0b851129 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 17 Jul 2019 15:48:53 +0200 Subject: [PATCH 01/17] WIP: Undo / Redo memory limiting by releasing the least recently used snapshots. Memory limit set to 10% of physical system memory. --- src/admesh/stl.h | 8 ++ src/libslic3r/TriangleMesh.cpp | 6 + src/libslic3r/TriangleMesh.hpp | 2 + src/libslic3r/Utils.hpp | 2 + src/libslic3r/utils.cpp | 82 +++++++++++- src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/GUI_ObjectList.cpp | 16 +-- src/slic3r/GUI/Plater.cpp | 112 ++++++++++------ src/slic3r/GUI/Plater.hpp | 38 +++++- src/slic3r/Utils/UndoRedo.cpp | 207 ++++++++++++++++++++++++++---- src/slic3r/Utils/UndoRedo.hpp | 12 ++ 12 files changed, 411 insertions(+), 82 deletions(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 2ac6c7fd2..9b1146f8d 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -127,6 +127,10 @@ struct stl_file { this->stats.reset(); } + size_t memsize() const { + return sizeof(*this) + sizeof(stl_facet) * facet_start.size() + sizeof(stl_neighbors) * neighbors_start.size(); + } + std::vector facet_start; std::vector neighbors_start; // Statistics @@ -139,6 +143,10 @@ struct indexed_triangle_set void clear() { indices.clear(); vertices.clear(); } + size_t memsize() const { + return sizeof(*this) + sizeof(stl_triangle_vertex_indices) * indices.size() + sizeof(stl_vertex) * vertices.size(); + } + std::vector indices; std::vector vertices; //FIXME add normals once we get rid of the stl_file from TriangleMesh completely. diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index fbfff90fb..d782bc5ac 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -607,6 +607,12 @@ void TriangleMesh::require_shared_vertices() BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } +size_t TriangleMesh::memsize() const +{ + size_t memsize = 8 + this->stl.memsize() + this->its.memsize(); + return memsize; +} + void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) { mesh = _mesh; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 5dd2597a5..d2b688638 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -67,6 +67,8 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } bool is_splittable() const; + // Estimate of the memory occupied by this structure. + size_t memsize() const; stl_file stl; indexed_triangle_set its; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 3b30e981c..09e24a475 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -21,6 +21,8 @@ extern std::string format_memsize_MB(size_t n); // The string is non-empty only if the loglevel >= info (3). extern std::string log_memory_info(); extern void disable_multi_threading(); +// Returns the size of physical memory (RAM) in bytes. +extern size_t total_physical_memory(); // Set a path with GUI resource files. void set_var_dir(const std::string &path); diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 519763731..f9a044338 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -7,10 +7,15 @@ #include #ifdef WIN32 -#include -#include + #include + #include #else -#include + #include + #include + #include + #ifdef BSD + #include + #endif #endif #include @@ -467,4 +472,75 @@ std::string log_memory_info() } #endif +// Returns the size of physical memory (RAM) in bytes. +// http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system +size_t total_physical_memory() +{ +#if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__)) + // Cygwin under Windows. ------------------------------------ + // New 64-bit MEMORYSTATUSEX isn't available. Use old 32.bit + MEMORYSTATUS status; + status.dwLength = sizeof(status); + GlobalMemoryStatus( &status ); + return (size_t)status.dwTotalPhys; +#elif defined(_WIN32) + // Windows. ------------------------------------------------- + // Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx( &status ); + return (size_t)status.ullTotalPhys; +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) + // UNIX variants. ------------------------------------------- + // Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM + +#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)) + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; // OSX. --------------------- +#elif defined(HW_PHYSMEM64) + mib[1] = HW_PHYSMEM64; // NetBSD, OpenBSD. --------- +#endif + int64_t size = 0; // 64-bit + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (size_t)size; + return 0L; // Failed? + +#elif defined(_SC_AIX_REALMEM) + // AIX. ----------------------------------------------------- + return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L; + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + // FreeBSD, Linux, OpenBSD, and Solaris. -------------------- + return (size_t)sysconf( _SC_PHYS_PAGES ) * + (size_t)sysconf( _SC_PAGESIZE ); + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE) + // Legacy. -------------------------------------------------- + return (size_t)sysconf( _SC_PHYS_PAGES ) * + (size_t)sysconf( _SC_PAGE_SIZE ); + +#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM)) + // DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_REALMEM) + mib[1] = HW_REALMEM; // FreeBSD. ----------------- +#elif defined(HW_PYSMEM) + mib[1] = HW_PHYSMEM; // Others. ------------------ +#endif + unsigned int size = 0; // 32-bit + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (size_t)size; + return 0L; // Failed? +#endif // sysctl and sysconf variants + +#else + return 0L; // Unknown OS. +#endif +} + }; // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 8a376c3a3..b4e7bc2b2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -534,7 +534,7 @@ void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_max }); } -void GUI_App::load_project(wxWindow *parent, wxString& input_file) +void GUI_App::load_project(wxWindow *parent, wxString& input_file) const { input_file.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), @@ -546,7 +546,7 @@ void GUI_App::load_project(wxWindow *parent, wxString& input_file) input_file = dialog.GetPath(); } -void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) +void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const { input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index e69503ff8..2f0e8bcf3 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -121,8 +121,8 @@ public: void recreate_GUI(); void system_info(); void keyboard_shortcuts(); - void load_project(wxWindow *parent, wxString& input_file); - void import_model(wxWindow *parent, wxArrayString& input_files); + void load_project(wxWindow *parent, wxString& input_file) const; + void import_model(wxWindow *parent, wxArrayString& input_files) const; static bool catch_error(std::function cb, const std::string& err); void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 9e681257c..3f3256c42 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -71,9 +71,6 @@ static void take_snapshot(const wxString& snapshot_name) wxGetApp().plater()->take_snapshot(snapshot_name); } -static void suppress_snapshots(){ wxGetApp().plater()->suppress_snapshots(); } -static void allow_snapshots() { wxGetApp().plater()->allow_snapshots(); } - ObjectList::ObjectList(wxWindow* parent) : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE), m_parent(parent) @@ -2406,8 +2403,7 @@ void ObjectList::remove() wxDataViewItem parent = wxDataViewItem(0); - take_snapshot(_(L("Delete Selected"))); - suppress_snapshots(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete Selected"))); for (auto& item : sels) { @@ -2429,8 +2425,6 @@ void ObjectList::remove() if (parent) select_item(parent); - - allow_snapshots(); } void ObjectList::del_layer_range(const t_layer_height_range& range) @@ -2505,8 +2499,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre t_layer_height_range new_range = { midl_layer, next_range.second }; - take_snapshot(_(L("Add New Layers Range"))); - suppress_snapshots(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add New Layers Range"))); // create new 2 layers instead of deleted one @@ -2521,7 +2514,6 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre new_range = { current_range.second, midl_layer }; ranges[new_range] = get_default_layer_config(obj_idx); add_layer_item(new_range, layers_item, layer_idx); - allow_snapshots(); } else { @@ -3531,7 +3523,7 @@ void ObjectList::update_after_undo_redo() m_prevent_list_events = true; m_prevent_canvas_selection_update = true; - suppress_snapshots(); + Plater::SuppressSnapshots suppress(wxGetApp().plater()); // Unselect all objects before deleting them, so that no change of selection is emitted during deletion. this->UnselectAll(); @@ -3543,8 +3535,6 @@ void ObjectList::update_after_undo_redo() ++obj_idx; } - allow_snapshots(); - #ifndef __WXOSX__ selection_changed(); #endif /* __WXOSX__ */ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f5f245471..11055af11 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1255,13 +1255,9 @@ const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { - plater->take_snapshot(_(L("Load Files"))); - std::vector paths; - for (const auto &filename : filenames) { fs::path path(into_path(filename)); - if (std::regex_match(path.string(), pattern_drop)) { paths.push_back(std::move(path)); } else { @@ -1269,6 +1265,23 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi } } + wxString snapshot_label; + assert(! paths.empty()); + if (paths.size() == 1) { + snapshot_label = _(L("Load File")); + snapshot_label += ": "; + snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str()); + } else { + snapshot_label = _(L("Load Files")); + snapshot_label += ": "; + snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str()); + for (size_t i = 1; i < paths.size(); ++ i) { + snapshot_label += ", "; + snapshot_label += wxString::FromUTF8(paths[i].filename().string().c_str()); + } + } + Plater::TakeSnapshot snapshot(plater, snapshot_label); + // FIXME: when drag and drop is done on a .3mf or a .amf file we should clear the plater for consistence with the open project command // (the following call to plater->load_files() will load the config data, if present) @@ -1595,7 +1608,7 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~priv(); - void update(bool force_full_scene_refresh = false); + void update(bool force_full_scene_refresh = false, bool force_background_processing_update = false); void select_view(const std::string& direction); void select_view_3D(const std::string& name); void select_next_view_3D(); @@ -1634,6 +1647,8 @@ struct Plater::priv return; assert(this->m_prevent_snapshots >= 0); this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); + this->undo_redo_stack.release_least_recently_used(); + BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack.memsize()) << log_memory_info(); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } int get_active_snapshot_index(); @@ -1734,7 +1749,7 @@ private: void update_fff_scene(); void update_sla_scene(); - void update_after_undo_redo(); + void update_after_undo_redo(bool temp_snapshot_was_taken = false); // path to project file stored with no extension wxString m_project_filename; @@ -1888,7 +1903,7 @@ Plater::priv::~priv() delete config; } -void Plater::priv::update(bool force_full_scene_refresh) +void Plater::priv::update(bool force_full_scene_refresh, bool force_background_processing_update) { // the following line, when enabled, causes flickering on NVIDIA graphics cards // wxWindowUpdateLocker freeze_guard(q); @@ -1901,7 +1916,7 @@ void Plater::priv::update(bool force_full_scene_refresh) } unsigned int update_status = 0; - if (this->printer_technology == ptSLA) + if (this->printer_technology == ptSLA || force_background_processing_update) // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. update_status = this->update_background_process(false); @@ -2459,7 +2474,10 @@ void Plater::priv::remove(size_t obj_idx) void Plater::priv::delete_object_from_model(size_t obj_idx) { - this->take_snapshot(_(L("Delete Object"))); + wxString snapshot_label = _(L("Delete Object")); + if (! model.objects[obj_idx]->name.empty()) + snapshot_label += ": " + wxString::FromUTF8(model.objects[obj_idx]->name.c_str()); + Plater::TakeSnapshot snapshot(q, snapshot_label); model.delete_object(obj_idx); update(); object_list_changed(); @@ -2467,7 +2485,7 @@ void Plater::priv::delete_object_from_model(size_t obj_idx) void Plater::priv::reset() { - this->take_snapshot(_(L("Reset Project"))); + Plater::TakeSnapshot snapshot(q, _(L("Reset Project"))); set_project_filename(wxEmptyString); @@ -2663,7 +2681,7 @@ void Plater::priv::split_object() Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part."))); else { - this->take_snapshot(_(L("Split to Objects"))); + Plater::TakeSnapshot snapshot(q, _(L("Split to Objects"))); unsigned int counter = 1; for (ModelObject* m : new_objects) @@ -2903,7 +2921,7 @@ void Plater::priv::update_sla_scene() void Plater::priv::reload_from_disk() { - this->take_snapshot(_(L("Reload from Disk"))); + Plater::TakeSnapshot snapshot(q, _(L("Reload from Disk"))); const auto &selection = get_selection(); const auto obj_orig_idx = selection.get_object_idx(); @@ -2939,7 +2957,7 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = if (obj_idx < 0) return; - this->take_snapshot(_(L("Fix Throught NetFabb"))); + Plater::TakeSnapshot snapshot(q, _(L("Fix Throught NetFabb"))); fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx); this->update(); @@ -3603,7 +3621,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const int Plater::priv::get_active_snapshot_index() { - const size_t& active_snapshot_time = this->undo_redo_stack.active_snapshot_time(); + const size_t active_snapshot_time = this->undo_redo_stack.active_snapshot_time(); const std::vector& ss_stack = this->undo_redo_stack.snapshots(); const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time)); return it - ss_stack.begin(); @@ -3611,8 +3629,9 @@ int Plater::priv::get_active_snapshot_index() void Plater::priv::undo() { + bool temp_snapshot_was_taken = this->undo_redo_stack.temp_snapshot_active(); if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection())) - this->update_after_undo_redo(); + this->update_after_undo_redo(temp_snapshot_was_taken); } void Plater::priv::redo() @@ -3623,8 +3642,9 @@ void Plater::priv::redo() void Plater::priv::undo_to(size_t time_to_load) { + bool temp_snapshot_was_taken = this->undo_redo_stack.temp_snapshot_active(); if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), time_to_load)) - this->update_after_undo_redo(); + this->update_after_undo_redo(temp_snapshot_was_taken); } void Plater::priv::redo_to(size_t time_to_load) @@ -3633,10 +3653,16 @@ void Plater::priv::redo_to(size_t time_to_load) this->update_after_undo_redo(); } -void Plater::priv::update_after_undo_redo() +void Plater::priv::update_after_undo_redo(bool /* temp_snapshot_was_taken */) { this->view3D->get_canvas3d()->get_selection().clear(); - this->update(false); // update volumes from the deserializd model + // Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies). + this->update(false, true); + // Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot. + //if (temp_snapshot_was_taken) + // Release the old snapshots always, as it may have happened, that some of the triangle meshes got deserialized from the snapshot, while some + // triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size. + this->undo_redo_stack.release_least_recently_used(); //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); @@ -3644,6 +3670,8 @@ void Plater::priv::update_after_undo_redo() //FIXME what about the state of the manipulators? //FIXME what about the focus? Cursor in the side panel? + + BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot reloaded. Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack.memsize()) << log_memory_info(); } void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const @@ -3683,10 +3711,12 @@ void Plater::new_project() void Plater::load_project() { - this->take_snapshot(_(L("Load Project"))); - + // Ask user for a project file name. wxString input_file; wxGetApp().load_project(this, input_file); + // Take the Undo / Redo snapshot. + Plater::TakeSnapshot snapshot(this, _(L("Load Project")) + ": " + wxString::FromUTF8(into_path(input_file).stem().string().c_str())); + // And finally load the new project. load_project(input_file); } @@ -3710,13 +3740,28 @@ void Plater::add_model() if (input_files.empty()) return; - this->take_snapshot(_(L("Add object(s)"))); + std::vector paths; + for (const auto &file : input_files) + paths.push_back(into_path(file)); - std::vector input_paths; - for (const auto &file : input_files) { - input_paths.push_back(into_path(file)); - } - load_files(input_paths, true, false); + wxString snapshot_label; + assert(! paths.empty()); + if (paths.size() == 1) { + snapshot_label = _(L("Import Object")); + snapshot_label += ": "; + snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str()); + } else { + snapshot_label = _(L("Import Objects")); + snapshot_label += ": "; + snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str()); + for (size_t i = 1; i < paths.size(); ++ i) { + snapshot_label += ", "; + snapshot_label += wxString::FromUTF8(paths[i].filename().string().c_str()); + } + } + + Plater::TakeSnapshot snapshot(this, snapshot_label); + load_files(paths, true, false); } void Plater::extract_config_from_project() @@ -3769,17 +3814,15 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo void Plater::remove_selected() { - this->take_snapshot(_(L("Delete Selected Objects"))); - this->suppress_snapshots(); + Plater::TakeSnapshot snapshot(this, _(L("Delete Selected Objects"))); this->p->view3D->delete_selected(); - this->allow_snapshots(); } void Plater::increase_instances(size_t num) { if (! can_increase_instances()) { return; } - this->take_snapshot(_(L("Increase Instances"))); + Plater::TakeSnapshot snapshot(this, _(L("Increase Instances"))); int obj_idx = p->get_selected_object_idx(); @@ -3815,7 +3858,7 @@ void Plater::decrease_instances(size_t num) { if (! can_decrease_instances()) { return; } - this->take_snapshot(_(L("Decrease Instances"))); + Plater::TakeSnapshot snapshot(this, _(L("Decrease Instances"))); int obj_idx = p->get_selected_object_idx(); @@ -3851,16 +3894,13 @@ void Plater::set_number_of_copies(/*size_t num*/) if (num < 0) return; - this->take_snapshot(wxString::Format(_(L("Set numbers of copies to %d")), num)); - this->suppress_snapshots(); + Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num)); int diff = (int)num - (int)model_object->instances.size(); if (diff > 0) increase_instances(diff); else if (diff < 0) decrease_instances(-diff); - - this->allow_snapshots(); } bool Plater::is_selection_empty() const @@ -3884,7 +3924,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe return; } - this->take_snapshot(_(L("Cut"))); + Plater::TakeSnapshot snapshot(this, _(L("Cut"))); wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index e73f88ef3..a4ced64a0 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -186,8 +186,6 @@ public: void take_snapshot(const std::string &snapshot_name); void take_snapshot(const wxString &snapshot_name); - void suppress_snapshots(); - void allow_snapshots(); void undo(); void redo(); void undo_to(int selection); @@ -235,10 +233,46 @@ public: const Camera& get_camera() const; + // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. + class SuppressSnapshots + { + public: + SuppressSnapshots(Plater *plater) : m_plater(plater) + { + m_plater->suppress_snapshots(); + } + ~SuppressSnapshots() + { + m_plater->allow_snapshots(); + } + private: + Plater *m_plater; + }; + + // ROII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot. + class TakeSnapshot + { + public: + TakeSnapshot(Plater *plater, const wxString &snapshot_name) : m_plater(plater) + { + m_plater->take_snapshot(snapshot_name); + m_plater->suppress_snapshots(); + } + ~TakeSnapshot() + { + m_plater->allow_snapshots(); + } + private: + Plater *m_plater; + }; + private: struct priv; std::unique_ptr p; + void suppress_snapshots(); + void allow_snapshots(); + friend class SuppressBackgroundProcessingUpdate; }; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 058062502..6910fa87b 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -18,6 +18,7 @@ #include #include +#include #include @@ -28,11 +29,11 @@ namespace Slic3r { namespace UndoRedo { -static std::string topmost_snapsnot_name = "@@@ Topmost @@@"; +static std::string topmost_snapshot_name = "@@@ Topmost @@@"; bool Snapshot::is_topmost() const { - return this->name == topmost_snapsnot_name; + return this->name == topmost_snapshot_name; } // Time interval, start is closed, end is open. @@ -53,9 +54,12 @@ public: bool operator<(const Interval &rhs) const { return (m_begin < rhs.m_begin) || (m_begin == rhs.m_begin && m_end < rhs.m_end); } bool operator==(const Interval &rhs) const { return m_begin == rhs.m_begin && m_end == rhs.m_end; } + void trim_begin(size_t new_begin) { m_begin = std::max(m_begin, new_begin); } void trim_end(size_t new_end) { m_end = std::min(m_end, new_end); } void extend_end(size_t new_end) { assert(new_end >= m_end); m_end = new_end; } + size_t memsize() const { return sizeof(this); } + private: size_t m_begin; size_t m_end; @@ -75,8 +79,15 @@ public: // If the history is empty, the ObjectHistory object could be released. virtual bool empty() = 0; + // Release all data before the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. + // Return the amount of memory released. + virtual size_t release_before_timestamp(size_t timestamp) = 0; // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. - virtual void release_after_timestamp(size_t timestamp) = 0; + // Return the amount of memory released. + virtual size_t release_after_timestamp(size_t timestamp) = 0; + + // Estimated size in memory, to be used to drop least recently used snapshots. + virtual size_t memsize() const = 0; #ifdef SLIC3R_UNDOREDO_DEBUG // Human readable debug information. @@ -96,21 +107,54 @@ public: // If the history is empty, the ObjectHistory object could be released. bool empty() override { return m_history.empty(); } - // Release all data after the given timestamp. The shared pointer is NOT released. - void release_after_timestamp(size_t timestamp) override { - assert(! m_history.empty()); - 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.begin()) { - auto it_prev = it; - -- it_prev; - assert(it_prev->begin() < timestamp); - // Trim the last interval with timestamp. - it_prev->trim_end(timestamp); + // Release all data before the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. + size_t release_before_timestamp(size_t timestamp) override { + size_t mem_released = 0; + if (! m_history.empty()) { + 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)); + // Find the first iterator with begin() < timestamp. + if (it == m_history.end()) + -- it; + while (it != m_history.begin() && it->begin() >= timestamp) + -- it; + if (it->begin() < timestamp && it->end() > timestamp) { + it->trim_begin(timestamp); + if (it != m_history.begin()) + -- it; + } + if (it->end() <= timestamp) { + auto it_end = ++ it; + for (it = m_history.begin(); it != it_end; ++ it) + mem_released += it->memsize(); + m_history.erase(m_history.begin(), it_end); + } + assert(this->valid()); } - m_history.erase(it, m_history.end()); - assert(this->valid()); + return mem_released; + } + + // Release all data after the given timestamp. The shared pointer is NOT released. + size_t release_after_timestamp(size_t timestamp) override { + size_t mem_released = 0; + if (! m_history.empty()) { + 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.begin()) { + auto it_prev = it; + -- it_prev; + assert(it_prev->begin() < timestamp); + // Trim the last interval with timestamp. + it_prev->trim_end(timestamp); + } + for (auto it2 = it; it2 != m_history.end(); ++ it2) + mem_released += it2->memsize(); + m_history.erase(it, m_history.end()); + assert(this->valid()); + } + return mem_released; } protected: @@ -138,6 +182,18 @@ public: bool is_immutable() const override { return true; } const void* immutable_object_ptr() const { return (const void*)m_shared_object.get(); } + // Estimated size in memory, to be used to drop least recently used snapshots. + size_t memsize() const override { + size_t memsize = sizeof(*this); + if (this->is_serialized()) + memsize += m_serialized.size(); + else if (m_shared_object.use_count() == 1) + // Only count the shared object's memsize into the total Undo / Redo stack memsize if it is referenced from the Undo / Redo stack only. + memsize += m_shared_object->memsize(); + memsize += m_history.size() * sizeof(Interval); + return memsize; + } + void save(size_t active_snapshot_time, size_t current_time) { assert(m_history.empty() || m_history.back().end() <= active_snapshot_time || // The snapshot of an immutable object may have already been taken from another mutable object. @@ -230,7 +286,8 @@ public: const Interval& interval() const { return m_interval; } size_t begin() const { return m_interval.begin(); } size_t end() const { return m_interval.end(); } - void trim_end(size_t timestamp) { m_interval.trim_end(timestamp); } + void trim_begin(size_t timestamp) { m_interval.trim_begin(timestamp); } + void trim_end (size_t timestamp) { m_interval.trim_end(timestamp); } void extend_end(size_t timestamp) { m_interval.extend_end(timestamp); } bool operator<(const MutableHistoryInterval& rhs) const { return m_interval < rhs.m_interval; } @@ -240,6 +297,13 @@ public: size_t size() const { return m_data->size; } size_t refcnt() const { return m_data->refcnt; } bool matches(const std::string& data) { return m_data->matches(data); } + size_t memsize() const { + return m_data->refcnt == 1 ? + // Count just the size of the snapshot data. + m_data->size : + // Count the size of the snapshot data divided by the number of references, rounded up. + (m_data->size + m_data->refcnt - 1) / m_data->refcnt; + } private: MutableHistoryInterval(const MutableHistoryInterval &rhs); @@ -270,6 +334,15 @@ public: bool is_mutable() const override { return true; } bool is_immutable() const override { return false; } + // Estimated size in memory, to be used to drop least recently used snapshots. + size_t memsize() const override { + size_t memsize = sizeof(*this); + memsize += m_history.size() * sizeof(MutableHistoryInterval); + for (const MutableHistoryInterval &interval : m_history) + memsize += interval.memsize(); + return memsize; + } + void save(size_t active_snapshot_time, size_t current_time, const std::string &data) { assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); if (m_history.empty() || m_history.back().end() < active_snapshot_time) { @@ -356,7 +429,17 @@ class StackImpl { public: // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. - StackImpl() : m_active_snapshot_time(0), m_current_time(0) {} + // Initially enable Undo / Redo stack to occupy maximum 10% of the total system physical memory. + StackImpl() : m_memory_limit(std::min(Slic3r::total_physical_memory() / 10, size_t(1 * 16384 * 65536))), m_active_snapshot_time(0), m_current_time(0) {} + + void set_memory_limit(size_t memsize) { m_memory_limit = memsize; } + + size_t memsize() const { + size_t memsize = 0; + for (const auto &object : m_objects) + memsize += object.second->memsize(); + return memsize; + } // The Undo / Redo stack is being initialized with an empty model and an empty selection. // The first snapshot cannot be removed. @@ -370,13 +453,15 @@ public: bool has_redo_snapshot() const; bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t jump_to_time); bool redo(Slic3r::Model &model, size_t jump_to_time); + void release_least_recently_used(); // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } // Timestamp of the active snapshot. size_t active_snapshot_time() const { return m_active_snapshot_time; } + bool temp_snapshot_active() const { return m_snapshots.back().timestamp == m_active_snapshot_time && ! m_snapshots.back().is_topmost_captured(); } - const Selection& selection_deserialized() const { return m_selection; } + const Selection& selection_deserialized() const { return m_selection; } //protected: template ObjectID save_mutable_object(const T &object); @@ -400,6 +485,7 @@ public: if (m_active_snapshot_time > m_snapshots.back().timestamp) out += ">>>\n"; out += "Current time: " + std::to_string(m_current_time) + "\n"; + out += "Total memory occupied: " + std::to_string(this->memsize()) + "\n"; return out; } void print() const { @@ -412,9 +498,10 @@ public: #ifndef NDEBUG bool valid() const { assert(! m_snapshots.empty()); + assert(m_snapshots.back().is_topmost()); auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); assert(it != m_snapshots.begin() && it != m_snapshots.end() && it->timestamp == m_active_snapshot_time); - assert(m_active_snapshot_time < m_snapshots.back().timestamp || m_snapshots.back().is_topmost()); + assert(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; @@ -436,6 +523,9 @@ private: } void collect_garbage(); + // Maximum memory allowed to be occupied by the Undo / Redo stack. If the limit is exceeded, + // least recently used snapshots will be released. + size_t m_memory_limit; // Each individual object (Model, ModelObject, ModelInstance, ModelVolume, Selection, TriangleMesh) // is stored with its own history, referenced by the ObjectID. Immutable objects do not provide // their own IDs, therefore there are temporary IDs generated for them and stored to m_shared_ptr_to_object_id. @@ -655,7 +745,7 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo m_active_snapshot_time = m_current_time; // Save snapshot info of the last "current" aka "top most" state, that is only being serialized // if undoing an action. Such a snapshot has an invalid Model ID assigned if it was not taken yet. - m_snapshots.emplace_back(topmost_snapsnot_name, m_active_snapshot_time, 0); + m_snapshots.emplace_back(topmost_snapshot_name, m_active_snapshot_time, 0); // Release empty objects from the history. this->collect_garbage(); assert(this->valid()); @@ -710,9 +800,10 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti } assert(time_to_load < m_active_snapshot_time); assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load))); + bool new_snapshot_taken = false; if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { // The current state is temporary. The current state needs to be captured to be redoable. - this->take_snapshot(topmost_snapsnot_name, model, selection); + this->take_snapshot(topmost_snapshot_name, model, selection); // The line above entered another topmost_snapshot_name. assert(m_snapshots.back().is_topmost()); assert(! m_snapshots.back().is_topmost_captured()); @@ -722,8 +813,15 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti //-- m_current_time; assert(m_snapshots.back().is_topmost()); assert(m_snapshots.back().is_topmost_captured()); + new_snapshot_taken = true; } this->load_snapshot(time_to_load, model); + if (new_snapshot_taken) { + // Release old snapshots if the memory allocated due to capturing the top most state is excessive. + // Don't release the snapshots here, release them first after the scene and background processing gets updated, as this will release some references + // to the shared TriangleMeshes. + //this->release_least_recently_used(); + } #ifdef SLIC3R_UNDOREDO_DEBUG std::cout << "After undo" << std::endl; this->print(); @@ -764,10 +862,70 @@ void StackImpl::collect_garbage() } } +void StackImpl::release_least_recently_used() +{ + assert(this->valid()); + size_t current_memsize = this->memsize(); +#ifdef SLIC3R_UNDOREDO_DEBUG + bool released = false; +#endif + while (current_memsize > m_memory_limit && m_snapshots.size() >= 3) { + // From which side to remove a snapshot? + assert(m_snapshots.front().timestamp < m_active_snapshot_time); + size_t mem_released = 0; + if (m_snapshots[1].timestamp == m_active_snapshot_time) { + // Remove the last snapshot. + for (auto it = m_objects.begin(); it != m_objects.end();) { + mem_released += it->second->release_after_timestamp(m_snapshots.back().timestamp); + if (it->second->empty()) { + if (it->second->immutable_object_ptr() != nullptr) + // Release the immutable object from the ptr to ObjectID map. + m_shared_ptr_to_object_id.erase(it->second->immutable_object_ptr()); + mem_released += it->second->memsize(); + it = m_objects.erase(it); + } else + ++ it; + } + m_snapshots.pop_back(); + m_snapshots.back().name = topmost_snapshot_name; + } else { + // Remove the first snapshot. + for (auto it = m_objects.begin(); it != m_objects.end();) { + mem_released += it->second->release_before_timestamp(m_snapshots[1].timestamp); + if (it->second->empty()) { + if (it->second->immutable_object_ptr() != nullptr) + // Release the immutable object from the ptr to ObjectID map. + m_shared_ptr_to_object_id.erase(it->second->immutable_object_ptr()); + mem_released += it->second->memsize(); + it = m_objects.erase(it); + } else + ++ it; + } + m_snapshots.erase(m_snapshots.begin()); + } + assert(current_memsize >= mem_released); + if (current_memsize >= mem_released) + current_memsize -= mem_released; + else + current_memsize = 0; +#ifdef SLIC3R_UNDOREDO_DEBUG + released = true; +#endif + } + assert(this->valid()); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After release_least_recently_used" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ +} + // Wrappers of the private implementation. Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} -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::set_memory_limit(size_t memsize) { pimpl->set_memory_limit(memsize); } +size_t Stack::memsize() const { return pimpl->memsize(); } +void Stack::release_least_recently_used() { pimpl->release_least_recently_used(); } +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); } 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, size_t time_to_load) { return pimpl->undo(model, selection, time_to_load); } @@ -776,6 +934,7 @@ const Selection& Stack::selection_deserialized() const { return pimpl->selection const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); } +bool Stack::temp_snapshot_active() const { return pimpl->temp_snapshot_active(); } } // namespace UndoRedo } // namespace Slic3r diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index f8bfda08c..606b8de74 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -55,6 +55,15 @@ public: Stack(); ~Stack(); + // Set maximum memory threshold. If the threshold is exceeded, least recently used snapshots are released. + void set_memory_limit(size_t memsize); + + // Estimate size of the RAM consumed by the Undo / Redo stack. + size_t memsize() const; + + // Release least recently used snapshots up to the memory limit set above. + void release_least_recently_used(); + // 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); @@ -78,6 +87,9 @@ public: // The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore // the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations. size_t active_snapshot_time() const; + // Temporary snapshot is active if the topmost snapshot is active and it has not been captured yet. + // In that case the Undo action will capture the last snapshot. + bool temp_snapshot_active() 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. From cc1338ce6ab60470586626660240667965cb9523 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 17 Jul 2019 16:00:09 +0200 Subject: [PATCH 02/17] Fix after merge --- src/slic3r/Utils/UndoRedo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 3ed7bd05e..51a811350 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -803,7 +803,7 @@ bool StackImpl::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selecti bool new_snapshot_taken = false; if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { // The current state is temporary. The current state needs to be captured to be redoable. - this->take_snapshot(topmost_snapsnot_name, model, selection, gizmos); + this->take_snapshot(topmost_snapshot_name, model, selection, gizmos); // The line above entered another topmost_snapshot_name. assert(m_snapshots.back().is_topmost()); assert(! m_snapshots.back().is_topmost_captured()); From cd95b52dcd0c12f5885abfea415ef8e05be37aba Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 18 Jul 2019 11:51:06 +0200 Subject: [PATCH 03/17] Undo / Redo memory conservation strategy: Release recoverable data starting from the objects of lowest ObjectID. (convex hulls are recoverable as well as the indexed triangle sets inside the TriangleMeshes or the triangle connectivity information). Now the top most snapshot (the temp one taken before Undo jump) will never be released. --- src/admesh/connect.cpp | 2 + src/libslic3r/Model.hpp | 24 ++++++- src/libslic3r/TriangleMesh.cpp | 28 ++++++++ src/libslic3r/TriangleMesh.hpp | 6 +- src/slic3r/Utils/UndoRedo.cpp | 115 ++++++++++++++++++++++++++++++--- 5 files changed, 162 insertions(+), 13 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index b86ec5055..d6de6ce6a 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -430,6 +430,8 @@ private: // floats of the first edge matches all six floats of the second edge. void stl_check_facets_exact(stl_file *stl) { + assert(stl->facet_start.size() == stl->neighbors_start.size()); + stl->stats.connected_edges = 0; stl->stats.connected_facets_1_edge = 0; stl->stats.connected_facets_2_edge = 0; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 7551bd8cb..37741a1f1 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -526,8 +526,23 @@ private: ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } - template void serialize(Archive &ar) { - ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); + template void load(Archive &ar) { + bool has_convex_hull; + ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + assert(m_mesh); + if (has_convex_hull) { + cereal::load_optional(ar, m_convex_hull); + if (! m_convex_hull && ! m_mesh->empty()) + // The convex hull was released from the Undo / Redo stack to conserve memory. Recalculate it. + this->calculate_convex_hull(); + } else + m_convex_hull.reset(); + } + template void save(Archive &ar) const { + bool has_convex_hull = m_convex_hull.get() != nullptr; + ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + if (has_convex_hull) + cereal::save_optional(ar, m_convex_hull); } }; @@ -747,4 +762,9 @@ void check_model_ids_equal(const Model &model1, const Model &model2); } // namespace Slic3r +namespace cereal +{ + template struct specialize {}; +} + #endif /* slic3r_Model_hpp_ */ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index d782bc5ac..c2fcb11bd 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -613,6 +613,34 @@ size_t TriangleMesh::memsize() const return memsize; } +// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released. +size_t TriangleMesh::release_optional() +{ + size_t memsize_released = sizeof(stl_neighbors) * this->stl.neighbors_start.size() + this->its.memsize(); + // The indexed triangle set may be recalculated using the stl_generate_shared_vertices() function. + this->its.clear(); + // The neighbors structure may be recalculated using the stl_check_facets_exact() function. + this->stl.neighbors_start.clear(); + return memsize_released; +} + +// Restore optional data possibly released by release_optional(). +void TriangleMesh::restore_optional() +{ + if (! this->stl.facet_start.empty()) { + // Save the old stats before calling stl_check_faces_exact, as it may modify the statistics. + stl_stats stats = this->stl.stats; + if (this->stl.neighbors_start.empty()) { + stl_reallocate(&this->stl); + stl_check_facets_exact(&this->stl); + } + if (this->its.vertices.empty()) + stl_generate_shared_vertices(&this->stl, this->its); + // Restore the old statistics. + this->stl.stats = stats; + } +} + void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) { mesh = _mesh; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index d2b688638..81390b79b 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -67,8 +67,12 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } bool is_splittable() const; - // Estimate of the memory occupied by this structure. + // Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation. size_t memsize() const; + // Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released. + size_t release_optional(); + // Restore optional data possibly released by release_optional(). + void restore_optional(); stl_file stl; indexed_triangle_set its; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 51a811350..10873ea89 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -25,6 +25,12 @@ #ifndef NDEBUG // #define SLIC3R_UNDOREDO_DEBUG #endif /* NDEBUG */ +#if 0 + // Stop at a fraction of the normal Undo / Redo stack size. + #define UNDO_REDO_DEBUG_LOW_MEM_FACTOR 10000 +#else + #define UNDO_REDO_DEBUG_LOW_MEM_FACTOR 1 +#endif namespace Slic3r { namespace UndoRedo { @@ -74,6 +80,9 @@ public: // Is the object captured by this history mutable or immutable? virtual bool is_mutable() const = 0; virtual bool is_immutable() const = 0; + // The object is optional, it may be released if the Undo / Redo stack memory grows over the limits. + virtual bool is_optional() const { return false; } + // If it is an immutable object, return its pointer. There is a map assigning a temporary ObjectID to the immutable object pointer. virtual const void* immutable_object_ptr() const { return nullptr; } // If the history is empty, the ObjectHistory object could be released. @@ -85,6 +94,10 @@ public: // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. // Return the amount of memory released. virtual size_t release_after_timestamp(size_t timestamp) = 0; + // Release all optional data of this history. + virtual size_t release_optional() = 0; + // Restore optional data possibly released by release_optional. + virtual void restore_optional() = 0; // Estimated size in memory, to be used to drop least recently used snapshots. virtual size_t memsize() const = 0; @@ -175,11 +188,13 @@ template class ImmutableObjectHistory : public ObjectHistory { public: - ImmutableObjectHistory(std::shared_ptr shared_object) : m_shared_object(shared_object) {} + ImmutableObjectHistory(std::shared_ptr shared_object, bool optional) : m_shared_object(shared_object), m_optional(optional) {} ~ImmutableObjectHistory() override {} bool is_mutable() const override { return false; } bool is_immutable() const override { return true; } + bool is_optional() const override { return m_optional; } + // If it is an immutable object, return its pointer. There is a map assigning a temporary ObjectID to the immutable object pointer. const void* immutable_object_ptr() const { return (const void*)m_shared_object.get(); } // Estimated size in memory, to be used to drop least recently used snapshots. @@ -216,6 +231,37 @@ public: return timestamp >= it->begin() && timestamp < it->end(); } + // Release all optional data of this history. + size_t release_optional() override { + size_t mem_released = 0; + if (m_optional) { + bool released = false; + if (this->is_serialized()) { + mem_released += m_serialized.size(); + m_serialized.clear(); + released = true; + } else if (m_shared_object.use_count() == 1) { + mem_released += m_shared_object->memsize(); + m_shared_object.reset(); + released = true; + } + if (released) { + mem_released += m_history.size() * sizeof(Interval); + m_history.clear(); + } + } else if (m_shared_object.use_count() == 1) { + // The object is in memory, but it is not shared with the scene. Let the object decide whether there is any optional data to release. + const_cast(m_shared_object.get())->release_optional(); + } + return mem_released; + } + + // Restore optional data possibly released by this->release_optional(). + void restore_optional() override { + if (m_shared_object.use_count() == 1) + const_cast(m_shared_object.get())->restore_optional(); + } + bool is_serialized() const { return m_shared_object.get() == nullptr; } const std::string& serialized_data() const { return m_serialized; } std::shared_ptr& shared_ptr(StackImpl &stack); @@ -240,6 +286,8 @@ private: // Either the source object is held by a shared pointer and the m_serialized field is empty, // or the shared pointer is null and the object is being serialized into m_serialized. std::shared_ptr m_shared_object; + // If this object is optional, then it may be deleted from the Undo / Redo stack and recalculated from other data (for example mesh convex hull). + bool m_optional; std::string m_serialized; }; @@ -375,6 +423,11 @@ public: return std::string(it->data(), it->data() + it->size()); } + // Currently all mutable snapshots are mandatory. + size_t release_optional() override { return 0; } + // Currently there is no way to release optional data from the mutable objects. + void restore_optional() override {} + #ifdef SLIC3R_UNDOREDO_DEBUG std::string format() override { std::string out = typeid(T).name(); @@ -430,7 +483,7 @@ class StackImpl public: // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. // Initially enable Undo / Redo stack to occupy maximum 10% of the total system physical memory. - StackImpl() : m_memory_limit(std::min(Slic3r::total_physical_memory() / 10, size_t(1 * 16384 * 65536))), m_active_snapshot_time(0), m_current_time(0) {} + StackImpl() : m_memory_limit(std::min(Slic3r::total_physical_memory() / 10, size_t(1 * 16384 * 65536 / UNDO_REDO_DEBUG_LOW_MEM_FACTOR))), m_active_snapshot_time(0), m_current_time(0) {} void set_memory_limit(size_t memsize) { m_memory_limit = memsize; } @@ -461,9 +514,9 @@ public: //protected: template ObjectID save_mutable_object(const T &object); - template ObjectID save_immutable_object(std::shared_ptr &object); + template ObjectID save_immutable_object(std::shared_ptr &object, bool optional); template T* load_mutable_object(const Slic3r::ObjectID id); - template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); + template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id, bool optional); template void load_mutable_object(const Slic3r::ObjectID id, T &target); #ifdef SLIC3R_UNDOREDO_DEBUG @@ -620,7 +673,11 @@ namespace cereal // store just the ObjectID to this stream. template void save(BinaryOutputArchive &ar, const std::shared_ptr &ptr) { - ar(cereal::get_user_data(ar).save_immutable_object(const_cast&>(ptr))); + ar(cereal::get_user_data(ar).save_immutable_object(const_cast&>(ptr), false)); + } + template void save_optional(BinaryOutputArchive &ar, const std::shared_ptr &ptr) + { + ar(cereal::get_user_data(ar).save_immutable_object(const_cast&>(ptr), true)); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object @@ -630,7 +687,14 @@ namespace cereal Slic3r::UndoRedo::StackImpl &stack = cereal::get_user_data(ar); size_t id; ar(id); - ptr = stack.load_immutable_object(Slic3r::ObjectID(id)); + ptr = stack.load_immutable_object(Slic3r::ObjectID(id), false); + } + template void load_optional(BinaryInputArchive &ar, std::shared_ptr &ptr) + { + Slic3r::UndoRedo::StackImpl &stack = cereal::get_user_data(ar); + size_t id; + ar(id); + ptr = stack.load_immutable_object(Slic3r::ObjectID(id), true); } } @@ -675,14 +739,16 @@ template ObjectID StackImpl::save_mutable_object(cons return object.id(); } -template ObjectID StackImpl::save_immutable_object(std::shared_ptr &object) +template ObjectID StackImpl::save_immutable_object(std::shared_ptr &object, bool optional) { // First allocate a temporary ObjectID for this pointer. ObjectID object_id = this->immutable_object_id(object); // and find or allocate a history stack for the ObjectID associated to this shared_ptr. auto it_object_history = m_objects.find(object_id); if (it_object_history == m_objects.end()) - it_object_history = m_objects.emplace_hint(it_object_history, object_id, std::unique_ptr>(new ImmutableObjectHistory(object))); + it_object_history = m_objects.emplace_hint(it_object_history, object_id, std::unique_ptr>(new ImmutableObjectHistory(object, optional))); + else + assert(it_object_history->second.get()->is_optional() == optional); // Then save the interval. static_cast*>(it_object_history->second.get())->save(m_active_snapshot_time, m_current_time); return object_id; @@ -695,13 +761,16 @@ template T* StackImpl::load_mutable_object(const Slic3r::ObjectID id return target; } -template std::shared_ptr StackImpl::load_immutable_object(const Slic3r::ObjectID id) +template std::shared_ptr StackImpl::load_immutable_object(const Slic3r::ObjectID id, bool optional) { // First find a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(id); - assert(it_object_history != m_objects.end()); + assert(optional || it_object_history != m_objects.end()); + if (it_object_history == m_objects.end()) + return std::shared_ptr(); auto *object_history = static_cast*>(it_object_history->second.get()); assert(object_history->has_snapshot(m_active_snapshot_time)); + object_history->restore_optional(); return object_history->shared_ptr(*this); } @@ -869,12 +938,32 @@ void StackImpl::release_least_recently_used() #ifdef SLIC3R_UNDOREDO_DEBUG bool released = false; #endif + // First try to release the optional immutable data (for example the convex hulls), + // or the shared vertices of triangle meshes. + for (auto it = m_objects.begin(); current_memsize > m_memory_limit && it != m_objects.end();) { + const void *ptr = it->second->immutable_object_ptr(); + size_t mem_released = it->second->release_optional(); + if (it->second->empty()) { + if (ptr != nullptr) + // Release the immutable object from the ptr to ObjectID map. + m_shared_ptr_to_object_id.erase(ptr); + mem_released += it->second->memsize(); + it = m_objects.erase(it); + } else + ++ it; + assert(current_memsize >= mem_released); + if (current_memsize >= mem_released) + current_memsize -= mem_released; + else + current_memsize = 0; + } while (current_memsize > m_memory_limit && m_snapshots.size() >= 3) { // From which side to remove a snapshot? assert(m_snapshots.front().timestamp < m_active_snapshot_time); size_t mem_released = 0; if (m_snapshots[1].timestamp == m_active_snapshot_time) { // Remove the last snapshot. +#if 0 for (auto it = m_objects.begin(); it != m_objects.end();) { mem_released += it->second->release_after_timestamp(m_snapshots.back().timestamp); if (it->second->empty()) { @@ -888,6 +977,12 @@ void StackImpl::release_least_recently_used() } m_snapshots.pop_back(); m_snapshots.back().name = topmost_snapshot_name; +#else + // Rather don't release the last snapshot as it will be very confusing to the user + // as of why he cannot jump to the top most state. The Undo / Redo stack maximum size + // should be set low enough to accomodate for the top most snapshot. + break; +#endif } else { // Remove the first snapshot. for (auto it = m_objects.begin(); it != m_objects.end();) { From 69ef6cf8067a2f10eb51e1570b1f9784557e734a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 18 Jul 2019 12:07:50 +0200 Subject: [PATCH 04/17] Fix for compilation on clang: Forward declarations of templates. --- src/libslic3r/Model.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 37741a1f1..c026863ec 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -17,6 +17,13 @@ #include #include +namespace cereal { + class BinaryInputArchive; + class BinaryOutputArchive; + template void load_optional(BinaryInputArchive &ar, std::shared_ptr &ptr); + template void save_optional(BinaryOutputArchive &ar, const std::shared_ptr &ptr); +} + namespace Slic3r { class Model; From a0ea96968d9097ad2f329e1e34bf207d955bed04 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 18 Jul 2019 17:41:47 +0200 Subject: [PATCH 05/17] Storing the active printer technology onto the Undo / Redo stack, remembering the last selected Printer profile for the SLA and FDM technologies separately, and activating them on Undo / Redo. When switching the technologies, user is asked whether to discard the modified profiles or not. --- src/libslic3r/Model.cpp | 25 ++++ src/libslic3r/Model.hpp | 6 + src/slic3r/GUI/GUI.cpp | 3 +- src/slic3r/GUI/GUI_App.cpp | 12 +- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/GUI_ObjectList.cpp | 15 +- src/slic3r/GUI/GUI_ObjectList.hpp | 1 - src/slic3r/GUI/GUI_ObjectManipulation.cpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 146 +++++++++---------- src/slic3r/GUI/Tab.cpp | 2 +- src/slic3r/Utils/UndoRedo.cpp | 20 +-- src/slic3r/Utils/UndoRedo.hpp | 14 +- 14 files changed, 141 insertions(+), 119 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 949f82c0a..66532e9ea 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1877,6 +1877,31 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO return false; } +extern bool model_has_multi_part_objects(const Model &model) +{ + for (const ModelObject *model_object : model.objects) + if (model_object->volumes.size() != 1 || ! model_object->volumes.front()->is_model_part()) + return true; + return false; +} + +extern bool model_has_advanced_features(const Model &model) +{ + auto config_is_advanced = [](const DynamicPrintConfig &config) { + return ! (config.empty() || (config.size() == 1 && config.cbegin()->first == "extruder")); + }; + for (const ModelObject *model_object : model.objects) { + // Is there more than one instance or advanced config data? + if (model_object->instances.size() > 1 || config_is_advanced(model_object->config)) + return true; + // Is there any modifier or advanced config data? + for (const ModelVolume* model_volume : model_object->volumes) + if (! model_volume->is_model_part() || config_is_advanced(model_volume->config)) + return true; + } + return false; +} + #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c026863ec..2b4a6d978 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -761,6 +761,12 @@ extern bool model_object_list_extended(const Model &model_old, const Model &mode // than the old ModelObject. extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type); +// If the model has multi-part objects, then it is currently not supported by the SLA mode. +// Either the model cannot be loaded, or a SLA printer has to be activated. +extern bool model_has_multi_part_objects(const Model &model); +// If the model has advanced features, then it cannot be processed in simple mode. +extern bool model_has_advanced_features(const Model &model); + #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 9a641c7c0..8f41ed5a3 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -135,8 +135,7 @@ void config_wizard(int reason) wxGetApp().load_current_presets(); - if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && - wxGetApp().obj_list()->has_multi_part_objects()) + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && model_has_multi_part_objects(wxGetApp().model())) { show_info(nullptr, _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b4e7bc2b2..02290e83d 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -854,7 +854,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) // This is called when closing the application, when loading a config file or when starting the config wizard // to notify the user whether he is aware that some preset changes will be lost. -bool GUI_App::check_unsaved_changes() +bool GUI_App::check_unsaved_changes(const wxString &header) { wxString dirty; PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); @@ -868,8 +868,12 @@ bool GUI_App::check_unsaved_changes() // No changes, the application may close or reload presets. return true; // Ask the user. + wxString message; + if (! header.empty()) + message = header + "\n\n"; + message += _(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?")); wxMessageDialog dialog(mainframe, - _(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?")), + message, wxString(SLIC3R_APP_NAME) + " - " + _(L("Unsaved Presets")), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); return dialog.ShowModal() == wxID_YES; @@ -944,9 +948,9 @@ Plater* GUI_App::plater() return plater_; } -ModelObjectPtrs* GUI_App::model_objects() +Model& GUI_App::model() { - return &plater_->model().objects; + return plater_->model(); } wxNotebook* GUI_App::tab_panel() const diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 2f0e8bcf3..8d0bcfe2f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -139,7 +139,7 @@ public: void update_mode(); void add_config_menu(wxMenuBar *menu); - bool check_unsaved_changes(); + bool check_unsaved_changes(const wxString &header = wxString()); bool checked_tab(Tab* tab); void load_current_presets(); @@ -158,7 +158,7 @@ public: ObjectList* obj_list(); ObjectLayers* obj_layers(); Plater* plater(); - std::vector *model_objects(); + Model& model(); AppConfig* app_config{ nullptr }; PresetBundle* preset_bundle{ nullptr }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3f3256c42..f7abb4128 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2603,7 +2603,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay void ObjectList::init_objects() { - m_objects = wxGetApp().model_objects(); + m_objects = &wxGetApp().model().objects; } bool ObjectList::multiple_selection() const @@ -3080,19 +3080,6 @@ void ObjectList::last_volume_is_deleted(const int obj_idx) volume->config.set_key_value("extruder", new ConfigOptionInt(0)); } -bool ObjectList::has_multi_part_objects() -{ - if (!m_objects_model->IsEmpty()) { - wxDataViewItemArray items; - m_objects_model->GetChildren(wxDataViewItem(0), items); - - for (auto& item : items) - if (m_objects_model->GetItemByType(item, itVolume)) - return true; - } - return false; -} - /* #lm_FIXME_delete_after_testing void ObjectList::update_settings_items() { diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 2d7e9f5f1..5f5d5d625 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -317,7 +317,6 @@ public: void change_part_type(); void last_volume_is_deleted(const int obj_idx); - bool has_multi_part_objects(); void update_settings_items(); void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index fcb047139..308b3f208 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -25,7 +25,7 @@ static double get_volume_min_z(const GLVolume* volume) const Transform3f& world_matrix = volume->world_matrix().cast(); // need to get the ModelVolume pointer - const ModelObject* mo = wxGetApp().model_objects()->at(volume->composite_id.object_id); + const ModelObject* mo = wxGetApp().model().objects[volume->composite_id.object_id]; const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id]; const TriangleMesh& hull = mv->get_convex_hull(); @@ -466,7 +466,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.; } else { m_new_rotation = volume->get_instance_rotation() * (180. / M_PI); - m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct((*wxGetApp().model_objects())[volume->object_idx()]->raw_mesh_bounding_box().size()); + m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); m_new_scale = volume->get_instance_scaling_factor() * 100.; } @@ -779,7 +779,7 @@ void ObjectManipulation::change_size_value(int axis, double value) else if (selection.is_single_full_instance()) ref_size = m_world_coordinates ? selection.get_unscaled_instance_bounding_box().size() : - (*wxGetApp().model_objects())[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size(); + wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size(); this->do_scale(axis, 100. * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); @@ -902,7 +902,7 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) return; } // Bake the rotation into the meshes of the object. - (*wxGetApp().model_objects())[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id); + wxGetApp().model().objects[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id); // Update the 3D scene, selections etc. wxGetApp().plater()->update(); // Recalculate cached values at this panel, refresh the screen. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index cb996a104..5a42cbd31 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -29,7 +29,7 @@ void GLGizmoFlatten::on_set_state() // m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id m_model_object = nullptr; - for (const auto mo : *wxGetApp().model_objects()) { + for (const auto mo : wxGetApp().model().objects) { if (mo->id() == m_model_object_id) { m_model_object = mo; break; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 833ba3ee2..8027906a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1049,7 +1049,7 @@ void GLGizmoSlaSupports::on_set_state() // we should recover it from the object id const ModelObject* old_model_object = m_model_object; m_model_object = nullptr; - for (const auto mo : *wxGetApp().model_objects()) { + for (const auto mo : wxGetApp().model().objects) { if (mo->id() == m_current_mesh_object_id) { m_model_object = mo; break; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7c7488c6a..559145eba 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1646,16 +1646,18 @@ struct Plater::priv if (this->m_prevent_snapshots > 0) return; assert(this->m_prevent_snapshots >= 0); - this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager()); + this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology); this->undo_redo_stack.release_least_recently_used(); + // Save the last active preset name of a particular printer technology. + ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack.memsize()) << log_memory_info(); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } int get_active_snapshot_index(); void undo(); void redo(); - void undo_to(size_t time_to_load); - void redo_to(size_t time_to_load); + void undo_redo_to(size_t time_to_load); + void suppress_snapshots() { this->m_prevent_snapshots++; } void allow_snapshots() { this->m_prevent_snapshots--; } @@ -1749,10 +1751,13 @@ private: void update_fff_scene(); void update_sla_scene(); + void undo_redo_to(std::vector::const_iterator it_snapshot); void update_after_undo_redo(bool temp_snapshot_was_taken = false); // path to project file stored with no extension wxString m_project_filename; + std::string m_last_fff_printer_profile_name; + std::string m_last_sla_printer_profile_name; }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); @@ -2102,66 +2107,22 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } } - else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf)) - { - bool advanced = false; - for (const ModelObject* model_object : model.objects) + else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { + wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), + _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) { - // is there more than one instance ? - if (model_object->instances.size() > 1) - { - advanced = true; - break; - } - - // is there any advanced config data ? - auto opt_keys = model_object->config.keys(); - if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder"))) - { - advanced = true; - break; - } - - // is there any modifier ? - for (const ModelVolume* model_volume : model_object->volumes) - { - if (!model_volume->is_model_part()) - { - advanced = true; - break; - } - - // is there any advanced config data ? - opt_keys = model_volume->config.keys(); - if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder"))) - { - advanced = true; - break; - } - } - - if (advanced) - break; - } - - if (advanced) - { - wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), - _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) - { - Slic3r::GUI::wxGetApp().save_mode(comAdvanced); - view3D->set_as_dirty(); - } - else - return obj_idxs; + Slic3r::GUI::wxGetApp().save_mode(comAdvanced); + view3D->set_as_dirty(); } + else + return obj_idxs; } - for (ModelObject* model_object : model.objects) { - model_object->center_around_origin(false); - model_object->ensure_on_bed(); - } + for (ModelObject* model_object : model.objects) { + model_object->center_around_origin(false); + model_object->ensure_on_bed(); + } // check multi-part object adding for the SLA-printing if (printer_technology == ptSLA) @@ -3629,28 +3590,58 @@ int Plater::priv::get_active_snapshot_index() void Plater::priv::undo() { - bool temp_snapshot_was_taken = this->undo_redo_stack.temp_snapshot_active(); - if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager())) - this->update_after_undo_redo(temp_snapshot_was_taken); + const std::vector &snapshots = this->undo_redo_stack.snapshots(); + auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack.active_snapshot_time())); + if (-- it_current != snapshots.begin()) + this->undo_redo_to(it_current); } void Plater::priv::redo() { - if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager())) - this->update_after_undo_redo(); + const std::vector &snapshots = this->undo_redo_stack.snapshots(); + auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack.active_snapshot_time())); + if (++ it_current != snapshots.end()) + this->undo_redo_to(it_current); } -void Plater::priv::undo_to(size_t time_to_load) +void Plater::priv::undo_redo_to(size_t time_to_load) { - bool temp_snapshot_was_taken = this->undo_redo_stack.temp_snapshot_active(); - if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load)) - this->update_after_undo_redo(temp_snapshot_was_taken); + const std::vector &snapshots = this->undo_redo_stack.snapshots(); + auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(time_to_load)); + assert(it_current != snapshots.end()); + this->undo_redo_to(it_current); } -void Plater::priv::redo_to(size_t time_to_load) -{ - if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load)) - this->update_after_undo_redo(); +void Plater::priv::undo_redo_to(std::vector::const_iterator it_snapshot) +{ + bool temp_snapshot_was_taken = this->undo_redo_stack.temp_snapshot_active(); + PrinterTechnology new_printer_technology = it_snapshot->printer_technology; + bool printer_technology_changed = this->printer_technology != new_printer_technology; + if (printer_technology_changed) { + // Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type. + std::string s_pt = (it_snapshot->printer_technology == ptFFF) ? "FFF" : "SLA"; + if (! wxGetApp().check_unsaved_changes(from_u8((boost::format(_utf8( + L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."))) % s_pt).str()))) + // Don't switch the profiles. + return; + } + // Save the last active preset name of a particular printer technology. + ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); + // Do the jump in time. + if (it_snapshot->timestamp < this->undo_redo_stack.active_snapshot_time() ? + this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, it_snapshot->timestamp) : + this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), it_snapshot->timestamp)) { + if (printer_technology_changed) { + // Switch to the other printer technology. Switch to the last printer active for that particular technology. + AppConfig *app_config = wxGetApp().app_config; + app_config->set("presets", "printer", (new_printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name); + wxGetApp().preset_bundle->load_presets(*app_config); + // Load the currently selected preset into the GUI, update the preset selection box. + // This also switches the printer technology based on the printer technology of the active printer profile. + wxGetApp().load_current_presets(); + } + this->update_after_undo_redo(temp_snapshot_was_taken); + } } void Plater::priv::update_after_undo_redo(bool /* temp_snapshot_was_taken */) @@ -3669,6 +3660,13 @@ void Plater::priv::update_after_undo_redo(bool /* temp_snapshot_was_taken */) wxGetApp().obj_list()->update_after_undo_redo(); + if (wxGetApp().get_mode() == comSimple && model_has_advanced_features(this->model)) { + // If the user jumped to a snapshot that require user interface with advanced features, switch to the advanced mode without asking. + // There is a little risk of surprising the user, as he already must have had the advanced or expert mode active for such a snapshot to be taken. + Slic3r::GUI::wxGetApp().save_mode(comAdvanced); + view3D->set_as_dirty(); + } + //FIXME what about the state of the manipulators? //FIXME what about the focus? Cursor in the side panel? @@ -4230,7 +4228,7 @@ void Plater::undo_to(int selection) } const int idx = p->get_active_snapshot_index() - selection - 1; - p->undo_to(p->undo_redo_stack.snapshots()[idx].timestamp); + p->undo_redo_to(p->undo_redo_stack.snapshots()[idx].timestamp); } void Plater::redo_to(int selection) { @@ -4240,7 +4238,7 @@ void Plater::redo_to(int selection) } const int idx = p->get_active_snapshot_index() + selection + 1; - p->redo_to(p->undo_redo_stack.snapshots()[idx].timestamp); + p->undo_redo_to(p->undo_redo_stack.snapshots()[idx].timestamp); } bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 7ab564beb..b52f9dd46 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2904,7 +2904,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr // Because of we can't to print the multi-part objects with SLA technology. bool Tab::may_switch_to_SLA_preset() { - if (wxGetApp().obj_list()->has_multi_part_objects()) + if (model_has_multi_part_objects(wxGetApp().model())) { show_info( parent(), _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 10873ea89..c4a082fb6 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -495,12 +495,12 @@ 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, const Slic3r::GUI::GLGizmosManager& gizmos); + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology); void load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos); bool has_undo_snapshot() const; bool has_redo_snapshot() const; - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, size_t jump_to_time); + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, size_t jump_to_time); bool redo(Slic3r::Model &model, Slic3r::GUI::GLGizmosManager &gizmos, size_t jump_to_time); void release_least_recently_used(); @@ -788,7 +788,7 @@ template void StackImpl::load_mutable_object(const Sl } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. -void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos) +void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology) { // Release old snapshot data. assert(m_active_snapshot_time <= m_current_time); @@ -808,11 +808,11 @@ void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Mo this->save_mutable_object(m_selection); this->save_mutable_object(gizmos); // Save the snapshot info. - m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id, printer_technology); m_active_snapshot_time = m_current_time; // Save snapshot info of the last "current" aka "top most" state, that is only being serialized // if undoing an action. Such a snapshot has an invalid Model ID assigned if it was not taken yet. - m_snapshots.emplace_back(topmost_snapshot_name, m_active_snapshot_time, 0); + m_snapshots.emplace_back(topmost_snapshot_name, m_active_snapshot_time, 0, printer_technology); // Release empty objects from the history. this->collect_garbage(); assert(this->valid()); @@ -858,7 +858,7 @@ bool StackImpl::has_redo_snapshot() const return ++ it != m_snapshots.end(); } -bool StackImpl::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) +bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, size_t time_to_load) { assert(this->valid()); if (time_to_load == SIZE_MAX) { @@ -872,7 +872,7 @@ bool StackImpl::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selecti bool new_snapshot_taken = false; if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { // The current state is temporary. The current state needs to be captured to be redoable. - this->take_snapshot(topmost_snapshot_name, model, selection, gizmos); + this->take_snapshot(topmost_snapshot_name, model, selection, gizmos, printer_technology); // The line above entered another topmost_snapshot_name. assert(m_snapshots.back().is_topmost()); assert(! m_snapshots.back().is_topmost_captured()); @@ -1020,10 +1020,12 @@ Stack::~Stack() {} void Stack::set_memory_limit(size_t memsize) { pimpl->set_memory_limit(memsize); } size_t Stack::memsize() const { return pimpl->memsize(); } void Stack::release_least_recently_used() { pimpl->release_least_recently_used(); } -void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos) { pimpl->take_snapshot(snapshot_name, model, selection, gizmos); } +void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology) + { pimpl->take_snapshot(snapshot_name, model, selection, gizmos, printer_technology); } 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, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->undo(model, selection, gizmos, time_to_load); } +bool Stack::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, size_t time_to_load) + { return pimpl->undo(model, selection, gizmos, printer_technology, time_to_load); } bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->redo(model, gizmos, time_to_load); } 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 6785cd740..00ab55822 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -23,11 +23,13 @@ namespace UndoRedo { struct Snapshot { Snapshot(size_t timestamp) : timestamp(timestamp) {} - Snapshot(const std::string &name, size_t timestamp, size_t model_id) : name(name), timestamp(timestamp), model_id(model_id) {} + Snapshot(const std::string &name, size_t timestamp, size_t model_id, Slic3r::PrinterTechnology printer_technology) : + name(name), timestamp(timestamp), model_id(model_id), printer_technology(printer_technology) {} - std::string name; - size_t timestamp; - size_t model_id; + std::string name; + size_t timestamp; + size_t model_id; + PrinterTechnology printer_technology; bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } @@ -66,7 +68,7 @@ public: void release_least_recently_used(); // 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, const Slic3r::GUI::GLGizmosManager& gizmos); + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology); // To be queried to enable / disable the Undo / Redo buttons at the UI. bool has_undo_snapshot() const; @@ -74,7 +76,7 @@ public: // Roll back the time. If time_to_load is SIZE_MAX, the previous snapshot is activated. // Undoing an action may need to take a snapshot of the current application state, so that redo to the current state is possible. - bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX); + bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, size_t time_to_load = SIZE_MAX); // Jump forward in time. If time_to_load is SIZE_MAX, the next snapshot is activated. bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX); From 4049f33609858bc2f6884f3e8a1b4c3ec9c87f7d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 18 Jul 2019 18:19:40 +0200 Subject: [PATCH 06/17] Fix of osx builds --- src/libslic3r/Config.hpp | 2 +- src/slic3r/Utils/UndoRedo.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 844287efb..7d96f6cf3 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -93,7 +93,7 @@ enum ConfigOptionMode { comExpert }; -enum PrinterTechnology +enum PrinterTechnology : unsigned char { // Fused Filament Fabrication ptFFF, diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 00ab55822..96b539f18 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -12,6 +12,7 @@ namespace Slic3r { class Model; +enum PrinterTechnology : unsigned char; namespace GUI { class Selection; From fca5562c6c35e2b3c087f2457fc2c60984330e25 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 19 Jul 2019 10:02:52 +0200 Subject: [PATCH 07/17] Process start_filament_gcode in case of usual single extruder printer Reported in https://github.com/prusa3d/PrusaSlicer/issues/2652 --- src/libslic3r/GCode.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 37a14d42c..d5ee6a048 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2784,7 +2784,17 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // if we are running a single-extruder setup, just set the extruder and return nothing if (!m_writer.multiple_extruders) { m_placeholder_parser.set("current_extruder", extruder_id); - return m_writer.toolchange(extruder_id); + + std::string gcode; + // Append the filament start G-code. + const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); + if (! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the filament. + gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); + check_add_eol(gcode); + } + gcode += m_writer.toolchange(extruder_id); + return gcode; } // prepend retraction on the current extruder From d9c325c7f067cff2bf997e67b205860025e32807 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 10:29:06 +0200 Subject: [PATCH 08/17] Adding a generic bitmap "flags" attrib to the Undo / Redo snapshot. using this new "flags" attrib to store & recover the "Layers editing active" flag and restoring the "Layers editing" tool state. --- src/slic3r/GUI/Plater.cpp | 41 +++++++++++++++++++++++++---------- src/slic3r/Utils/UndoRedo.cpp | 22 +++++++++---------- src/slic3r/Utils/UndoRedo.hpp | 15 +++++++++---- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 559145eba..f1be5c9f2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1641,17 +1641,7 @@ struct Plater::priv void split_volume(); void scale_selection_to_fit_print_volume(); - void take_snapshot(const std::string& snapshot_name) - { - if (this->m_prevent_snapshots > 0) - return; - assert(this->m_prevent_snapshots >= 0); - this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology); - this->undo_redo_stack.release_least_recently_used(); - // Save the last active preset name of a particular printer technology. - ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); - BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack.memsize()) << log_memory_info(); - } + void take_snapshot(const std::string& snapshot_name); void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } int get_active_snapshot_index(); void undo(); @@ -3588,6 +3578,21 @@ int Plater::priv::get_active_snapshot_index() return it - ss_stack.begin(); } +void Plater::priv::take_snapshot(const std::string& snapshot_name) +{ + if (this->m_prevent_snapshots > 0) + return; + assert(this->m_prevent_snapshots >= 0); + unsigned int flags = 0; + if (this->view3D->is_layers_editing_enabled()) + flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; + this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, flags); + this->undo_redo_stack.release_least_recently_used(); + // Save the last active preset name of a particular printer technology. + ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); + BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack.memsize()) << log_memory_info(); +} + void Plater::priv::undo() { const std::vector &snapshots = this->undo_redo_stack.snapshots(); @@ -3627,9 +3632,18 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } // Save the last active preset name of a particular printer technology. ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); + // Flags made of Snapshot::Flags enum values. + unsigned int new_flags = it_snapshot->flags; + unsigned int top_snapshot_flags = 0; + if (this->view3D->is_layers_editing_enabled()) + top_snapshot_flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; + bool new_variable_layer_editing_active = (new_flags & UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE) != 0; + // Disable layer editing before the Undo / Redo jump. + if (! new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) + view3D->enable_layers_editing(false); // Do the jump in time. if (it_snapshot->timestamp < this->undo_redo_stack.active_snapshot_time() ? - this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, it_snapshot->timestamp) : + this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, top_snapshot_flags, it_snapshot->timestamp) : this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), it_snapshot->timestamp)) { if (printer_technology_changed) { // Switch to the other printer technology. Switch to the last printer active for that particular technology. @@ -3641,6 +3655,9 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator wxGetApp().load_current_presets(); } this->update_after_undo_redo(temp_snapshot_was_taken); + // Enable layer editing after the Undo / Redo jump. + if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) + view3D->enable_layers_editing(true); } } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index c4a082fb6..efbdb767a 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -495,12 +495,12 @@ 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, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology); + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology, unsigned int flags); void load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos); bool has_undo_snapshot() const; bool has_redo_snapshot() const; - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, size_t jump_to_time); + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, unsigned int flags, size_t jump_to_time); bool redo(Slic3r::Model &model, Slic3r::GUI::GLGizmosManager &gizmos, size_t jump_to_time); void release_least_recently_used(); @@ -788,7 +788,7 @@ template void StackImpl::load_mutable_object(const Sl } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. -void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology) +void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology, unsigned int flags) { // Release old snapshot data. assert(m_active_snapshot_time <= m_current_time); @@ -808,11 +808,11 @@ void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Mo this->save_mutable_object(m_selection); this->save_mutable_object(gizmos); // Save the snapshot info. - m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id, printer_technology); + m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id, printer_technology, flags); m_active_snapshot_time = m_current_time; // Save snapshot info of the last "current" aka "top most" state, that is only being serialized // if undoing an action. Such a snapshot has an invalid Model ID assigned if it was not taken yet. - m_snapshots.emplace_back(topmost_snapshot_name, m_active_snapshot_time, 0, printer_technology); + m_snapshots.emplace_back(topmost_snapshot_name, m_active_snapshot_time, 0, printer_technology, flags); // Release empty objects from the history. this->collect_garbage(); assert(this->valid()); @@ -858,7 +858,7 @@ bool StackImpl::has_redo_snapshot() const return ++ it != m_snapshots.end(); } -bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, size_t time_to_load) +bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, Slic3r::GUI::GLGizmosManager &gizmos, PrinterTechnology printer_technology, unsigned int flags, size_t time_to_load) { assert(this->valid()); if (time_to_load == SIZE_MAX) { @@ -872,7 +872,7 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti bool new_snapshot_taken = false; if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { // The current state is temporary. The current state needs to be captured to be redoable. - this->take_snapshot(topmost_snapshot_name, model, selection, gizmos, printer_technology); + this->take_snapshot(topmost_snapshot_name, model, selection, gizmos, printer_technology, flags); // The line above entered another topmost_snapshot_name. assert(m_snapshots.back().is_topmost()); assert(! m_snapshots.back().is_topmost_captured()); @@ -1020,12 +1020,12 @@ Stack::~Stack() {} void Stack::set_memory_limit(size_t memsize) { pimpl->set_memory_limit(memsize); } size_t Stack::memsize() const { return pimpl->memsize(); } void Stack::release_least_recently_used() { pimpl->release_least_recently_used(); } -void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology) - { pimpl->take_snapshot(snapshot_name, model, selection, gizmos, printer_technology); } +void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology, unsigned int flags) + { pimpl->take_snapshot(snapshot_name, model, selection, gizmos, printer_technology, flags); } 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, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, size_t time_to_load) - { return pimpl->undo(model, selection, gizmos, printer_technology, time_to_load); } +bool Stack::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, unsigned int flags, size_t time_to_load) + { return pimpl->undo(model, selection, gizmos, printer_technology, flags, time_to_load); } bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->redo(model, gizmos, time_to_load); } 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 96b539f18..28ba31a00 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -24,13 +24,20 @@ namespace UndoRedo { struct Snapshot { Snapshot(size_t timestamp) : timestamp(timestamp) {} - Snapshot(const std::string &name, size_t timestamp, size_t model_id, Slic3r::PrinterTechnology printer_technology) : - name(name), timestamp(timestamp), model_id(model_id), printer_technology(printer_technology) {} + Snapshot(const std::string &name, size_t timestamp, size_t model_id, Slic3r::PrinterTechnology printer_technology, unsigned int flags) : + name(name), timestamp(timestamp), model_id(model_id), printer_technology(printer_technology), flags(flags) {} + + // Bitmask of various binary flags to be stored with the snapshot. + enum Flags { + VARIABLE_LAYER_EDITING_ACTIVE = 1, + }; std::string name; size_t timestamp; size_t model_id; PrinterTechnology printer_technology; + // Bitmap of Flags (see the Flags enum). + unsigned int flags; bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } @@ -69,7 +76,7 @@ public: void release_least_recently_used(); // 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, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology); + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology, unsigned int flags); // To be queried to enable / disable the Undo / Redo buttons at the UI. bool has_undo_snapshot() const; @@ -77,7 +84,7 @@ public: // Roll back the time. If time_to_load is SIZE_MAX, the previous snapshot is activated. // Undoing an action may need to take a snapshot of the current application state, so that redo to the current state is possible. - bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, size_t time_to_load = SIZE_MAX); + bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, PrinterTechnology printer_technology, unsigned int flags, size_t time_to_load = SIZE_MAX); // Jump forward in time. If time_to_load is SIZE_MAX, the next snapshot is activated. bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX); From 2de6d95322430ba2835495f18dde302b3128001c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 11:12:38 +0200 Subject: [PATCH 09/17] Memory statistics (total memory, memory usage, Undo / Redo stack size) into the System INfo dialog. --- src/slic3r/GUI/Plater.cpp | 12 +++------- src/slic3r/GUI/Plater.hpp | 6 +++++ src/slic3r/GUI/SysInfoDialog.cpp | 38 +++++++++++++++++++++++++++++++- src/slic3r/Utils/UndoRedo.cpp | 2 ++ src/slic3r/Utils/UndoRedo.hpp | 1 + 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f1be5c9f2..74636bbdc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4564,15 +4564,9 @@ bool Plater::can_copy_to_clipboard() const return true; } -bool Plater::can_undo() const -{ - return p->undo_redo_stack.has_undo_snapshot(); -} - -bool Plater::can_redo() const -{ - return p->undo_redo_stack.has_redo_snapshot(); -} +bool Plater::can_undo() const { return p->undo_redo_stack.has_undo_snapshot(); } +bool Plater::can_redo() const { return p->undo_redo_stack.has_redo_snapshot(); } +const UndoRedo::Stack& Plater::undo_redo_stack() const { return p->undo_redo_stack; } SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() : m_was_running(wxGetApp().plater()->is_background_process_running()) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a4ced64a0..d7e91f516 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -27,6 +27,11 @@ class ModelObject; class Print; class SLAPrint; +namespace UndoRedo { + class Stack; + struct Snapshot; +}; + namespace GUI { class MainFrame; @@ -191,6 +196,7 @@ public: void undo_to(int selection); void redo_to(int selection); bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text); + const Slic3r::UndoRedo::Stack& undo_redo_stack() const; void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config); diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index bd19c38c3..96a3e9a81 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -2,6 +2,7 @@ #include "I18N.hpp" #include "3DScene.hpp" #include "GUI.hpp" +#include "../Utils/UndoRedo.hpp" #include @@ -10,6 +11,14 @@ #include "GUI_App.hpp" #include "wxExtensions.hpp" +#ifdef _WIN32 + // The standard Windows includes. + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include + #include +#endif /* _WIN32 */ + namespace Slic3r { namespace GUI { @@ -36,10 +45,37 @@ std::string get_main_info(bool format_as_html) "System Version: " #endif << b_end << wxPlatformInfo::Get().GetOperatingSystemDescription() << line_end; + out << b_start << "Total RAM size [MB]: " << b_end << Slic3r::format_memsize_MB(Slic3r::total_physical_memory()); return out.str(); } +std::string get_mem_info(bool format_as_html) +{ + std::stringstream out; + + std::string b_start = format_as_html ? "" : ""; + std::string b_end = format_as_html ? "" : ""; + std::string line_end = format_as_html ? "
" : "\n"; + + const Slic3r::UndoRedo::Stack &stack = wxGetApp().plater()->undo_redo_stack(); + out << b_start << "RAM size reserved for the Undo / Redo stack [MB]: " << b_end << Slic3r::format_memsize_MB(stack.get_memory_limit()) << line_end; + out << b_start << "RAM size occupied by the Undo / Redo stack [MB]: " << b_end << Slic3r::format_memsize_MB(stack.memsize()) << line_end << line_end; + +#ifdef _WIN32 + HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId()); + if (hProcess != nullptr) { + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) + out << b_start << "WorkingSet [MB]: " << b_end << format_memsize_MB(pmc.WorkingSetSize) << line_end + << b_start << "PrivateBytes [MB]: " << b_end << format_memsize_MB(pmc.PrivateUsage) << line_end + << b_start << "Pagefile(peak) [MB]: " << b_end << format_memsize_MB(pmc.PagefileUsage) << "(" << format_memsize_MB(pmc.PeakPagefileUsage) << ")" << line_end; + CloseHandle(hProcess); + } +#endif + return out.str(); +} + SysInfoDialog::SysInfoDialog() : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("System Information")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { @@ -111,7 +147,7 @@ SysInfoDialog::SysInfoDialog() "" "" "", bgr_clr_str, text_clr_str, text_clr_str, - _3DScene::get_gl_info(true, true)); + get_mem_info(true) + "
" + _3DScene::get_gl_info(true, true)); m_opengl_info_html->SetPage(text); main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15); } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index efbdb767a..2605bd2a7 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -486,6 +486,7 @@ public: StackImpl() : m_memory_limit(std::min(Slic3r::total_physical_memory() / 10, size_t(1 * 16384 * 65536 / UNDO_REDO_DEBUG_LOW_MEM_FACTOR))), m_active_snapshot_time(0), m_current_time(0) {} void set_memory_limit(size_t memsize) { m_memory_limit = memsize; } + size_t get_memory_limit() const { return m_memory_limit; } size_t memsize() const { size_t memsize = 0; @@ -1018,6 +1019,7 @@ void StackImpl::release_least_recently_used() Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} void Stack::set_memory_limit(size_t memsize) { pimpl->set_memory_limit(memsize); } +size_t Stack::get_memory_limit() const { return pimpl->get_memory_limit(); } size_t Stack::memsize() const { return pimpl->memsize(); } void Stack::release_least_recently_used() { pimpl->release_least_recently_used(); } void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos, Slic3r::PrinterTechnology printer_technology, unsigned int flags) diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 28ba31a00..916e44aa2 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -68,6 +68,7 @@ public: // Set maximum memory threshold. If the threshold is exceeded, least recently used snapshots are released. void set_memory_limit(size_t memsize); + size_t get_memory_limit() const; // Estimate size of the RAM consumed by the Undo / Redo stack. size_t memsize() const; From eb29c3e01d3ff188db74faa567ece26cc3425fae Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 19 Jul 2019 12:59:56 +0200 Subject: [PATCH 10/17] Wipe tower accounts for extruder offsets Also, in case of non-single-extruder printer with the wipe tower, first wiping line was printed where the border should have been - fixed --- src/libslic3r/GCode.cpp | 47 ++++++++++++++++++++++++------- src/libslic3r/GCode.hpp | 7 +++-- src/libslic3r/GCode/WipeTower.cpp | 4 +-- src/libslic3r/GCode/WipeTower.hpp | 20 +++++++------ 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d5ee6a048..81880831f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -169,6 +169,9 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2 std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const { + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + std::string gcode; // Toolchangeresult.gcode assumes the wipe tower corner is at the origin @@ -182,8 +185,11 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T end_pos = Eigen::Rotation2Df(alpha) * end_pos; end_pos += m_wipe_tower_pos; } - std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha); - + + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); // Disable linear advance for the wipe tower operations. gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); @@ -285,17 +291,21 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) -std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const +std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const { - std::istringstream gcode_str(gcode_original); + Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); + + std::istringstream gcode_str(tcr.gcode); std::string gcode_out; std::string line; - Vec2f pos = start_pos; - Vec2f transformed_pos; + Vec2f pos = tcr.start_pos; + Vec2f transformed_pos = pos; Vec2f old_pos(-1000.1f, -1000.1f); while (gcode_str) { std::getline(gcode_str, line); // we read the gcode line by line + + // All G1 commands should be translated and rotated if (line.find("G1 ") == 0) { std::ostringstream line_out; std::istringstream line_str(line); @@ -317,17 +327,34 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco if (transformed_pos != old_pos) { line = line_out.str(); - char buf[2048] = "G1"; + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) << "G1 "; if (transformed_pos.x() != old_pos.x()) - sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x()); + oss << " X" << transformed_pos.x() - extruder_offset.x(); if (transformed_pos.y() != old_pos.y()) - sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y()); + oss << " Y" << transformed_pos.y() - extruder_offset.y(); - line.replace(line.find("G1 "), 3, buf); + line.replace(line.find("G1 "), 3, oss.str()); old_pos = transformed_pos; } } + gcode_out += line + "\n"; + + // If this was a toolchange command, we should change current extruder offset + if (line == "[toolchange_gcode]") { + extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); + + // If the extruder offset changed, add an extra move so everything is continuous + if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) + << "G1 X" << transformed_pos.x() - extruder_offset.x() + << " Y" << transformed_pos.y() - extruder_offset.y() + << "\n"; + gcode_out += oss.str(); + } + } } return gcode_out; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 4b81b42aa..f2a67f600 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -90,6 +90,7 @@ public: m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), + m_extruder_offsets(print_config.extruder_offset.values), m_priming(priming), m_tool_changes(tool_changes), m_final_purge(final_purge), @@ -107,14 +108,16 @@ private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; - // Postprocesses gcode: rotates and moves all G1 extrusions and returns result - std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const; + // Postprocesses gcode: rotates and moves G1 extrusions and returns result + std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; // Left / right edges of the wipe tower, for the planning of wipe moves. const float m_left; const float m_right; const Vec2f m_wipe_tower_pos; const float m_wipe_tower_rotation; + const std::vector m_extruder_offsets; + // Reference to cached values at the Printer class. const std::vector &m_priming; const std::vector> &m_tool_changes; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 37e4040d1..354ec6d9e 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -553,7 +553,7 @@ std::vector WipeTower::prime( result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); result.start_pos = writer.start_pos_rotated(); - result.end_pos = writer.pos_rotated(); + result.end_pos = writer.pos(); results.push_back(std::move(result)); @@ -643,7 +643,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature); toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials. toolchange_Load(writer, cleaning_box); - writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road + writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area. ++ m_num_tool_changes; } else diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index c18a502b1..badb3e8b2 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -139,13 +139,15 @@ public: m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter - std::stringstream stream{m_semm ? ramming_parameters : std::string()}; - float speed = 0.f; - stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator; - m_filpar[idx].ramming_line_width_multiplicator /= 100; - m_filpar[idx].ramming_step_multiplicator /= 100; - while (stream >> speed) - m_filpar[idx].ramming_speed.push_back(speed); + if (m_semm) { + std::stringstream stream{ramming_parameters}; + float speed = 0.f; + stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator; + m_filpar[idx].ramming_line_width_multiplicator /= 100; + m_filpar[idx].ramming_step_multiplicator /= 100; + while (stream >> speed) + m_filpar[idx].ramming_speed.push_back(speed); + } m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later } @@ -241,8 +243,8 @@ public: int cooling_moves = 0; float cooling_initial_speed = 0.f; float cooling_final_speed = 0.f; - float ramming_line_width_multiplicator = 0.f; - float ramming_step_multiplicator = 0.f; + float ramming_line_width_multiplicator = 1.f; + float ramming_step_multiplicator = 1.f; float max_e_speed = std::numeric_limits::max(); std::vector ramming_speed; float nozzle_diameter; From dbc1918193ebdf0badec67c7ca15bfeb553f70a4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 15:29:04 +0200 Subject: [PATCH 11/17] Undo / Redo. Workaround for the Wipe tower. --- src/libslic3r/Model.cpp | 2 +- src/libslic3r/Model.hpp | 65 ++++++++++++++++++++++++++++++++--- src/slic3r/GUI/Plater.cpp | 30 ++++++++++++++++ src/slic3r/Utils/UndoRedo.cpp | 39 ++++++++++----------- 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 66532e9ea..21e155793 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1971,4 +1971,4 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model) -#endif \ No newline at end of file +#endif diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2b4a6d978..f03d53153 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -22,6 +22,8 @@ namespace cereal { class BinaryOutputArchive; template void load_optional(BinaryInputArchive &ar, std::shared_ptr &ptr); template void save_optional(BinaryOutputArchive &ar, const std::shared_ptr &ptr); + template void load_by_value(BinaryInputArchive &ar, T &obj); + template void save_by_value(BinaryOutputArchive &ar, const T &obj); } namespace Slic3r { @@ -31,6 +33,7 @@ class ModelInstance; class ModelMaterial; class ModelObject; class ModelVolume; +class ModelWipeTower; class Print; class SLAPrint; @@ -66,6 +69,21 @@ private: } }; +namespace Internal { + template + class StaticSerializationWrapper + { + public: + StaticSerializationWrapper(T &wrap) : wrapped(wrap) {} + private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + template void load(Archive &ar) { cereal::load_by_value(ar, wrapped); } + template void save(Archive &ar) const { cereal::save_by_value(ar, wrapped); } + T& wrapped; + }; +} + typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; typedef std::map t_model_material_attributes; @@ -140,7 +158,8 @@ private: ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } template void serialize(Archive &ar) { assert(this->id().invalid()); assert(this->config.id().invalid()); - ar(attributes, config); + Internal::StaticSerializationWrapper config_wrapper(config); + ar(attributes, config_wrapper); // assert(this->id().valid()); assert(this->config.id().valid()); } @@ -349,7 +368,8 @@ private: } template void serialize(Archive &ar) { ar(cereal::base_class(this)); - ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, + Internal::StaticSerializationWrapper config_wrapper(config); + ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; @@ -535,7 +555,8 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { cereal::load_optional(ar, m_convex_hull); @@ -547,7 +568,8 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); } @@ -650,6 +672,35 @@ private: } }; +class ModelWipeTower final : public ObjectBase +{ +public: + Vec2d position; + double rotation; + +private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + friend class Model; + + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + explicit ModelWipeTower() {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + explicit ModelWipeTower(int) : ObjectBase(-1) {} + // Copy constructor copies the ID. + explicit ModelWipeTower(const ModelWipeTower &cfg) = default; + + // Disabled methods. + ModelWipeTower(ModelWipeTower &&rhs) = delete; + ModelWipeTower& operator=(const ModelWipeTower &rhs) = delete; + ModelWipeTower& operator=(ModelWipeTower &&rhs) = delete; + + // For serialization / deserialization of ModelWipeTower composed into another class into the Undo / Redo stack as a separate object. + template void serialize(Archive &ar) { ar(position, rotation); } +}; + // The print bed content. // Description of a triangular model with multiple materials, multiple instances with various affine transformations // and with multiple modifier meshes. @@ -665,6 +716,8 @@ public: ModelMaterialMap materials; // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). ModelObjectPtrs objects; + // Wipe tower object. + ModelWipeTower wipe_tower; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -742,7 +795,8 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { - ar(materials, objects); + Internal::StaticSerializationWrapper wipe_tower_wrapper(wipe_tower); + ar(materials, objects, wipe_tower_wrapper); } }; @@ -778,6 +832,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2); namespace cereal { template struct specialize {}; + template struct specialize {}; } #endif /* slic3r_Model_hpp_ */ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 74636bbdc..47109e2cd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3586,6 +3586,13 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) unsigned int flags = 0; if (this->view3D->is_layers_editing_enabled()) flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; + //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, flags); this->undo_redo_stack.release_least_recently_used(); // Save the last active preset name of a particular printer technology. @@ -3632,6 +3639,13 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } // Save the last active preset name of a particular printer technology. ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); + //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } // Flags made of Snapshot::Flags enum values. unsigned int new_flags = it_snapshot->flags; unsigned int top_snapshot_flags = 0; @@ -3654,6 +3668,22 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator // This also switches the printer technology based on the printer technology of the active printer profile. wxGetApp().load_current_presets(); } + //FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig ¤t_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + Vec2d current_position(current_config.opt_float("wipe_tower_x"), current_config.opt_float("wipe_tower_y")); + double current_rotation = current_config.opt_float("wipe_tower_rotation_angle"); + if (current_position != model.wipe_tower.position || current_rotation != model.wipe_tower.rotation) { + DynamicPrintConfig new_config; + new_config.set_key_value("wipe_tower_x", new ConfigOptionFloat(model.wipe_tower.position.x())); + new_config.set_key_value("wipe_tower_y", new ConfigOptionFloat(model.wipe_tower.position.y())); + new_config.set_key_value("wipe_tower_rotation_angle", new ConfigOptionFloat(model.wipe_tower.rotation)); + Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + tab_print->load_config(new_config); + tab_print->update_dirty(); + } + } this->update_after_undo_redo(temp_snapshot_was_taken); // Enable layer editing after the Undo / Redo jump. if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 2605bd2a7..a8f9cc134 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -514,11 +514,11 @@ public: const Selection& selection_deserialized() const { return m_selection; } //protected: - template ObjectID save_mutable_object(const T &object); + template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object, bool optional); template T* load_mutable_object(const Slic3r::ObjectID id); template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id, bool optional); - template void load_mutable_object(const Slic3r::ObjectID id, T &target); + template void load_mutable_object(const Slic3r::ObjectID id, T &target); #ifdef SLIC3R_UNDOREDO_DEBUG std::string format() const { @@ -601,7 +601,6 @@ class ModelObject; class ModelVolume; class ModelInstance; class ModelMaterial; -class ModelConfig; class DynamicPrintConfig; class TriangleMesh; @@ -616,14 +615,13 @@ namespace cereal template struct specialize {}; template struct specialize {}; template struct specialize {}; - template struct specialize {}; template struct specialize, cereal::specialization::non_member_load_save> {}; // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive& ar, T* const& ptr) { - ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); + ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object @@ -655,19 +653,18 @@ namespace cereal // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. - void save(BinaryOutputArchive& ar, const Slic3r::ModelConfig &cfg) + template void save_by_value(BinaryOutputArchive& ar, const T &cfg) { - ar(cereal::get_user_data(ar).save_mutable_object(cfg)); + ar(cereal::get_user_data(ar).save_mutable_object(cfg)); } - // Load ObjectBase derived class from the Undo / Redo stack as a separate object // based on the ObjectID loaded from this stream. - void load(BinaryInputArchive& ar, Slic3r::ModelConfig &cfg) + template void load_by_value(BinaryInputArchive& ar, T &cfg) { Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); size_t id; ar(id); - stack.load_mutable_object(Slic3r::ObjectID(id), cfg); + stack.load_mutable_object(Slic3r::ObjectID(id), cfg); } // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, @@ -723,7 +720,7 @@ template std::shared_ptr& ImmutableObjectHistory::share return m_shared_object; } -template ObjectID StackImpl::save_mutable_object(const T &object) +template ObjectID StackImpl::save_mutable_object(const T &object) { // First find or allocate a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(object.id()); @@ -734,7 +731,7 @@ template ObjectID StackImpl::save_mutable_object(cons std::ostringstream oss; { Slic3r::UndoRedo::OutputArchive archive(*this, oss); - archive(static_cast(object)); + archive(object); } object_history->save(m_active_snapshot_time, m_current_time, oss.str()); return object.id(); @@ -758,7 +755,7 @@ template ObjectID StackImpl::save_immutable_object(std::shared_ptr T* StackImpl::load_mutable_object(const Slic3r::ObjectID id) { T *target = new T(); - this->load_mutable_object(id, *target); + this->load_mutable_object(id, *target); return target; } @@ -775,7 +772,7 @@ template std::shared_ptr StackImpl::load_immutable_object(c return object_history->shared_ptr(*this); } -template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) +template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) { // First find a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(id); @@ -785,7 +782,7 @@ template void StackImpl::load_mutable_object(const Sl std::istringstream iss(object_history->load(m_active_snapshot_time)); Slic3r::UndoRedo::InputArchive archive(*this, iss); target.m_id = id; - archive(static_cast(target)); + archive(target); } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. @@ -800,14 +797,14 @@ void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Mo m_snapshots.erase(it, m_snapshots.end()); } // Take new snapshots. - this->save_mutable_object(model); + this->save_mutable_object(model); 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()) m_selection.volumes_and_instances.emplace_back(selection.get_volume(volume_idx)->geometry_id); - this->save_mutable_object(m_selection); - this->save_mutable_object(gizmos); + this->save_mutable_object(m_selection); + this->save_mutable_object(gizmos); // Save the snapshot info. m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id, printer_technology, flags); m_active_snapshot_time = m_current_time; @@ -833,12 +830,12 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU m_active_snapshot_time = timestamp; model.clear_objects(); model.clear_materials(); - this->load_mutable_object(ObjectID(it_snapshot->model_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); + this->load_mutable_object(m_selection.id(), m_selection); gizmos.reset_all_states(); - this->load_mutable_object(gizmos.id(), gizmos); + this->load_mutable_object(gizmos.id(), gizmos); // 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; From 81d3669a2575231041ade3387cafd9928cf458e8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 19 Jul 2019 15:36:55 +0200 Subject: [PATCH 12/17] Undo/Redo buttons moved into their own toolbar --- src/slic3r/GUI/GLCanvas3D.cpp | 231 ++++++++++++++++++++------------- src/slic3r/GUI/GLCanvas3D.hpp | 17 ++- src/slic3r/GUI/GLToolbar.cpp | 9 +- src/slic3r/GUI/GUI_Preview.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 8 +- 5 files changed, 163 insertions(+), 105 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 47b9ad48a..85cbcadae 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1202,7 +1202,8 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_bed(bed) , m_camera(camera) , m_view_toolbar(view_toolbar) - , m_toolbar(GLToolbar::Normal, "Top") + , m_main_toolbar(GLToolbar::Normal, "Top") + , m_undoredo_toolbar(GLToolbar::Normal, "Top") , m_gizmos(*this) , m_use_clipping_planes(false) , m_sidebar_field("") @@ -1309,7 +1310,7 @@ bool GLCanvas3D::init() return false; } - if (m_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) + if (m_main_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) { std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl; return false; @@ -1323,7 +1324,7 @@ bool GLCanvas3D::init() if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; - if (!_init_toolbar()) + if (!_init_toolbars()) return false; if (m_selection.is_enabled() && !m_selection.init()) @@ -1514,9 +1515,14 @@ void GLCanvas3D::enable_selection(bool enable) m_selection.set_enabled(enable); } -void GLCanvas3D::enable_toolbar(bool enable) +void GLCanvas3D::enable_main_toolbar(bool enable) { - m_toolbar.set_enabled(enable); + m_main_toolbar.set_enabled(enable); +} + +void GLCanvas3D::enable_undoredo_toolbar(bool enable) +{ + m_undoredo_toolbar.set_enabled(enable); } void GLCanvas3D::enable_dynamic_background(bool enable) @@ -2295,7 +2301,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; - m_dirty |= m_toolbar.update_items_state(); + m_dirty |= m_main_toolbar.update_items_state(); + m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); if (!m_dirty) @@ -2674,7 +2681,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ } - if (m_toolbar.on_mouse(evt, *this)) + if (m_main_toolbar.on_mouse(evt, *this)) + { + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + mouse_up_cleanup(); + m_mouse.set_start_position_3D_as_invalid(); + return; + } + + if (m_undoredo_toolbar.on_mouse(evt, *this)) { if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); @@ -3015,7 +3030,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) tooltip = m_gizmos.get_tooltip(); if (tooltip.empty()) - tooltip = m_toolbar.get_tooltip(); + tooltip = m_main_toolbar.get_tooltip(); + + if (tooltip.empty()) + tooltip = m_undoredo_toolbar.get_tooltip(); if (tooltip.empty()) tooltip = m_view_toolbar.get_tooltip(); @@ -3480,7 +3498,7 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) ImGuiWrapper* imgui = wxGetApp().imgui(); const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); - imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); + imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); imgui->set_next_window_bg_alpha(0.5f); imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -3502,9 +3520,20 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) imgui->end(); } -bool GLCanvas3D::_init_toolbar() +bool GLCanvas3D::_init_toolbars() { - if (!m_toolbar.is_enabled()) + if (!_init_main_toolbar()) + return false; + + if (!_init_undoredo_toolbar()) + return false; + + return true; +} + +bool GLCanvas3D::_init_main_toolbar() +{ + if (!m_main_toolbar.is_enabled()) return true; BackgroundTexture::Metadata background_data; @@ -3514,19 +3543,19 @@ bool GLCanvas3D::_init_toolbar() background_data.right = 16; background_data.bottom = 16; - if (!m_toolbar.init(background_data)) + if (!m_main_toolbar.init(background_data)) { // unable to init the toolbar texture, disable it - m_toolbar.set_enabled(false); + m_main_toolbar.set_enabled(false); return true; } -// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); - m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); - m_toolbar.set_layout_orientation(GLToolbar::Layout::Top); - m_toolbar.set_border(5.0f); - m_toolbar.set_separator_size(5); - m_toolbar.set_gap_size(2); +// m_main_toolbar.set_layout_type(GLToolbar::Layout::Vertical); + m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + m_main_toolbar.set_layout_orientation(GLToolbar::Layout::Top); + m_main_toolbar.set_border(5.0f); + m_main_toolbar.set_separator_size(5); + m_main_toolbar.set_gap_size(2); GLToolbarItem::Data item; @@ -3535,7 +3564,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "delete"; @@ -3544,7 +3573,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 1; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "deleteall"; @@ -3553,7 +3582,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 2; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "arrange"; @@ -3562,10 +3591,10 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 3; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "copy"; @@ -3574,7 +3603,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 4; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "paste"; @@ -3583,10 +3612,10 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 5; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "more"; @@ -3597,7 +3626,7 @@ bool GLCanvas3D::_init_toolbar() item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "fewer"; @@ -3607,10 +3636,10 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "splitobjects"; @@ -3620,7 +3649,7 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "splitvolumes"; @@ -3630,10 +3659,10 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "layersediting"; @@ -3644,16 +3673,44 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) - return false; + return true; +} + +bool GLCanvas3D::_init_undoredo_toolbar() +{ + if (!m_undoredo_toolbar.is_enabled()) + return true; + + BackgroundTexture::Metadata background_data; + background_data.filename = "toolbar_background.png"; + background_data.left = 16; + background_data.top = 16; + background_data.right = 16; + background_data.bottom = 16; + + if (!m_undoredo_toolbar.init(background_data)) + { + // unable to init the toolbar texture, disable it + m_undoredo_toolbar.set_enabled(false); + return true; + } + +// m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Vertical); + m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + m_undoredo_toolbar.set_layout_orientation(GLToolbar::Layout::Top); + m_undoredo_toolbar.set_border(5.0f); + m_undoredo_toolbar.set_separator_size(5); + m_undoredo_toolbar.set_gap_size(2); + + GLToolbarItem::Data item; item.name = "undo"; item.icon_filename = "undo_toolbar.svg"; item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; - item.sprite_id = 11; + item.sprite_id = 0; item.left.toggable = false; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; item.right.toggable = true; @@ -3661,18 +3718,18 @@ bool GLCanvas3D::_init_toolbar() item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; item.visibility_callback = []()->bool { return true; }; item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); }; - if (!m_toolbar.add_item(item)) + if (!m_undoredo_toolbar.add_item(item)) return false; item.name = "redo"; item.icon_filename = "redo_toolbar.svg"; item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; - item.sprite_id = 12; + item.sprite_id = 1; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; - if (!m_toolbar.add_item(item)) + if (!m_undoredo_toolbar.add_item(item)) return false; return true; @@ -4003,7 +4060,8 @@ void GLCanvas3D::_render_overlays() const _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); - _render_toolbar(); + _render_main_toolbar(); + _render_undoredo_toolbar(); _render_view_toolbar(); if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f)) @@ -4093,64 +4151,57 @@ void GLCanvas3D::_render_gizmos_overlay() const m_gizmos.render_overlay(); } -void GLCanvas3D::_render_toolbar() const +void GLCanvas3D::_render_main_toolbar() const { + if (!m_main_toolbar.is_enabled()) + return; + #if ENABLE_RETINA_GL -// m_toolbar.set_scale(m_retina_helper->get_scale_factor()); +// m_main_toolbar.set_scale(m_retina_helper->get_scale_factor()); const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); - m_toolbar.set_scale(scale); //! #ys_FIXME_experiment + m_main_toolbar.set_scale(scale); //! #ys_FIXME_experiment #else -// m_toolbar.set_scale(m_canvas->GetContentScaleFactor()); -// m_toolbar.set_scale(wxGetApp().em_unit()*0.1f); +// m_main_toolbar.set_scale(m_canvas->GetContentScaleFactor()); +// m_main_toolbar.set_scale(wxGetApp().em_unit()*0.1f); const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true)); - m_toolbar.set_icons_size(size); //! #ys_FIXME_experiment + m_main_toolbar.set_icons_size(size); //! #ys_FIXME_experiment #endif // ENABLE_RETINA_GL Size cnv_size = get_canvas_size(); float zoom = (float)m_camera.get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); + float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width()) * inv_zoom; - float top = 0.0f; - float left = 0.0f; - switch (m_toolbar.get_layout_type()) - { - default: - case GLToolbar::Layout::Horizontal: - { - // centers the toolbar on the top edge of the 3d scene - if (orientation == GLToolbar::Layout::Top) - { - top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - left = -0.5f * m_toolbar.get_width() * inv_zoom; - } - else - { - top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; - left = -0.5f * m_toolbar.get_width() * inv_zoom; - } - break; - } - case GLToolbar::Layout::Vertical: - { - // centers the toolbar on the right edge of the 3d scene - if (orientation == GLToolbar::Layout::Left) - { - top = 0.5f * m_toolbar.get_height() * inv_zoom; - left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; - } - else - { - top = 0.5f * m_toolbar.get_height() * inv_zoom; - left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; - } - break; - } - } - m_toolbar.set_position(top, left); + m_main_toolbar.set_position(top, left); + m_main_toolbar.render(*this); +} - m_toolbar.render(*this); +void GLCanvas3D::_render_undoredo_toolbar() const +{ + if (!m_undoredo_toolbar.is_enabled()) + return; + +#if ENABLE_RETINA_GL +// m_undoredo_toolbar.set_scale(m_retina_helper->get_scale_factor()); + const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); + m_undoredo_toolbar.set_scale(scale); //! #ys_FIXME_experiment +#else +// m_undoredo_toolbar.set_scale(m_canvas->GetContentScaleFactor()); +// m_undoredo_toolbar.set_scale(wxGetApp().em_unit()*0.1f); + const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true)); + m_undoredo_toolbar.set_icons_size(size); //! #ys_FIXME_experiment +#endif // ENABLE_RETINA_GL + + Size cnv_size = get_canvas_size(); + float zoom = (float)m_camera.get_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width())) * inv_zoom; + m_undoredo_toolbar.set_position(top, left); + m_undoredo_toolbar.render(*this); } void GLCanvas3D::_render_view_toolbar() const @@ -5672,14 +5723,14 @@ void GLCanvas3D::_update_selection_from_hover() bool GLCanvas3D::_deactivate_undo_redo_toolbar_items() { - if (m_toolbar.is_item_pressed("undo")) + if (m_undoredo_toolbar.is_item_pressed("undo")) { - m_toolbar.force_right_action(m_toolbar.get_item_id("undo"), *this); + m_undoredo_toolbar.force_right_action(m_undoredo_toolbar.get_item_id("undo"), *this); return true; } - else if (m_toolbar.is_item_pressed("redo")) + else if (m_undoredo_toolbar.is_item_pressed("redo")) { - m_toolbar.force_right_action(m_toolbar.get_item_id("redo"), *this); + m_undoredo_toolbar.force_right_action(m_undoredo_toolbar.get_item_id("redo"), *this); return true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6bb17da4a..022262c90 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -438,7 +438,8 @@ private: Shader m_shader; Mouse m_mouse; mutable GLGizmosManager m_gizmos; - mutable GLToolbar m_toolbar; + mutable GLToolbar m_main_toolbar; + mutable GLToolbar m_undoredo_toolbar; ClippingPlane m_clipping_planes[2]; mutable ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; @@ -551,7 +552,8 @@ public: void enable_moving(bool enable); void enable_gizmos(bool enable); void enable_selection(bool enable); - void enable_toolbar(bool enable); + void enable_main_toolbar(bool enable); + void enable_undoredo_toolbar(bool enable); void enable_dynamic_background(bool enable); void allow_multisample(bool allow); @@ -644,10 +646,16 @@ public: void start_keeping_dirty() { m_keep_dirty = true; } void stop_keeping_dirty() { m_keep_dirty = false; } + unsigned int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } + void force_main_toolbar_left_action(unsigned int item_id) { m_main_toolbar.force_left_action(item_id, *this); } + void force_main_toolbar_right_action(unsigned int item_id) { m_main_toolbar.force_right_action(item_id, *this); } + private: bool _is_shown_on_screen() const; - bool _init_toolbar(); + bool _init_toolbars(); + bool _init_main_toolbar(); + bool _init_undoredo_toolbar(); bool _set_current(); void _resize(unsigned int w, unsigned int h); @@ -674,7 +682,8 @@ private: void _render_volumes_for_picking() const; void _render_current_gizmo() const; void _render_gizmos_overlay() const; - void _render_toolbar() const; + void _render_main_toolbar() const; + void _render_undoredo_toolbar() const; void _render_view_toolbar() const; #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target() const; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 0002eda2d..cdd80ed17 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -403,6 +403,9 @@ void GLToolbar::render(const GLCanvas3D& parent) const bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { + if (!m_enabled) + return false; + Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); bool processed = false; @@ -1009,9 +1012,6 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const float bg_right = right; float bg_top = top; float bg_bottom = bottom; - float bg_width = right - left; - float bg_height = top - bottom; - float bg_min_size = std::min(bg_width, bg_height); float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; @@ -1139,9 +1139,6 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const float bg_right = right; float bg_top = top; float bg_bottom = bottom; - float bg_width = right - left; - float bg_height = top - bottom; - float bg_min_size = std::min(bg_width, bg_height); float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0ba447c1b..36354ab24 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -63,7 +63,8 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_ m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_selection(true); - m_canvas->enable_toolbar(true); + m_canvas->enable_main_toolbar(true); + m_canvas->enable_undoredo_toolbar(true); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 74636bbdc..76b16f055 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3639,8 +3639,8 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator top_snapshot_flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; bool new_variable_layer_editing_active = (new_flags & UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE) != 0; // Disable layer editing before the Undo / Redo jump. - if (! new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) - view3D->enable_layers_editing(false); + if (!new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); // Do the jump in time. if (it_snapshot->timestamp < this->undo_redo_stack.active_snapshot_time() ? this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, top_snapshot_flags, it_snapshot->timestamp) : @@ -3657,8 +3657,8 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator this->update_after_undo_redo(temp_snapshot_was_taken); // Enable layer editing after the Undo / Redo jump. if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) - view3D->enable_layers_editing(true); - } + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + } } void Plater::priv::update_after_undo_redo(bool /* temp_snapshot_was_taken */) From 3285bf7945d48e91f8f266866e21fc8847ec9ac0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 19 Jul 2019 15:47:10 +0200 Subject: [PATCH 13/17] Fixed conflicts after pulling from master --- src/slic3r/GUI/Plater.cpp | 50 +++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5b8d6eca3..bc7b09975 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3589,10 +3589,10 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. if (this->printer_technology == ptFFF) { - const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); - model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); - } + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, flags); this->undo_redo_stack.release_least_recently_used(); // Save the last active preset name of a particular printer technology. @@ -3642,10 +3642,10 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. if (this->printer_technology == ptFFF) { - const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); - model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); - } + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } // Flags made of Snapshot::Flags enum values. unsigned int new_flags = it_snapshot->flags; unsigned int top_snapshot_flags = 0; @@ -3668,23 +3668,23 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator // This also switches the printer technology based on the printer technology of the active printer profile. wxGetApp().load_current_presets(); } - //FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower. - // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. - if (this->printer_technology == ptFFF) { - const DynamicPrintConfig ¤t_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - Vec2d current_position(current_config.opt_float("wipe_tower_x"), current_config.opt_float("wipe_tower_y")); - double current_rotation = current_config.opt_float("wipe_tower_rotation_angle"); - if (current_position != model.wipe_tower.position || current_rotation != model.wipe_tower.rotation) { - DynamicPrintConfig new_config; - new_config.set_key_value("wipe_tower_x", new ConfigOptionFloat(model.wipe_tower.position.x())); - new_config.set_key_value("wipe_tower_y", new ConfigOptionFloat(model.wipe_tower.position.y())); - new_config.set_key_value("wipe_tower_rotation_angle", new ConfigOptionFloat(model.wipe_tower.rotation)); - Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); - tab_print->load_config(new_config); - tab_print->update_dirty(); - } - } - this->update_after_undo_redo(temp_snapshot_was_taken); + //FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig ¤t_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + Vec2d current_position(current_config.opt_float("wipe_tower_x"), current_config.opt_float("wipe_tower_y")); + double current_rotation = current_config.opt_float("wipe_tower_rotation_angle"); + if (current_position != model.wipe_tower.position || current_rotation != model.wipe_tower.rotation) { + DynamicPrintConfig new_config; + new_config.set_key_value("wipe_tower_x", new ConfigOptionFloat(model.wipe_tower.position.x())); + new_config.set_key_value("wipe_tower_y", new ConfigOptionFloat(model.wipe_tower.position.y())); + new_config.set_key_value("wipe_tower_rotation_angle", new ConfigOptionFloat(model.wipe_tower.rotation)); + Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + tab_print->load_config(new_config); + tab_print->update_dirty(); + } + } + this->update_after_undo_redo(temp_snapshot_was_taken); // Enable layer editing after the Undo / Redo jump. if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); From 152c2fe0c0d9cb662ca7d51edc2e48376eeca215 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 15:59:23 +0200 Subject: [PATCH 14/17] Undo / Redo pull down menu scaling fix on OSX Retina --- src/slic3r/GUI/GLCanvas3D.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 47b9ad48a..247334fb9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3488,8 +3488,11 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) int hovered = m_imgui_undo_redo_hovered_pos; int selected = -1; const float em = static_cast(wxGetApp().em_unit()); +#if ENABLE_RETINA_GL + em *= m_retina_helper->get_scale_factor(); +#endif - if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) + if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected)) m_imgui_undo_redo_hovered_pos = hovered; else m_imgui_undo_redo_hovered_pos = -1; From 07a3072622147cf70e3fed9075e5eb713f41055f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 17:14:37 +0200 Subject: [PATCH 15/17] Simplified loading of the SLA support structures into the scene. Fixed referesh of SLA support structures after Undo / Redo and when moving an object outside / inside the build volume. --- src/slic3r/GUI/GLCanvas3D.cpp | 65 +++++++---------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 2 -- 2 files changed, 12 insertions(+), 55 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 69d33649d..265f6eae7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1858,6 +1858,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re PrinterTechnology printer_technology = m_process->current_printer_technology(); int volume_idx_wipe_tower_old = -1; + if (printer_technology == ptSLA) + // Always do the full refresh in SLA mode to show / hide SLA support structures when an object is moved outside / inside the build volume. + m_regenerate_volumes = true; + if (m_regenerate_volumes) { // Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed). @@ -1890,7 +1894,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re state.step[istep].state = PrintStateBase::INVALID; else for (const ModelInstance *model_instance : print_object->model_object()->instances) - aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); + // Only the instances, which are currently printable, will have the SLA support structures kept. + // The instances outside the print bed will have the GLVolumes of their support structures released. + if (model_instance->is_printable()) + aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); } } sla_support_state.emplace_back(state); @@ -2042,7 +2049,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) - volume->set_sla_shift_z(shift_zs[volume->object_idx()]); + if (volume->object_idx() < m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) + volume->set_sla_shift_z(shift_zs[volume->object_idx()]); } if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) @@ -2198,7 +2206,8 @@ void GLCanvas3D::load_sla_preview() if ((m_canvas != nullptr) && (print != nullptr)) { _set_current(); - _load_sla_shells(); + // Reload the SLA support structures into GLVolumes. + this->reload_scene(true, true); _update_sla_shells_outside_state(); _show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside); } @@ -5461,56 +5470,6 @@ void GLCanvas3D::_load_fff_shells() } } -void GLCanvas3D::_load_sla_shells() -{ - //FIXME use reload_scene -#if 1 - const SLAPrint* print = this->sla_print(); - if (print->objects().empty()) - // nothing to render, return - return; - - auto add_volume = [this](const SLAPrintObject &object, int volume_id, const SLAPrintObject::Instance& instance, - const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) { - m_volumes.volumes.emplace_back(new GLVolume(color)); - GLVolume& v = *m_volumes.volumes.back(); - v.indexed_vertex_array.load_mesh(mesh); - v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; - v.composite_id.volume_id = volume_id; - v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); - v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); - v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); - v.set_convex_hull(mesh.convex_hull_3d()); - }; - - // adds objects' volumes - for (const SLAPrintObject* obj : print->objects()) - if (obj->is_step_done(slaposSliceSupports)) { - unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); - for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->transformed_mesh(), GLVolume::MODEL_COLOR[0], true); - // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when - // through the update_volumes_colors_by_extruder() call. - m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree)) - add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true); - if (obj->is_step_done(slaposBasePool) && obj->has_mesh(slaposBasePool)) - add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false); - } - double shift_z = obj->get_current_elevation(); - for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { - GLVolume& v = *m_volumes.volumes[i]; - // apply shift z - v.set_sla_shift_z(shift_z); - } - } - - update_volumes_colors_by_extruder(); -#else - this->reload_scene(true, true); -#endif -} - void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) { unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 022262c90..487416a14 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -730,8 +730,6 @@ private: void _load_gcode_unretractions(const GCodePreviewData& preview_data); // generates objects and wipe tower geometry void _load_fff_shells(); - // generates objects geometry for sla - void _load_sla_shells(); // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); void _update_toolpath_volumes_outside_state(); From 47df9506bb1086b04510a85c0cf3ad70ac48aaeb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 19 Jul 2019 17:16:20 +0200 Subject: [PATCH 16/17] Fix of OSX Imgui Undo / Redo pull down list scaling issue. --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 265f6eae7..4255f4ecf 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3514,7 +3514,7 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) int hovered = m_imgui_undo_redo_hovered_pos; int selected = -1; - const float em = static_cast(wxGetApp().em_unit()); + float em = static_cast(wxGetApp().em_unit()); #if ENABLE_RETINA_GL em *= m_retina_helper->get_scale_factor(); #endif From 99e2fe20a50b5003b87b39cb3f4a79b749bf7506 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 19 Jul 2019 17:56:16 +0200 Subject: [PATCH 17/17] Fix a memory access bug in ObjectManipulation --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 308b3f208..0a2877d92 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -199,18 +199,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off.png"); m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); - for (const std::string axis : { "x", "y", "z" }) { - const std::string label = boost::algorithm::to_upper_copy(axis); - def.set_default_value(new ConfigOptionString{ " " + label }); - Option option = Option(def, axis + "_axis_legend"); - - unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2 + static const char axes[] = { 'X', 'Y', 'Z' }; + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) { + const char label = axes[axis_idx]; + def.set_default_value(new ConfigOptionString{ std::string(" ") + label }); + Option option(def, std::string() + label + "_axis_legend"); // We will add a button to toggle mirroring to each axis: - auto mirror_button = [this, mirror_btn_width, axis_idx, &label](wxWindow* parent) { + auto mirror_button = [this, mirror_btn_width, axis_idx, label](wxWindow* parent) { wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); - btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label)); + btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label)); m_mirror_buttons[axis_idx].first = btn; m_mirror_buttons[axis_idx].second = mbShown; @@ -245,7 +244,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : canvas->do_mirror(L("Set Mirror")); UpdateAndShow(true); }); - return sizer; + + return sizer; }; option.side_widget = mirror_button;