From 144e37c2745b188c99e8c88fde7c6f4da3603ef3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 10:00:17 +0200 Subject: [PATCH 01/79] 1st installment of project dirty state manager --- src/libslic3r/Technologies.hpp | 5 +++ src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 5 +++ src/slic3r/GUI/Plater.cpp | 17 ++++++-- src/slic3r/GUI/Plater.hpp | 6 ++- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 47 +++++++++++++++++++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 35 +++++++++++++++ 7 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 src/slic3r/GUI/ProjectDirtyStateManager.cpp create mode 100644 src/slic3r/GUI/ProjectDirtyStateManager.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d6b2cff8e..fcb59f1a1 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,5 +58,10 @@ // Enable exporting lines M73 for remaining time to next printer stop to gcode #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) +// Enable project dirty state manager +#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) +// Enable project dirty state manager debug window +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4b3a1c6ca..5b904c87d 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -189,6 +189,8 @@ set(SLIC3R_GUI_SOURCES GUI/UnsavedChangesDialog.hpp GUI/ExtraRenderers.cpp GUI/ExtraRenderers.hpp + GUI/ProjectDirtyStateManager.hpp + GUI/ProjectDirtyStateManager.cpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7bbdc72b1..4570670cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1718,6 +1718,11 @@ void GLCanvas3D::render() } #endif // ENABLE_RENDER_STATISTICS +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + if (wxGetApp().is_editor() && wxGetApp().plater()->is_view3D_shown()) + wxGetApp().plater()->render_project_state_debug_window(); +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + #if ENABLE_CAMERA_STATISTICS camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b4b025cd2..0434d2555 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -81,6 +81,9 @@ #include "InstanceCheck.hpp" #include "NotificationManager.hpp" #include "PresetComboBoxes.hpp" +#if ENABLE_PROJECT_DIRTY_STATE +#include "ProjectDirtyStateManager.hpp" +#endif // ENABLE_PROJECT_DIRTY_STATE #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -1434,6 +1437,10 @@ struct Plater::priv Preview *preview; NotificationManager* notification_manager { nullptr }; +#if ENABLE_PROJECT_DIRTY_STATE + ProjectDirtyStateManager dirty_state; +#endif // ENABLE_PROJECT_DIRTY_STATE + BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; @@ -1504,6 +1511,10 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~priv(); +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_project_state_debug_window() const { dirty_state.render_debug_window(); } +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + enum class UpdateParams { FORCE_FULL_SCREEN_REFRESH = 1, FORCE_BACKGROUND_PROCESSING_UPDATE = 2, @@ -4418,9 +4429,9 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) // Initialization performed in the private c-tor } -Plater::~Plater() -{ -} +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW Sidebar& Plater::sidebar() { return *p->sidebar; } Model& Plater::model() { return p->model; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ff81dad26..f2d60d801 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -128,7 +128,11 @@ public: Plater(const Plater &) = delete; Plater &operator=(Plater &&) = delete; Plater &operator=(const Plater &) = delete; - ~Plater(); + ~Plater() = default; + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_project_state_debug_window() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW Sidebar& sidebar(); Model& model(); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp new file mode 100644 index 000000000..9a19676b2 --- /dev/null +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -0,0 +1,47 @@ +#include "libslic3r/libslic3r.h" +#include "ProjectDirtyStateManager.hpp" +#include "ImGuiWrapper.hpp" +#include "GUI_App.hpp" + +#if ENABLE_PROJECT_DIRTY_STATE + +namespace Slic3r { +namespace GUI { + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void ProjectDirtyStateManager::render_debug_window() const +{ + auto color = [](bool value) { + return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + }; + auto text = [](bool value) { + return value ? "true" : "false"; + }; + + std::string title = "Project dirty state statistics"; + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(title, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + bool dirty = is_dirty(); + imgui.text_colored(color(dirty), "State:"); + ImGui::SameLine(); + imgui.text_colored(color(dirty), text(dirty)); + + ImGui::Separator(); + imgui.text_colored(color(m_state.plater), "Plater:"); + ImGui::SameLine(); + imgui.text_colored(color(m_state.plater), text(m_state.plater)); + + imgui.text_colored(color(m_state.presets), "Presets:"); + ImGui::SameLine(); + imgui.text_colored(color(m_state.presets), text(m_state.presets)); + + imgui.end(); +} +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_PROJECT_DIRTY_STATE + diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp new file mode 100644 index 000000000..81ac28915 --- /dev/null +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -0,0 +1,35 @@ +#ifndef slic3r_ProjectDirtyStateManager_hpp_ +#define slic3r_ProjectDirtyStateManager_hpp_ + +#if ENABLE_PROJECT_DIRTY_STATE + +namespace Slic3r { +namespace GUI { + +class ProjectDirtyStateManager +{ + struct DirtyState + { + bool plater{ false }; + bool presets{ false }; + + bool is_dirty() const { return plater || presets; } + }; + + DirtyState m_state; + +public: + bool is_dirty() const { return m_state.is_dirty(); } + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_debug_window() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_PROJECT_DIRTY_STATE + +#endif // slic3r_ProjectDirtyStateManager_hpp_ + From 5d4b7c03b603945cec03e270faca14f957f08cb0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 13:17:29 +0200 Subject: [PATCH 02/79] Extended interface of project dirty state manager --- src/slic3r/GUI/GUI_App.cpp | 8 ++ src/slic3r/GUI/GUI_ObjectList.cpp | 10 +- src/slic3r/GUI/MainFrame.cpp | 48 +++++++++ src/slic3r/GUI/MainFrame.hpp | 9 ++ src/slic3r/GUI/Plater.cpp | 103 +++++++++++++++++++- src/slic3r/GUI/Plater.hpp | 10 ++ src/slic3r/GUI/ProjectDirtyStateManager.cpp | 20 ++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 10 ++ src/slic3r/GUI/Tab.cpp | 7 +- 9 files changed, 219 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b22cd6009..793ef80b7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -904,6 +904,14 @@ bool GUI_App::on_init_inner() } else load_current_presets(); + +#if ENABLE_PROJECT_DIRTY_STATE + if (plater_ != nullptr) { +// plater_->reset_project_initial_presets(); + plater_->update_project_dirty_from_presets(); + } +#endif // ENABLE_PROJECT_DIRTY_STATE + mainframe->Show(true); obj_list()->set_min_height(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 58560c8bd..bce64f9ea 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -7,6 +7,9 @@ #include "GUI_App.hpp" #include "I18N.hpp" #include "Plater.hpp" +#if ENABLE_PROJECT_DIRTY_STATE +#include "MainFrame.hpp" +#endif // ENABLE_PROJECT_DIRTY_STATE #include "OptionsGroup.hpp" #include "Tab.hpp" @@ -1457,12 +1460,15 @@ void ObjectList::load_shape_object(const std::string& type_name) if (obj_idx < 0) return; - take_snapshot(_(L("Add Shape"))); + take_snapshot(_L("Add Shape")); // Create mesh BoundingBoxf3 bb; TriangleMesh mesh = create_mesh(type_name, bb); - load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name)); + load_mesh_object(mesh, _L("Shape") + "-" + _(type_name)); +#if ENABLE_PROJECT_DIRTY_STATE + wxGetApp().mainframe->update_title(); +#endif // ENABLE_PROJECT_DIRTY_STATE } void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 35b1c16d8..832ebc257 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -206,6 +206,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // declare events Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { +#if ENABLE_PROJECT_DIRTY_STATE + if (m_plater != nullptr) + m_plater->save_project_if_dirty(); +#endif // ENABLE_PROJECT_DIRTY_STATE + if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { event.Veto(); return; @@ -487,8 +492,14 @@ void MainFrame::update_title() // m_plater->get_project_filename() produces file name including path, but excluding extension. // Don't try to remove the extension, it would remove part of the file name after the last dot! wxString project = from_path(into_path(m_plater->get_project_filename()).filename()); +#if ENABLE_PROJECT_DIRTY_STATE + wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : ""; + if (!dirty_marker.empty() || !project.empty()) + title = dirty_marker + project + " - "; +#else if (!project.empty()) title += (project + " - "); +#endif // ENABLE_PROJECT_DIRTY_STATE } std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID; @@ -668,10 +679,36 @@ bool MainFrame::can_start_new_project() const return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty()); } +#if ENABLE_PROJECT_DIRTY_STATE +bool MainFrame::can_save() const +{ + return (m_plater != nullptr) && !m_plater->model().objects.empty() && !m_plater->get_project_filename().empty() && m_plater->is_project_dirty(); +} + +bool MainFrame::can_save_as() const +{ + return (m_plater != nullptr) && !m_plater->model().objects.empty(); +} + +void MainFrame::save_project() +{ + save_project_as(m_plater->get_project_filename(".3mf")); +} + +void MainFrame::save_project_as(const wxString& filename) +{ + bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false; + if (ret) { +// wxGetApp().update_saved_preset_from_current_preset(); + m_plater->reset_project_dirty_after_save(); + } +} +#else bool MainFrame::can_save() const { return (m_plater != nullptr) && !m_plater->model().objects.empty(); } +#endif // ENABLE_PROJECT_DIRTY_STATE bool MainFrame::can_export_model() const { @@ -977,16 +1014,27 @@ void MainFrame::init_menubar_as_editor() Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId()); +#if ENABLE_PROJECT_DIRTY_STATE + append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"), + [this](wxCommandEvent&) { save_project(); }, "save", nullptr, + [this](){return m_plater != nullptr && can_save(); }, this); +#else append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"), [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); +#endif // ENABLE_PROJECT_DIRTY_STATE #ifdef __APPLE__ append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"), #else append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"), #endif // __APPLE__ +#if ENABLE_PROJECT_DIRTY_STATE + [this](wxCommandEvent&) { save_project_as(); }, "save", nullptr, + [this](){return m_plater != nullptr && can_save_as(); }, this); +#else [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); +#endif // ENABLE_PROJECT_DIRTY_STATE fileMenu->AppendSeparator(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 0971fdc77..307cdf1ae 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -91,7 +91,9 @@ class MainFrame : public DPIFrame void on_value_changed(wxCommandEvent&); bool can_start_new_project() const; +#if !ENABLE_PROJECT_DIRTY_STATE bool can_save() const; +#endif // !ENABLE_PROJECT_DIRTY_STATE bool can_export_model() const; bool can_export_toolpaths() const; bool can_export_supports() const; @@ -184,6 +186,13 @@ public: // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; +#if ENABLE_PROJECT_DIRTY_STATE + bool can_save() const; + bool can_save_as() const; + void save_project(); + void save_project_as(const wxString& filename = wxString()); +#endif // ENABLE_PROJECT_DIRTY_STATE + void add_to_recent_projects(const wxString& filename); PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0434d2555..9de4641c0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1393,7 +1393,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi this->MSWUpdateDragImageOnLeave(); #endif // WIN32 +#if ENABLE_PROJECT_DIRTY_STATE + bool res = (m_plater != nullptr) ? m_plater->load_files(filenames) : false; + wxGetApp().mainframe->update_title(); + return res; +#else return (m_plater != nullptr) ? m_plater->load_files(filenames) : false; +#endif // ENABLE_PROJECT_DIRTY_STATE } // State to manage showing after export notifications and device ejecting @@ -1511,9 +1517,26 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~priv(); +#if ENABLE_PROJECT_DIRTY_STATE + bool is_project_dirty() const { return dirty_state.is_dirty(); } + void update_project_dirty_from_presets() { dirty_state.update_from_presets(); } + bool save_project_if_dirty() { + if (dirty_state.is_dirty()) { + MainFrame* mainframe = wxGetApp().mainframe; + if (mainframe->can_save_as()) { + wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL); + if (dlg.ShowModal() == wxID_CANCEL) + return false; + mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); + } + } + return true; + } + void reset_project_dirty_after_save() { dirty_state.reset_after_save(); } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const { dirty_state.render_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +#endif // ENABLE_PROJECT_DIRTY_STATE enum class UpdateParams { FORCE_FULL_SCREEN_REFRESH = 1, @@ -4216,6 +4239,11 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) } this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data); this->undo_redo_stack().release_least_recently_used(); + +#if ENABLE_PROJECT_DIRTY_STATE + dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack()); +#endif // ENABLE_PROJECT_DIRTY_STATE + // 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(); @@ -4346,6 +4374,10 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator 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")); } + +#if ENABLE_PROJECT_DIRTY_STATE + dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack()); +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */) @@ -4429,9 +4461,15 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) // Initialization performed in the private c-tor } +#if ENABLE_PROJECT_DIRTY_STATE +bool Plater::is_project_dirty() const { return p->is_project_dirty(); } +void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); } +bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); } +void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +#endif // ENABLE_PROJECT_DIRTY_STATE Sidebar& Plater::sidebar() { return *p->sidebar; } Model& Plater::model() { return p->model; } @@ -4442,12 +4480,30 @@ SLAPrint& Plater::sla_print() { return p->sla_print; } void Plater::new_project() { +#if ENABLE_PROJECT_DIRTY_STATE + if (!p->save_project_if_dirty()) + return; +#endif // ENABLE_PROJECT_DIRTY_STATE + p->select_view_3D("3D"); +#if ENABLE_PROJECT_DIRTY_STATE + take_snapshot(_L("New Project")); + Plater::SuppressSnapshots suppress(this); + reset(); +// reset_project_initial_presets(); + update_project_dirty_from_presets(); +#else wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::load_project() { +#if ENABLE_PROJECT_DIRTY_STATE + if (!p->save_project_if_dirty()) + return; +#endif // ENABLE_PROJECT_DIRTY_STATE + // Ask user for a project file name. wxString input_file; wxGetApp().load_project(this, input_file); @@ -4471,8 +4527,16 @@ void Plater::load_project(const wxString& filename) std::vector res = load_files(input_paths); // if res is empty no data has been loaded +#if ENABLE_PROJECT_DIRTY_STATE + if (!res.empty()) { + p->set_project_filename(filename); +// reset_project_initial_presets(); + update_project_dirty_from_presets(); + } +#else if (!res.empty()) p->set_project_filename(filename); +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::add_model(bool imperial_units/* = false*/) @@ -4503,7 +4567,13 @@ void Plater::add_model(bool imperial_units/* = false*/) } Plater::TakeSnapshot snapshot(this, snapshot_label); +#if ENABLE_PROJECT_DIRTY_STATE + std::vector res = load_files(paths, true, false, imperial_units); + if (!res.empty()) + wxGetApp().mainframe->update_title(); +#else load_files(paths, true, false, imperial_units); +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::import_sl1_archive() @@ -5187,24 +5257,39 @@ void Plater::export_amf() } } +#if ENABLE_PROJECT_DIRTY_STATE +bool Plater::export_3mf(const boost::filesystem::path& output_path) +#else void Plater::export_3mf(const boost::filesystem::path& output_path) +#endif // ENABLE_PROJECT_DIRTY_STATE { if (p->model.objects.empty() || canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) +#if ENABLE_PROJECT_DIRTY_STATE + return false; +#else return; +#endif // ENABLE_PROJECT_DIRTY_STATE wxString path; bool export_config = true; - if (output_path.empty()) - { + if (output_path.empty()) { path = p->get_export_file(FT_3MF); +#if ENABLE_PROJECT_DIRTY_STATE + if (path.empty()) { return false; } +#else if (path.empty()) { return; } +#endif // ENABLE_PROJECT_DIRTY_STATE } else path = from_path(output_path); if (!path.Lower().EndsWith(".3mf")) +#if ENABLE_PROJECT_DIRTY_STATE + return false; +#else return; +#endif // ENABLE_PROJECT_DIRTY_STATE DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); const std::string path_u8 = into_u8(path); @@ -5212,6 +5297,19 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; ThumbnailData thumbnail_data; p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); +#if ENABLE_PROJECT_DIRTY_STATE + bool ret = Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data); + if (ret) { + // Success + p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path)); + p->set_project_filename(path); + } + else { + // Failure + p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path)); + } + return ret; +#else if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) { // Success p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path)); @@ -5221,6 +5319,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) // Failure p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path)); } +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::reload_from_disk() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f2d60d801..ead9679c7 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -130,9 +130,15 @@ public: Plater &operator=(const Plater &) = delete; ~Plater() = default; +#if ENABLE_PROJECT_DIRTY_STATE + bool is_project_dirty() const; + void update_project_dirty_from_presets(); + bool save_project_if_dirty(); + void reset_project_dirty_after_save(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +#endif // ENABLE_PROJECT_DIRTY_STATE Sidebar& sidebar(); Model& model(); @@ -201,7 +207,11 @@ public: void export_gcode(bool prefer_removable); void export_stl(bool extended = false, bool selection_only = false); void export_amf(); +#if ENABLE_PROJECT_DIRTY_STATE + bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); +#else void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); +#endif // ENABLE_PROJECT_DIRTY_STATE void reload_from_disk(); void reload_all_from_disk(); bool has_toolpaths_to_export() const; diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9a19676b2..5cf7274bb 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -2,12 +2,32 @@ #include "ProjectDirtyStateManager.hpp" #include "ImGuiWrapper.hpp" #include "GUI_App.hpp" +#include "MainFrame.hpp" #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { namespace GUI { +void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) +{ + if (!wxGetApp().initialized()) + return; + + wxGetApp().mainframe->update_title(); +} + +void ProjectDirtyStateManager::update_from_presets() +{ + wxGetApp().mainframe->update_title(); +} + +void ProjectDirtyStateManager::reset_after_save() +{ + m_state.reset(); + wxGetApp().mainframe->update_title(); +} + #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void ProjectDirtyStateManager::render_debug_window() const { diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 81ac28915..b488c00bb 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -4,6 +4,9 @@ #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { +namespace UndoRedo { +class Stack; +} // namespace UndoRedo namespace GUI { class ProjectDirtyStateManager @@ -14,12 +17,19 @@ class ProjectDirtyStateManager bool presets{ false }; bool is_dirty() const { return plater || presets; } + void reset() { + plater = false; + presets = false; + } }; DirtyState m_state; public: bool is_dirty() const { return m_state.is_dirty(); } + void update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); + void update_from_presets(); + void reset_after_save(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 11c4875eb..97117f418 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1215,9 +1215,8 @@ void Tab::apply_config_from_cache() // to update number of "filament" selection boxes when the number of extruders change. void Tab::on_presets_changed() { - if (wxGetApp().plater() == nullptr) { + if (wxGetApp().plater() == nullptr) return; - } // Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets wxGetApp().plater()->sidebar().update_presets(m_type); @@ -1235,6 +1234,10 @@ void Tab::on_presets_changed() // clear m_dependent_tabs after first update from select_preset() // to avoid needless preset loading from update() function m_dependent_tabs.clear(); + +#if ENABLE_PROJECT_DIRTY_STATE + wxGetApp().plater()->update_project_dirty_from_presets(); +#endif // ENABLE_PROJECT_DIRTY_STATE } void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) From edbb1d0f69b6dddc46420acada77c7f5d4d45130 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 16:29:05 +0200 Subject: [PATCH 03/79] Project dirty state manager -> presets dirty state --- src/libslic3r/Preset.cpp | 14 ++++- src/libslic3r/Preset.hpp | 30 +++++++++- src/slic3r/GUI/GUI_App.cpp | 63 ++++++++++++++++++++- src/slic3r/GUI/GUI_App.hpp | 10 +++- src/slic3r/GUI/MainFrame.cpp | 16 +++++- src/slic3r/GUI/Plater.cpp | 18 ++++-- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/ProjectDirtyStateManager.cpp | 17 ++++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 7 ++- src/slic3r/GUI/Tab.cpp | 6 ++ src/slic3r/GUI/Tab.hpp | 42 ++++++++++++-- 11 files changed, 206 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ecb20a18e..aae6a29dc 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -617,11 +617,17 @@ const std::vector& Preset::sla_printer_options() PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), +#if ENABLE_PROJECT_DIRTY_STATE + m_saved_preset(type, "", false), +#endif // ENABLE_PROJECT_DIRTY_STATE m_idx_selected(0) { // Insert just the default preset. this->add_default_preset(keys, defaults, default_name); m_edited_preset.config.apply(m_presets.front().config); +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE } void PresetCollection::reset(bool delete_files) @@ -798,7 +804,10 @@ std::pair PresetCollection::load_external_preset( // The source config may contain keys from many possible preset types. Just copy those that relate to this preset. this->get_edited_preset().config.apply_only(combined_config, keys, true); this->update_dirty(); - assert(this->get_edited_preset().is_dirty); +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE + assert(this->get_edited_preset().is_dirty); return std::make_pair(&(*it), this->get_edited_preset().is_dirty); } if (inherits.empty()) { @@ -1208,6 +1217,9 @@ Preset& PresetCollection::select_preset(size_t idx) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets; for (size_t i = 0; i < m_num_default_presets; ++i) m_presets[i].is_visible = default_visible; diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 6e56ad911..8d407fb64 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -346,6 +346,11 @@ public: Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } +#if ENABLE_PROJECT_DIRTY_STATE + // Return the last saved preset. + const Preset& get_saved_preset() const { return m_saved_preset; } +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist. PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const; PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); } @@ -365,8 +370,16 @@ public: // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } +#if ENABLE_PROJECT_DIRTY_STATE + void discard_current_changes() { + m_presets[m_idx_selected].reset_dirty(); + m_edited_preset = m_presets[m_idx_selected]; + update_saved_preset_from_current_preset(); + } +#else void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; } - +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return a preset by its name. If the preset is active, a temporary copy is returned. // If a preset is not found by its name, null is returned. Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false); @@ -440,6 +453,16 @@ public: std::vector current_different_from_parent_options(const bool deep_compare = false) const { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } +#if ENABLE_PROJECT_DIRTY_STATE + // Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ. + bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); } + // Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ. + std::vector saved_dirty_options(const bool deep_compare = false) const + { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); } + // Copy edited preset into saved preset. + void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; } +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return a sorted list of system preset names. // Used for validating the "inherits" flag when importing user's config bundles. // Returns names of all system presets including the former names of these presets. @@ -527,6 +550,11 @@ private: std::map m_map_system_profile_renamed; // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user. Preset m_edited_preset; +#if ENABLE_PROJECT_DIRTY_STATE + // Contains a copy of the last saved selected preset. + Preset m_saved_preset; +#endif // ENABLE_PROJECT_DIRTY_STATE + // Selected preset. size_t m_idx_selected; // Is the "- default -" preset suppressed? diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 793ef80b7..610e5f07a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -907,7 +907,7 @@ bool GUI_App::on_init_inner() #if ENABLE_PROJECT_DIRTY_STATE if (plater_ != nullptr) { -// plater_->reset_project_initial_presets(); + plater_->reset_project_dirty_initial_presets(); plater_->update_project_dirty_from_presets(); } #endif // ENABLE_PROJECT_DIRTY_STATE @@ -1673,7 +1673,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu) break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. +#if ENABLE_PROJECT_DIRTY_STATE + if (check_and_save_current_preset_changes()) { +#else if (check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name")); // set current normal font for dialog children, @@ -1688,7 +1692,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu) } break; case ConfigMenuSnapshots: +#if ENABLE_PROJECT_DIRTY_STATE + if (check_and_save_current_preset_changes()) { +#else if (check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE std::string on_snapshot; if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) on_snapshot = app_config->get("on_snapshot"); @@ -1789,8 +1797,57 @@ void GUI_App::add_config_menu(wxMenuBar *menu) menu->Append(local_menu, _L("&Configuration")); } +#if ENABLE_PROJECT_DIRTY_STATE +bool GUI_App::has_unsaved_preset_changes() const +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (const Tab* const tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology) && tab->saved_preset_is_dirty()) + return true; + } + return false; +} + +bool GUI_App::has_current_preset_changes() const +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (const Tab* const tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + return true; + } + return false; +} + +void GUI_App::update_saved_preset_from_current_preset() +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab* tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology)) + tab->update_saved_preset_from_current_preset(); + } +} + +std::vector> GUI_App::get_selected_presets() const +{ + std::vector> ret; + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab* tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology)) { + const PresetCollection* presets = tab->get_presets(); + ret.push_back({ static_cast(presets->type()), presets->get_selected_preset_name() }); + } + } + return ret; +} +#endif // ENABLE_PROJECT_DIRTY_STATE + // 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. +#if ENABLE_PROJECT_DIRTY_STATE +bool GUI_App::check_and_save_current_preset_changes(const wxString& header) +{ + if (this->plater()->model().objects.empty() && has_current_preset_changes()) { +#else bool GUI_App::check_unsaved_changes(const wxString &header) { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); @@ -1802,8 +1859,8 @@ bool GUI_App::check_unsaved_changes(const wxString &header) break; } - if (has_unsaved_changes) - { + if (has_unsaved_changes) { +#endif // ENABLE_PROJECT_DIRTY_STATE UnsavedChangesDialog dlg(header); if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL) return false; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index f1ee0746a..d1df4212b 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -209,7 +209,15 @@ public: void update_mode(); void add_config_menu(wxMenuBar *menu); - bool check_unsaved_changes(const wxString &header = wxString()); +#if ENABLE_PROJECT_DIRTY_STATE + bool has_unsaved_preset_changes() const; + bool has_current_preset_changes() const; + void update_saved_preset_from_current_preset(); + std::vector> get_selected_presets() const; + bool check_and_save_current_preset_changes(const wxString& header = wxString()); +#else + bool check_unsaved_changes(const wxString& header = wxString()); +#endif // ENABLE_PROJECT_DIRTY_STATE bool check_print_host_queue(); bool checked_tab(Tab* tab); void load_current_presets(bool check_printer_presets = true); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 832ebc257..f556431f6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -209,9 +209,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #if ENABLE_PROJECT_DIRTY_STATE if (m_plater != nullptr) m_plater->save_project_if_dirty(); -#endif // ENABLE_PROJECT_DIRTY_STATE + if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) { +#else if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE event.Veto(); return; } @@ -1559,7 +1561,11 @@ void MainFrame::export_config() // Load a config file containing a Print, Filament & Printer preset. void MainFrame::load_config_file() { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; wxFileDialog dlg(this, _L("Select configuration to load:"), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), @@ -1588,7 +1594,11 @@ bool MainFrame::load_config_file(const std::string &path) void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; // validate current configuration in case it's dirty auto err = wxGetApp().preset_bundle->full_config().validate(); @@ -1620,7 +1630,11 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) // but that behavior was not documented and likely buggy. void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/) { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; if (file.IsEmpty()) { wxFileDialog dlg(this, _L("Select configuration to load:"), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9de4641c0..0ea20bd5e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1525,14 +1525,18 @@ struct Plater::priv MainFrame* mainframe = wxGetApp().mainframe; if (mainframe->can_save_as()) { wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL); - if (dlg.ShowModal() == wxID_CANCEL) + int res = dlg.ShowModal(); + if (res == wxID_YES) + mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); + else if (res == wxID_CANCEL) return false; - mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); } } return true; } void reset_project_dirty_after_save() { dirty_state.reset_after_save(); } + void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); } + #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const { dirty_state.render_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW @@ -4284,8 +4288,13 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator 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->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA"; +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_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))) +#else if (! wxGetApp().check_unsaved_changes(format_wxstr(_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))) +#endif // ENABLE_PROJECT_DIRTY_STATE // Don't switch the profiles. return; } @@ -4466,6 +4475,7 @@ bool Plater::is_project_dirty() const { return p->is_project_dirty(); } void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); } bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); } void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); } +void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW @@ -4490,7 +4500,7 @@ void Plater::new_project() take_snapshot(_L("New Project")); Plater::SuppressSnapshots suppress(this); reset(); -// reset_project_initial_presets(); + reset_project_dirty_initial_presets(); update_project_dirty_from_presets(); #else wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); @@ -4530,7 +4540,7 @@ void Plater::load_project(const wxString& filename) #if ENABLE_PROJECT_DIRTY_STATE if (!res.empty()) { p->set_project_filename(filename); -// reset_project_initial_presets(); + reset_project_dirty_initial_presets(); update_project_dirty_from_presets(); } #else diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ead9679c7..9b42863ec 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -135,6 +135,7 @@ public: void update_project_dirty_from_presets(); bool save_project_if_dirty(); void reset_project_dirty_after_save(); + void reset_project_dirty_initial_presets(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5cf7274bb..5fc45ccec 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -19,15 +19,32 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRed void ProjectDirtyStateManager::update_from_presets() { + m_state.presets = false; + std::vector> selected_presets = wxGetApp().get_selected_presets(); + for (const auto& [type, name] : selected_presets) { + m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name; + } + m_state.presets |= wxGetApp().has_unsaved_preset_changes(); wxGetApp().mainframe->update_title(); } void ProjectDirtyStateManager::reset_after_save() { + reset_initial_presets(); + m_state.reset(); wxGetApp().mainframe->update_title(); } +void ProjectDirtyStateManager::reset_initial_presets() +{ + m_initial_presets = std::array(); + std::vector> selected_presets = wxGetApp().get_selected_presets(); + for (const auto& [type, name] : selected_presets) { + m_initial_presets[type] = name; + } +} + #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void ProjectDirtyStateManager::render_debug_window() const { diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index b488c00bb..2aa6680c6 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_ProjectDirtyStateManager_hpp_ #define slic3r_ProjectDirtyStateManager_hpp_ +#include "libslic3r/Preset.hpp" + #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { @@ -25,12 +27,15 @@ class ProjectDirtyStateManager DirtyState m_state; + // keeps track of initial selected presets + std::array m_initial_presets; + public: bool is_dirty() const { return m_state.is_dirty(); } void update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); void update_from_presets(); void reset_after_save(); - + void reset_initial_presets(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 97117f418..294af5f76 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2113,10 +2113,16 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex return sizer; } +#if ENABLE_PROJECT_DIRTY_STATE +bool Tab::saved_preset_is_dirty() const { return m_presets->saved_is_dirty(); } +void Tab::update_saved_preset_from_current_preset() { m_presets->update_saved_preset_from_current_preset(); } +bool Tab::current_preset_is_dirty() const { return m_presets->current_is_dirty(); } +#else bool Tab::current_preset_is_dirty() { return m_presets->current_is_dirty(); } +#endif // ENABLE_PROJECT_DIRTY_STATE void TabPrinter::build() { diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 8cbc6585a..0a11e838a 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -270,7 +270,11 @@ public: Preset::Type type() const { return m_type; } // The tab is already constructed. bool completed() const { return m_completed; } - virtual bool supports_printer_technology(const PrinterTechnology tech) = 0; +#if ENABLE_PROJECT_DIRTY_STATE + virtual bool supports_printer_technology(const PrinterTechnology tech) const = 0; +#else + virtual bool supports_printer_technology(const PrinterTechnology tech) = 0; +#endif // ENABLE_PROJECT_DIRTY_STATE void create_preset_tab(); void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name, @@ -333,7 +337,13 @@ public: Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); +#if ENABLE_PROJECT_DIRTY_STATE + bool current_preset_is_dirty() const; + bool saved_preset_is_dirty() const; + void update_saved_preset_from_current_preset(); +#else bool current_preset_is_dirty(); +#endif // ENABLE_PROJECT_DIRTY_STATE DynamicPrintConfig* get_config() { return m_config; } PresetCollection* get_presets() { return m_presets; } @@ -387,7 +397,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#endif // ENABLE_PROJECT_DIRTY_STATE private: ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr; @@ -417,7 +431,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; class TabPrinter : public Tab @@ -471,7 +489,11 @@ public: void init_options_list() override; void msw_rescale() override; void sys_color_changed() override; - bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; } +#else + bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } +#endif // ENABLE_PROJECT_DIRTY_STATE wxSizer* create_bed_shape_widget(wxWindow* parent); void cache_extruder_cnt(); @@ -491,7 +513,11 @@ public: void toggle_options() override {}; void update() override; void init_options_list() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; class TabSLAPrint : public Tab @@ -510,7 +536,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; } // GUI From 926ecd0585d3d40b8f9e9adc9e968a01351ea723 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Apr 2021 12:58:14 +0200 Subject: [PATCH 04/79] Project dirty state manager -> plater dirty state --- src/slic3r/GUI/Plater.cpp | 3 + src/slic3r/GUI/Plater.hpp | 3 + src/slic3r/GUI/ProjectDirtyStateManager.cpp | 131 +++++++++++++++++--- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 20 ++- src/slic3r/GUI/Tab.hpp | 18 +-- 5 files changed, 149 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0ea20bd5e..5ffd4e51c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6190,6 +6190,9 @@ bool Plater::can_mirror() const { return p->can_mirror(); } bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); } const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } +#if ENABLE_PROJECT_DIRTY_STATE +const UndoRedo::Stack& Plater::undo_redo_stack_active() const { return p->undo_redo_stack(); } +#endif // ENABLE_PROJECT_DIRTY_STATE void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9b42863ec..8d228e68f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -242,6 +242,9 @@ public: // For the memory statistics. const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const; void clear_undo_redo_stack_main(); +#if ENABLE_PROJECT_DIRTY_STATE + const Slic3r::UndoRedo::Stack& undo_redo_stack_active() const; +#endif // ENABLE_PROJECT_DIRTY_STATE // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo. void enter_gizmos_stack(); void leave_gizmos_stack(); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5fc45ccec..5d4cd8605 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -1,19 +1,68 @@ #include "libslic3r/libslic3r.h" + #include "ProjectDirtyStateManager.hpp" #include "ImGuiWrapper.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" +#include "I18N.hpp" +#include "Plater.hpp" +#include "../Utils/UndoRedo.hpp" + +#include + +#include +#include #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { namespace GUI { +enum class EStackType +{ + Main, + Gizmo +}; + +static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) { + const std::vector& snapshots = stack.snapshots(); + const size_t active_snapshot_time = stack.active_snapshot_time(); + const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time)); + const int idx = it - snapshots.begin() - 1; + const Slic3r::UndoRedo::Snapshot* ret = (0 < idx && (size_t)idx < snapshots.size() - 1) ? + &snapshots[idx] : nullptr; + + assert(ret != nullptr); + + return ret; +} + +static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { + auto skip_main = [](const UndoRedo::Snapshot& snapshot) { + return boost::starts_with(snapshot.name, _utf8("Selection")); + }; + + const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); + + const UndoRedo::Snapshot* curr = snapshot; + const std::vector& snapshots = stack.snapshots(); + while (type == EStackType::Main && skip_main(*curr)) { + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - 1))); + } + + return curr; +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) return; + if (&main_stack == &active_stack) + update_from_undo_redo_main_stack(main_stack); + else + update_from_undo_redo_gizmo_stack(active_stack); + wxGetApp().mainframe->update_title(); } @@ -31,8 +80,27 @@ void ProjectDirtyStateManager::update_from_presets() void ProjectDirtyStateManager::reset_after_save() { reset_initial_presets(); - m_state.reset(); + + const Plater* plater = wxGetApp().plater(); + const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); + const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); + + if (&main_stack == &active_stack) { + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + +// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; +// std::cout << "SAVE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; + + m_last_save.main = valid_snapshot->timestamp; + } + else { + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); + +// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + } + wxGetApp().mainframe->update_title(); } @@ -48,35 +116,66 @@ void ProjectDirtyStateManager::reset_initial_presets() #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void ProjectDirtyStateManager::render_debug_window() const { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + auto color = [](bool value) { return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); }; auto text = [](bool value) { return value ? "true" : "false"; }; + auto append_item = [color, text, &imgui](const std::string& name, bool value) { + imgui.text_colored(color(value), name); + ImGui::SameLine(); + imgui.text_colored(color(value), text(value)); + }; - std::string title = "Project dirty state statistics"; - ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.begin(title, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - bool dirty = is_dirty(); - imgui.text_colored(color(dirty), "State:"); - ImGui::SameLine(); - imgui.text_colored(color(dirty), text(dirty)); + imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + append_item("State:", is_dirty()); ImGui::Separator(); - imgui.text_colored(color(m_state.plater), "Plater:"); - ImGui::SameLine(); - imgui.text_colored(color(m_state.plater), text(m_state.plater)); - - imgui.text_colored(color(m_state.presets), "Presets:"); - ImGui::SameLine(); - imgui.text_colored(color(m_state.presets), text(m_state.presets)); + append_item("Plater:", m_state.plater); + append_item("Presets:", m_state.presets); + append_item("Current gizmo:", m_state.current_gizmo); imgui.end(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack) +{ + m_state.plater = false; + + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + +// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + + if (active_snapshot->name == _utf8("New Project") || + active_snapshot->name == _utf8("Reset Project") || + boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) + return; + + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); + +// std::cout << "UPDATE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; + + m_state.plater = valid_snapshot->timestamp != m_last_save.main; +} + +void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack) +{ + m_state.current_gizmo = false; + + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + +// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + + if (active_snapshot->name == "Gizmos-Initial") + return; + + m_state.current_gizmo = true; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 2aa6680c6..2eac4ba92 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -17,15 +17,29 @@ class ProjectDirtyStateManager { bool plater{ false }; bool presets{ false }; + bool current_gizmo{ false }; - bool is_dirty() const { return plater || presets; } + bool is_dirty() const { return plater || presets || current_gizmo; } void reset() { plater = false; presets = false; + current_gizmo = false; + } + }; + + struct Timestamps + { + size_t main{ 0 }; + size_t gizmo{ 0 }; + + void reset() { + main = 0; + gizmo = 0; } }; DirtyState m_state; + Timestamps m_last_save; // keeps track of initial selected presets std::array m_initial_presets; @@ -39,6 +53,10 @@ public: #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +private: + void update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack); }; } // namespace GUI diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 0a11e838a..c1806cfcd 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -387,8 +387,8 @@ class TabPrint : public Tab { public: TabPrint(wxNotebook* parent) : -// Tab(parent, _(L("Print Settings")), L("print")) {} - Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {} +// Tab(parent, _L("Print Settings"), L("print")) {} + Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_PRINT) {} ~TabPrint() {} void build() override; @@ -421,8 +421,8 @@ private: std::map m_overrides_options; public: TabFilament(wxNotebook* parent) : -// Tab(parent, _(L("Filament Settings")), L("filament")) {} - Tab(parent, _(L("Filament Settings")), Slic3r::Preset::TYPE_FILAMENT) {} +// Tab(parent, _L("Filament Settings"), L("filament")) {} + Tab(parent, _L("Filament Settings"), Slic3r::Preset::TYPE_FILAMENT) {} ~TabFilament() {} void build() override; @@ -467,7 +467,7 @@ public: // TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), L("printer")) {} TabPrinter(wxNotebook* parent) : - Tab(parent, _(L("Printer Settings")), Slic3r::Preset::TYPE_PRINTER) {} + Tab(parent, _L("Printer Settings"), Slic3r::Preset::TYPE_PRINTER) {} ~TabPrinter() {} void build() override; @@ -504,8 +504,8 @@ class TabSLAMaterial : public Tab { public: TabSLAMaterial(wxNotebook* parent) : -// Tab(parent, _(L("Material Settings")), L("sla_material")) {} - Tab(parent, _(L("Material Settings")), Slic3r::Preset::TYPE_SLA_MATERIAL) {} +// Tab(parent, _L("Material Settings"), L("sla_material")) {} + Tab(parent, _L("Material Settings"), Slic3r::Preset::TYPE_SLA_MATERIAL) {} ~TabSLAMaterial() {} void build() override; @@ -524,8 +524,8 @@ class TabSLAPrint : public Tab { public: TabSLAPrint(wxNotebook* parent) : -// Tab(parent, _(L("Print Settings")), L("sla_print")) {} - Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_SLA_PRINT) {} +// Tab(parent, _L("Print Settings"), L("sla_print")) {} + Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_SLA_PRINT) {} ~TabSLAPrint() {} ogStaticText* m_support_object_elevation_description_line = nullptr; From bfbc683a59c00487eba8f1b01f2aeaf1bac586d0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Apr 2021 14:26:04 +0200 Subject: [PATCH 05/79] Follow-up of 926ecd0585d3d40b8f9e9adc9e968a01351ea723 -> Improved management of plater dirty state --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 97 +++++++++++--------- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 20 +--- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b200623f4..7ff274aac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -45,18 +45,18 @@ bool GLGizmoSlaSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; - m_desc["head_diameter"] = _(L("Head diameter")) + ": "; - m_desc["lock_supports"] = _(L("Lock supports under new islands")); - m_desc["remove_selected"] = _(L("Remove selected points")); - m_desc["remove_all"] = _(L("Remove all points")); - m_desc["apply_changes"] = _(L("Apply changes")); - m_desc["discard_changes"] = _(L("Discard changes")); - m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; - m_desc["points_density"] = _(L("Support points density")) + ": "; - m_desc["auto_generate"] = _(L("Auto-generate points")); - m_desc["manual_editing"] = _(L("Manual editing")); - m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; - m_desc["reset_direction"] = _(L("Reset direction")); + m_desc["head_diameter"] = _L("Head diameter") + ": "; + m_desc["lock_supports"] = _L("Lock supports under new islands"); + m_desc["remove_selected"] = _L("Remove selected points"); + m_desc["remove_all"] = _L("Remove all points"); + m_desc["apply_changes"] = _L("Apply changes"); + m_desc["discard_changes"] = _L("Discard changes"); + m_desc["minimal_distance"] = _L("Minimal points distance") + ": "; + m_desc["points_density"] = _L("Support points density") + ": "; + m_desc["auto_generate"] = _L("Auto-generate points"); + m_desc["manual_editing"] = _L("Manual editing"); + m_desc["clipping_of_view"] = _L("Clipping of view")+ ": "; + m_desc["reset_direction"] = _L("Reset direction"); return true; } @@ -372,7 +372,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_selection_empty) { std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add support point")); m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_parent.set_as_dirty(); m_wait_for_up_event = true; @@ -512,7 +512,7 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) std::abort(); } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete support point")); for (unsigned int idx=0; idxconfig.set("support_points_minimal_distance", m_minimal_point_distance_stash); mo->config.set("support_points_density_relative", (int)m_density_stash); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support parameter change")); mo->config.set("support_points_minimal_distance", minimal_point_distance); mo->config.set("support_points_density_relative", (int)density); wxGetApp().obj_list()->update_and_show_object_settings_item(); @@ -867,10 +867,9 @@ bool GLGizmoSlaSupports::on_is_selectable() const std::string GLGizmoSlaSupports::on_get_name() const { - return (_(L("SLA Support Points")) + " [L]").ToUTF8().data(); + return (_L("SLA Support Points") + " [L]").ToUTF8().data(); } - CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const { return CommonGizmosDataID( @@ -895,7 +894,11 @@ void GLGizmoSlaSupports::on_set_state() // data are not yet available, the CallAfter will postpone taking the // snapshot until they are. No, it does not feel right. wxGetApp().CallAfter([]() { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); +#if ENABLE_PROJECT_DIRTY_STATE + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo")); +#else + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned on")); +#endif // ENABLE_PROJECT_DIRTY_STATE }); } @@ -909,8 +912,8 @@ void GLGizmoSlaSupports::on_set_state() wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem // on OSX with the wxMessageDialog being shown several times when clicked into. - wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually " - "edited support points?")) + "\n",_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO); + wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " + "edited support points?") + "\n",_L("Save changes?"), wxICON_QUESTION | wxYES | wxNO); if (dlg.ShowModal() == wxID_YES) editing_mode_apply_changes(); else @@ -922,7 +925,11 @@ void GLGizmoSlaSupports::on_set_state() else { // we are actually shutting down disable_editing_mode(); // so it is not active next time the gizmo opens - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); +#if ENABLE_PROJECT_DIRTY_STATE + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo")); +#else + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned off")); +#endif // ENABLE_PROJECT_DIRTY_STATE m_normal_cache.clear(); m_old_mo_id = -1; } @@ -953,7 +960,7 @@ void GLGizmoSlaSupports::on_stop_dragging() && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected { m_editing_cache[m_hover_id] = m_point_before_drag; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move support point")); m_editing_cache[m_hover_id] = backup; } } @@ -1046,7 +1053,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() disable_editing_mode(); // this leaves the editing mode undo/redo stack and must be done before the snapshot is taken if (unsaved_changes()) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support points edit"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support points edit")); m_normal_cache.clear(); for (const CacheEntry& ce : m_editing_cache) @@ -1125,14 +1132,14 @@ void GLGizmoSlaSupports::get_data_from_backend() void GLGizmoSlaSupports::auto_generate() { wxMessageDialog dlg(GUI::wxGetApp().plater(), - _(L("Autogeneration will erase all manually edited points.")) + "\n\n" + - _(L("Are you sure you want to do it?")) + "\n", - _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + _L("Autogeneration will erase all manually edited points.") + "\n\n" + + _L("Are you sure you want to do it?") + "\n", + _L("Warning"), wxICON_WARNING | wxYES | wxNO); ModelObject* mo = m_c->selection_info()->model_object(); if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); mo->sla_points_status = sla::PointsStatus::Generating; } @@ -1180,7 +1187,7 @@ bool GLGizmoSlaSupports::unsaved_changes() const } SlaGizmoHelpDialog::SlaGizmoHelpDialog() -: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +: wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); const wxString ctrl = GUI::shortkey_ctrl_prefix(); @@ -1191,7 +1198,7 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog() const wxFont& font = wxGetApp().small_font(); const wxFont& bold_font = wxGetApp().bold_font(); - auto note_text = new wxStaticText(this, wxID_ANY, _(L("Note: some shortcuts work in (non)editing mode only."))); + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: some shortcuts work in (non)editing mode only.")); note_text->SetFont(font); auto vsizer = new wxBoxSizer(wxVERTICAL); @@ -1209,21 +1216,21 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog() vsizer->AddSpacer(20); std::vector> shortcuts; - shortcuts.push_back(std::make_pair(_(L("Left click")), _(L("Add point")))); - shortcuts.push_back(std::make_pair(_(L("Right click")), _(L("Remove point")))); - shortcuts.push_back(std::make_pair(_(L("Drag")), _(L("Move point")))); - shortcuts.push_back(std::make_pair(ctrl+_(L("Left click")), _(L("Add point to selection")))); - shortcuts.push_back(std::make_pair(alt+_(L("Left click")), _(L("Remove point from selection")))); - shortcuts.push_back(std::make_pair(wxString("Shift+")+_(L("Drag")), _(L("Select by rectangle")))); - shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _(L("Deselect by rectangle")))); - shortcuts.push_back(std::make_pair(ctrl+"A", _(L("Select all points")))); - shortcuts.push_back(std::make_pair("Delete", _(L("Remove selected points")))); - shortcuts.push_back(std::make_pair(ctrl+_(L("Mouse wheel")), _(L("Move clipping plane")))); - shortcuts.push_back(std::make_pair("R", _(L("Reset clipping plane")))); - shortcuts.push_back(std::make_pair("Enter", _(L("Apply changes")))); - shortcuts.push_back(std::make_pair("Esc", _(L("Discard changes")))); - shortcuts.push_back(std::make_pair("M", _(L("Switch to editing mode")))); - shortcuts.push_back(std::make_pair("A", _(L("Auto-generate points")))); + shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add point"))); + shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove point"))); + shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move point"))); + shortcuts.push_back(std::make_pair(ctrl+_L("Left click"), _L("Add point to selection"))); + shortcuts.push_back(std::make_pair(alt+_L("Left click"), _L("Remove point from selection"))); + shortcuts.push_back(std::make_pair(wxString("Shift+")+_L("Drag"), _L("Select by rectangle"))); + shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _L("Deselect by rectangle"))); + shortcuts.push_back(std::make_pair(ctrl+"A", _L("Select all points"))); + shortcuts.push_back(std::make_pair("Delete", _L("Remove selected points"))); + shortcuts.push_back(std::make_pair(ctrl+_L("Mouse wheel"), _L("Move clipping plane"))); + shortcuts.push_back(std::make_pair("R", _L("Reset clipping plane"))); + shortcuts.push_back(std::make_pair("Enter", _L("Apply changes"))); + shortcuts.push_back(std::make_pair("Esc", _L("Discard changes"))); + shortcuts.push_back(std::make_pair("M", _L("Switch to editing mode"))); + shortcuts.push_back(std::make_pair("A", _L("Auto-generate points"))); for (const auto& pair : shortcuts) { auto shortcut = new wxStaticText(this, wxID_ANY, pair.first); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5d4cd8605..a14674ed8 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -29,7 +29,7 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac const size_t active_snapshot_time = stack.active_snapshot_time(); const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time)); const int idx = it - snapshots.begin() - 1; - const Slic3r::UndoRedo::Snapshot* ret = (0 < idx && (size_t)idx < snapshots.size() - 1) ? + const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ? &snapshots[idx] : nullptr; assert(ret != nullptr); @@ -39,7 +39,9 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { auto skip_main = [](const UndoRedo::Snapshot& snapshot) { - return boost::starts_with(snapshot.name, _utf8("Selection")); + return boost::starts_with(snapshot.name, _utf8("Selection")) || + boost::starts_with(snapshot.name, _utf8("Entering")) || + boost::starts_with(snapshot.name, _utf8("Leaving")); }; const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); @@ -90,15 +92,12 @@ void ProjectDirtyStateManager::reset_after_save() const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); -// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; -// std::cout << "SAVE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; - m_last_save.main = valid_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); -// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; } wxGetApp().mainframe->update_title(); @@ -147,18 +146,12 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::Un m_state.plater = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - -// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; - if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); - -// std::cout << "UPDATE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; - m_state.plater = valid_snapshot->timestamp != m_last_save.main; } @@ -167,9 +160,6 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::U m_state.current_gizmo = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - -// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; - if (active_snapshot->name == "Gizmos-Initial") return; From e89a14c8a7343753b3390e1317eaecc32f31b638 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 9 Apr 2021 08:26:48 +0200 Subject: [PATCH 06/79] Project dirty state manager -> current gizmo dirty state --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 104 +++++++++++++++----- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 8 +- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index a14674ed8..82557c000 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,24 +37,38 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { - auto skip_main = [](const UndoRedo::Snapshot& snapshot) { +static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack, size_t last_save_timestamp) { + auto skip_main = [last_save_timestamp](const UndoRedo::Snapshot& snapshot) { return boost::starts_with(snapshot.name, _utf8("Selection")) || - boost::starts_with(snapshot.name, _utf8("Entering")) || - boost::starts_with(snapshot.name, _utf8("Leaving")); + ((boost::starts_with(snapshot.name, _utf8("Entering")) || boost::starts_with(snapshot.name, _utf8("Leaving"))) && + (last_save_timestamp == 0 || last_save_timestamp != snapshot.timestamp)); }; - const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); - - const UndoRedo::Snapshot* curr = snapshot; + const UndoRedo::Snapshot* curr = get_active_snapshot(stack); const std::vector& snapshots = stack.snapshots(); + size_t shift = 1; while (type == EStackType::Main && skip_main(*curr)) { - curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - 1))); + const UndoRedo::Snapshot* temp = curr; + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); + shift = (curr == temp) ? shift + 1 : 1; } - return curr; } +static std::string extract_gizmo_name(const std::string& s) { + static const std::array prefixes = { _utf8("Entering"), _utf8("Leaving") }; + + std::string ret; + for (const std::string& prefix : prefixes) { + if (boost::starts_with(s, prefix)) + ret = s.substr(prefix.length() + 1); + + if (!ret.empty()) + break; + } + return ret; +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) @@ -81,25 +95,44 @@ void ProjectDirtyStateManager::update_from_presets() void ProjectDirtyStateManager::reset_after_save() { - reset_initial_presets(); - m_state.reset(); - const Plater* plater = wxGetApp().plater(); const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack, m_last_save.main); + +// if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { +// if (m_state.current_gizmo) { +// int a = 0; +// } +// else { +// int a = 0; +// } +// } m_last_save.main = valid_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, active_stack, m_last_save.gizmo); + const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); + if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { + if (m_state.current_gizmo) { + m_last_save.main = main_active_snapshot->timestamp; + } +// else { +// int a = 0; +// } + } + + m_last_save.gizmo = valid_snapshot->timestamp; } + reset_initial_presets(); + m_state.reset(); wxGetApp().mainframe->update_title(); } @@ -123,19 +156,32 @@ void ProjectDirtyStateManager::render_debug_window() const auto text = [](bool value) { return value ? "true" : "false"; }; - auto append_item = [color, text, &imgui](const std::string& name, bool value) { + auto append_bool_item = [color, text, &imgui](const std::string& name, bool value) { imgui.text_colored(color(value), name); ImGui::SameLine(); imgui.text_colored(color(value), text(value)); }; + auto append_int_item = [color, text, &imgui](const std::string& name, int value) { + imgui.text(name); + ImGui::SameLine(); + imgui.text(std::to_string(value)); + }; imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - append_item("State:", is_dirty()); - ImGui::Separator(); - append_item("Plater:", m_state.plater); - append_item("Presets:", m_state.presets); - append_item("Current gizmo:", m_state.current_gizmo); + if (ImGui::CollapsingHeader("Dirty state")) { + append_bool_item("Overall:", is_dirty()); + ImGui::Separator(); + append_bool_item("Plater:", m_state.plater); + append_bool_item("Presets:", m_state.presets); + append_bool_item("Current gizmo:", m_state.current_gizmo); + append_bool_item("Any gizmo:", !m_state.gizmos.empty()); + } + + if (ImGui::CollapsingHeader("Last save timestamps")) { + append_int_item("Main:", m_last_save.main); + append_int_item("Gizmo:", m_last_save.gizmo); + } imgui.end(); } @@ -151,7 +197,16 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::Un boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); + size_t search_timestamp = 0; + if (boost::starts_with(active_snapshot->name, _utf8("Leaving")) || boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + if (m_state.current_gizmo) + m_state.gizmos.push_back(extract_gizmo_name(active_snapshot->name)); + m_state.current_gizmo = false; + m_last_save.gizmo = 0; + search_timestamp = m_last_save.main; + } + + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack, search_timestamp); m_state.plater = valid_snapshot->timestamp != m_last_save.main; } @@ -160,10 +215,13 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::U m_state.current_gizmo = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == "Gizmos-Initial") + if (active_snapshot->name == "Gizmos-Initial") { + m_state.current_gizmo = (m_last_save.gizmo != 0); return; + } - m_state.current_gizmo = true; + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, stack, m_last_save.gizmo); + m_state.current_gizmo = valid_snapshot->timestamp != m_last_save.gizmo; } } // namespace GUI diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 2eac4ba92..a1fdd1fda 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -18,16 +18,18 @@ class ProjectDirtyStateManager bool plater{ false }; bool presets{ false }; bool current_gizmo{ false }; + std::vector gizmos; - bool is_dirty() const { return plater || presets || current_gizmo; } + bool is_dirty() const { return plater || presets || current_gizmo || !gizmos.empty(); } void reset() { plater = false; presets = false; current_gizmo = false; + gizmos.clear(); } }; - struct Timestamps + struct LastSaveTimestamps { size_t main{ 0 }; size_t gizmo{ 0 }; @@ -39,7 +41,7 @@ class ProjectDirtyStateManager }; DirtyState m_state; - Timestamps m_last_save; + LastSaveTimestamps m_last_save; // keeps track of initial selected presets std::array m_initial_presets; From 8c3d098ff68674939ce8617ed177d6116cdbe456 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Apr 2021 15:19:03 +0200 Subject: [PATCH 07/79] Project dirty state manager -> management of gizmos dirty state WIP --- src/slic3r/GUI/ImGuiWrapper.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 225 +++++++++++++++----- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 43 +++- 4 files changed, 212 insertions(+), 63 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index db7af046b..5726477ab 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -488,8 +488,7 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool ( int i=0; const char* item_text; - while (items_getter(is_undo, i, &item_text)) - { + while (items_getter(is_undo, i, &item_text)) { ImGui::Selectable(item_text, i < hovered); if (ImGui::IsItemHovered()) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5ffd4e51c..dad3fcef0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4245,7 +4245,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) this->undo_redo_stack().release_least_recently_used(); #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot, undo_redo_stack_main(), undo_redo_stack()); #endif // ENABLE_PROJECT_DIRTY_STATE // Save the last active preset name of a particular printer technology. @@ -4385,7 +4385,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo, undo_redo_stack_main(), undo_redo_stack()); #endif // ENABLE_PROJECT_DIRTY_STATE } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 82557c000..dfdeabd02 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,22 +37,59 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack, size_t last_save_timestamp) { - auto skip_main = [last_save_timestamp](const UndoRedo::Snapshot& snapshot) { - return boost::starts_with(snapshot.name, _utf8("Selection")) || - ((boost::starts_with(snapshot.name, _utf8("Entering")) || boost::starts_with(snapshot.name, _utf8("Leaving"))) && - (last_save_timestamp == 0 || last_save_timestamp != snapshot.timestamp)); +static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos) { + auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { + if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (gizmos.current) + return true; + + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); + if (gizmos.is_used_and_modified(*leaving_snapshot)) + return true; + } + } + return false; + }; + + auto skip_main = [&gizmos, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + if (snapshot.name == _utf8("New Project")) + return true; + else if (snapshot.name == _utf8("Reset Project")) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Load Project:"))) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Selection"))) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (!is_gizmo_with_modifications(snapshot)) + return true; + } + else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) { + if (!gizmos.is_used_and_modified(snapshot)) + return true; + } + + return false; + }; + + auto skip_gizmo = [&gizmos](const UndoRedo::Snapshot& snapshot) { + // put here any needed condition to skip the snapshot + return false; }; const UndoRedo::Snapshot* curr = get_active_snapshot(stack); const std::vector& snapshots = stack.snapshots(); size_t shift = 1; - while (type == EStackType::Main && skip_main(*curr)) { + while (curr->timestamp > 0 && ((type == EStackType::Main && skip_main(*curr)) || (type == EStackType::Gizmo && skip_gizmo(*curr)))) { const UndoRedo::Snapshot* temp = curr; curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } - return curr; + return curr->timestamp > 0 ? curr : nullptr; } static std::string extract_gizmo_name(const std::string& s) { @@ -69,15 +106,63 @@ static std::string extract_gizmo_name(const std::string& s) { return ret; } -void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) +void ProjectDirtyStateManager::DirtyState::Gizmos::add_used(const UndoRedo::Snapshot& snapshot) +{ + const std::string name = extract_gizmo_name(snapshot.name); + auto it = used.find(name); + if (it == used.end()) + it = used.insert({ name, { {} } }).first; + + it->second.modified_timestamps.push_back(snapshot.timestamp); +} + +void ProjectDirtyStateManager::DirtyState::Gizmos::remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack) +{ + const std::vector& snapshots = main_stack.snapshots(); + for (auto& [name, gizmo] : used) { + auto it = gizmo.modified_timestamps.begin(); + while (it != gizmo.modified_timestamps.end()) { + size_t timestamp = *it; + auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [timestamp](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == timestamp; }); + if (snapshot_it == snapshots.end()) + it = gizmo.modified_timestamps.erase(it); + else + ++it; + } + } +} + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const +{ + for (auto& [name, gizmo] : used) { + if (!gizmo.modified_timestamps.empty()) + return true; + } + return false; +} +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const +{ + for (auto& [name, gizmo] : used) { + for (size_t i : gizmo.modified_timestamps) { + if (i == snapshot.timestamp) + return true; + } + } + return false; +} + +void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) return; if (&main_stack == &active_stack) - update_from_undo_redo_main_stack(main_stack); + update_from_undo_redo_main_stack(type, main_stack); else - update_from_undo_redo_gizmo_stack(active_stack); + update_from_undo_redo_gizmo_stack(type, active_stack); wxGetApp().mainframe->update_title(); } @@ -101,34 +186,21 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack, m_last_save.main); - -// if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { -// if (m_state.current_gizmo) { -// int a = 0; -// } -// else { -// int a = 0; -// } -// } - - m_last_save.main = valid_snapshot->timestamp; + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos); + assert(saveable_snapshot != nullptr); + m_last_save.main = saveable_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, active_stack, m_last_save.gizmo); - + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { - if (m_state.current_gizmo) { + if (m_state.gizmos.current) { m_last_save.main = main_active_snapshot->timestamp; } -// else { -// int a = 0; -// } } - m_last_save.gizmo = valid_snapshot->timestamp; + m_last_save.gizmo = saveable_snapshot->timestamp; } reset_initial_presets(); @@ -153,75 +225,128 @@ void ProjectDirtyStateManager::render_debug_window() const auto color = [](bool value) { return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); }; - auto text = [](bool value) { + auto bool_to_text = [](bool value) { return value ? "true" : "false"; }; - auto append_bool_item = [color, text, &imgui](const std::string& name, bool value) { + auto append_bool_item = [color, bool_to_text, &imgui](const std::string& name, bool value) { imgui.text_colored(color(value), name); ImGui::SameLine(); - imgui.text_colored(color(value), text(value)); + imgui.text_colored(color(value), bool_to_text(value)); }; - auto append_int_item = [color, text, &imgui](const std::string& name, int value) { + auto append_int_item = [&imgui](const std::string& name, int value) { imgui.text(name); ImGui::SameLine(); imgui.text(std::to_string(value)); }; + auto append_snapshot_item = [&imgui](const std::string& label, const UndoRedo::Snapshot* snapshot) { + imgui.text(label); + ImGui::SameLine(100); + if (snapshot != nullptr) + imgui.text(snapshot->name + " (" + std::to_string(snapshot->timestamp) + ")"); + else + imgui.text("-"); + }; - imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + imgui.begin(std::string("Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - if (ImGui::CollapsingHeader("Dirty state")) { + if (ImGui::CollapsingHeader("Dirty state", ImGuiTreeNodeFlags_DefaultOpen)) { append_bool_item("Overall:", is_dirty()); ImGui::Separator(); append_bool_item("Plater:", m_state.plater); append_bool_item("Presets:", m_state.presets); - append_bool_item("Current gizmo:", m_state.current_gizmo); - append_bool_item("Any gizmo:", !m_state.gizmos.empty()); + append_bool_item("Current gizmo:", m_state.gizmos.current); } - if (ImGui::CollapsingHeader("Last save timestamps")) { + if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) { append_int_item("Main:", m_last_save.main); append_int_item("Gizmo:", m_last_save.gizmo); } + if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { + const UndoRedo::Stack& stack = wxGetApp().plater()->undo_redo_stack_main(); + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + append_snapshot_item("Active:", active_snapshot); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + append_snapshot_item("Last saveable:", last_saveable_snapshot); + if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + const std::vector& snapshots = stack.snapshots(); + for (const UndoRedo::Snapshot& snapshot : snapshots) { + bool active = active_snapshot->timestamp == snapshot.timestamp; + imgui.text_colored(color(active), snapshot.name); + ImGui::SameLine(150); + imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); + if (&snapshot == last_saveable_snapshot) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (S)"); + } + } + } + } + + if (m_state.gizmos.any_used_modified()) { + if (ImGui::CollapsingHeader("Gizmos", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Indent(10.0f); + for (const auto& [name, gizmo] : m_state.gizmos.used) { + if (!gizmo.modified_timestamps.empty()) { + if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + for (size_t i : gizmo.modified_timestamps) { + imgui.text(std::to_string(i)); + } + } + } + } + ImGui::Unindent(10.0f); + } + } + imgui.end(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW -void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack) +void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack) { m_state.plater = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + + if (type == UpdateType::TakeSnapshot) + m_state.gizmos.remove_obsolete_used(stack); + if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; size_t search_timestamp = 0; - if (boost::starts_with(active_snapshot->name, _utf8("Leaving")) || boost::starts_with(active_snapshot->name, _utf8("Entering"))) { - if (m_state.current_gizmo) - m_state.gizmos.push_back(extract_gizmo_name(active_snapshot->name)); - m_state.current_gizmo = false; + if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + m_state.gizmos.current = false; + m_last_save.gizmo = 0; + search_timestamp = m_last_save.main; + } + else if (boost::starts_with(active_snapshot->name, _utf8("Leaving"))) { + if (m_state.gizmos.current) + m_state.gizmos.add_used(*active_snapshot); + m_state.gizmos.current = false; m_last_save.gizmo = 0; search_timestamp = m_last_save.main; } - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack, search_timestamp); - m_state.plater = valid_snapshot->timestamp != m_last_save.main; + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main); } -void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack) +void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack) { - m_state.current_gizmo = false; + m_state.gizmos.current = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); if (active_snapshot->name == "Gizmos-Initial") { - m_state.current_gizmo = (m_last_save.gizmo != 0); + m_state.gizmos.current = (m_last_save.gizmo != 0); return; } - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, stack, m_last_save.gizmo); - m_state.current_gizmo = valid_snapshot->timestamp != m_last_save.gizmo; + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos); + m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); } } // namespace GUI diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index a1fdd1fda..a7c16705b 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -8,27 +8,52 @@ namespace Slic3r { namespace UndoRedo { class Stack; +struct Snapshot; } // namespace UndoRedo -namespace GUI { +namespace GUI { class ProjectDirtyStateManager { +public: + enum class UpdateType : unsigned char + { + TakeSnapshot, + UndoRedoTo + }; + struct DirtyState { + struct Gizmos + { + struct Gizmo + { + std::vector modified_timestamps; + }; + + bool current{ false }; + std::map used; + + void add_used(const UndoRedo::Snapshot& snapshot); + void remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack); +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + bool any_used_modified() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + bool is_used_and_modified(const UndoRedo::Snapshot& snapshot) const; + }; + bool plater{ false }; bool presets{ false }; - bool current_gizmo{ false }; - std::vector gizmos; + Gizmos gizmos; - bool is_dirty() const { return plater || presets || current_gizmo || !gizmos.empty(); } + bool is_dirty() const { return plater || presets || gizmos.current; } void reset() { plater = false; presets = false; - current_gizmo = false; - gizmos.clear(); + gizmos.current = false; } }; +private: struct LastSaveTimestamps { size_t main{ 0 }; @@ -48,7 +73,7 @@ class ProjectDirtyStateManager public: bool is_dirty() const { return m_state.is_dirty(); } - void update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); + void update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); void update_from_presets(); void reset_after_save(); void reset_initial_presets(); @@ -57,8 +82,8 @@ public: #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW private: - void update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack); - void update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack); }; } // namespace GUI From ce73671f475583b4cedab2d8f17b1d695e16fd03 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 09:36:19 +0200 Subject: [PATCH 08/79] Project dirty state manager -> Improvements to management of gizmos dirty state --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 37 ++++++++++++++++++++ src/slic3r/GUI/ProjectDirtyStateManager.cpp | 22 ++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 91aef75d9..3a932c598 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -32,6 +32,42 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic +#if ENABLE_PROJECT_DIRTY_STATE +// port of 948bc382655993721d93d3b9fce9b0186fcfb211 +void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) +{ + Plater* plater = wxGetApp().plater(); + + // Following is needed to prevent taking an extra snapshot when the activation of + // the internal stack happens when the gizmo is already active (such as open gizmo, + // close gizmo, undo, start painting). The internal stack does not activate on the + // undo, because that would obliterate all future of the main stack (user would + // have to close the gizmo himself, he has no access to main undo/redo after the + // internal stack opens). We don't want the "entering" snapshot taken in this case, + // because there already is one. + std::string last_snapshot_name; + plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name); + + if (activate && !m_internal_stack_active) { + std::string str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS + ? _u8L("Entering Paint-on supports") + : _u8L("Entering Seam painting"); + if (last_snapshot_name != str) + Plater::TakeSnapshot(plater, str); + plater->enter_gizmos_stack(); + m_internal_stack_active = true; + } + if (!activate && m_internal_stack_active) { + plater->leave_gizmos_stack(); + std::string str = get_painter_type() == PainterGizmoType::SEAM + ? _u8L("Leaving Seam painting") + : _u8L("Leaving Paint-on supports"); + if (last_snapshot_name != str) + Plater::TakeSnapshot(plater, str); + m_internal_stack_active = false; + } +} +#else void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) { if (activate && ! m_internal_stack_active) { @@ -51,6 +87,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) m_internal_stack_active = false; } } +#endif // ENABLE_PROJECT_DIRTY_STATE diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index dfdeabd02..06f54359f 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -259,7 +259,7 @@ void ProjectDirtyStateManager::render_debug_window() const if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) { append_int_item("Main:", m_last_save.main); - append_int_item("Gizmo:", m_last_save.gizmo); + append_int_item("Current gizmo:", m_last_save.gizmo); } if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { @@ -279,6 +279,10 @@ void ProjectDirtyStateManager::render_debug_window() const ImGui::SameLine(); imgui.text_colored(color(active), " (S)"); } + if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (LS)"); + } } } } @@ -307,11 +311,11 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, { m_state.plater = false; - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (type == UpdateType::TakeSnapshot) m_state.gizmos.remove_obsolete_used(stack); + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) @@ -319,6 +323,18 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, size_t search_timestamp = 0; if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + if (type == UpdateType::UndoRedoTo) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot->timestamp + 1))); + if (m_state.gizmos.is_used_and_modified(*leaving_snapshot)) { + m_state.plater = (leaving_snapshot != nullptr && leaving_snapshot->timestamp != m_last_save.main); + return; + } + } + } m_state.gizmos.current = false; m_last_save.gizmo = 0; search_timestamp = m_last_save.main; From c691464659e634934a889dd6226697671ff27cf1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 09:09:06 +0200 Subject: [PATCH 09/79] Project dirty state manager -> Improvements update of plater dirty state after save commands --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 102 +++++++++++++------- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 1 + 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 06f54359f..9c6d097d7 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,7 +37,8 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos) { +static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, + const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos, size_t last_save_main) { auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { if (boost::starts_with(snapshot.name, _utf8("Entering"))) { if (gizmos.current) @@ -55,7 +56,7 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con return false; }; - auto skip_main = [&gizmos, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + auto skip_main = [&gizmos, last_save_main, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { if (snapshot.name == _utf8("New Project")) return true; else if (snapshot.name == _utf8("Reset Project")) @@ -65,11 +66,11 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con else if (boost::starts_with(snapshot.name, _utf8("Selection"))) return true; else if (boost::starts_with(snapshot.name, _utf8("Entering"))) { - if (!is_gizmo_with_modifications(snapshot)) + if (last_save_main != snapshot.timestamp + 1 && !is_gizmo_with_modifications(snapshot)) return true; } else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) { - if (!gizmos.is_used_and_modified(snapshot)) + if (last_save_main != snapshot.timestamp && !gizmos.is_used_and_modified(snapshot)) return true; } @@ -89,6 +90,12 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } + if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + } return curr->timestamp > 0 ? curr : nullptr; } @@ -154,6 +161,11 @@ bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const Un return false; } +void ProjectDirtyStateManager::DirtyState::Gizmos::reset() +{ + used.clear(); +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) @@ -186,20 +198,18 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos); + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); assert(saveable_snapshot != nullptr); m_last_save.main = saveable_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { - if (m_state.gizmos.current) { - m_last_save.main = main_active_snapshot->timestamp; - } + if (m_state.gizmos.current) + m_last_save.main = main_active_snapshot->timestamp + 1; } - + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos, m_last_save.main); m_last_save.gizmo = saveable_snapshot->timestamp; } @@ -262,27 +272,43 @@ void ProjectDirtyStateManager::render_debug_window() const append_int_item("Current gizmo:", m_last_save.gizmo); } + const UndoRedo::Stack& main_stack = wxGetApp().plater()->undo_redo_stack_main(); + const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); + const UndoRedo::Snapshot* main_last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); + const std::vector& main_snapshots = main_stack.snapshots(); + if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { - const UndoRedo::Stack& stack = wxGetApp().plater()->undo_redo_stack_main(); - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - append_snapshot_item("Active:", active_snapshot); - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); - append_snapshot_item("Last saveable:", last_saveable_snapshot); - if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { - const std::vector& snapshots = stack.snapshots(); - for (const UndoRedo::Snapshot& snapshot : snapshots) { - bool active = active_snapshot->timestamp == snapshot.timestamp; + append_snapshot_item("Active:", main_active_snapshot); + append_snapshot_item("Last saveable:", main_last_saveable_snapshot); + } + + if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + for (const UndoRedo::Snapshot& snapshot : main_snapshots) { + bool active = main_active_snapshot->timestamp == snapshot.timestamp; + imgui.text_colored(color(active), snapshot.name); + ImGui::SameLine(150); + imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); + if (&snapshot == main_last_saveable_snapshot) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (S)"); + } + if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (LS)"); + } + } + } + + const UndoRedo::Stack& active_stack = wxGetApp().plater()->undo_redo_stack_active(); + if (&active_stack != &main_stack) { + if (ImGui::CollapsingHeader("Gizmo undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + const UndoRedo::Snapshot* active_active_snapshot = get_active_snapshot(active_stack); + const std::vector& active_snapshots = active_stack.snapshots(); + for (const UndoRedo::Snapshot& snapshot : active_snapshots) { + bool active = active_active_snapshot->timestamp == snapshot.timestamp; imgui.text_colored(color(active), snapshot.name); ImGui::SameLine(150); imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); - if (&snapshot == last_saveable_snapshot) { - ImGui::SameLine(); - imgui.text_colored(color(active), " (S)"); - } - if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { - ImGui::SameLine(); - imgui.text_colored(color(active), " (LS)"); - } } } } @@ -293,9 +319,13 @@ void ProjectDirtyStateManager::render_debug_window() const for (const auto& [name, gizmo] : m_state.gizmos.used) { if (!gizmo.modified_timestamps.empty()) { if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - for (size_t i : gizmo.modified_timestamps) { - imgui.text(std::to_string(i)); + std::string modified_timestamps; + for (size_t i = 0; i < gizmo.modified_timestamps.size(); ++i) { + if (i > 0) + modified_timestamps += " | "; + modified_timestamps += std::to_string(gizmo.modified_timestamps[i]); } + imgui.text(modified_timestamps); } } } @@ -311,11 +341,17 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, { m_state.plater = false; - if (type == UpdateType::TakeSnapshot) + if (type == UpdateType::TakeSnapshot) { + if (m_last_save.main != 0) { + const std::vector& snapshots = stack.snapshots(); + auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [this](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == m_last_save.main; }); + if (snapshot_it == snapshots.end()) + m_last_save.main = 0; + } m_state.gizmos.remove_obsolete_used(stack); + } const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) @@ -347,7 +383,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, search_timestamp = m_last_save.main; } - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos, m_last_save.main); m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main); } @@ -361,7 +397,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type return; } - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos, m_last_save.main); m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index a7c16705b..79a16edc4 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -39,6 +39,7 @@ public: bool any_used_modified() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW bool is_used_and_modified(const UndoRedo::Snapshot& snapshot) const; + void reset(); }; bool plater{ false }; From f486dedb52754d9db3ba060b75650b089f005b9c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 10:41:38 +0200 Subject: [PATCH 10/79] Disabled tech ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW --- src/libslic3r/Technologies.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index fcb59f1a1..a74ff55ae 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -61,7 +61,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_ From 9cd5ba13f24161b341466d6e2a40150705b5ff85 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 16:07:39 +0200 Subject: [PATCH 11/79] Some refactoring into ProjectDirtyStateManager --- src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 66 ++++++++++++--------- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dad3fcef0..0da345c1e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4245,7 +4245,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) this->undo_redo_stack().release_least_recently_used(); #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot, undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot); #endif // ENABLE_PROJECT_DIRTY_STATE // Save the last active preset name of a particular printer technology. @@ -4385,7 +4385,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo, undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo); #endif // ENABLE_PROJECT_DIRTY_STATE } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9c6d097d7..95cfa042e 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -24,6 +24,7 @@ enum class EStackType Gizmo }; +// returns the current active snapshot (the topmost snapshot in the undo part of the stack) in the given stack static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) { const std::vector& snapshots = stack.snapshots(); const size_t active_snapshot_time = stack.active_snapshot_time(); @@ -33,30 +34,32 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac &snapshots[idx] : nullptr; assert(ret != nullptr); - return ret; } +// returns the last saveable snapshot (the topmost snapshot in the undo part of the stack that can be saved) in the given stack static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos, size_t last_save_main) { - auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { - if (boost::starts_with(snapshot.name, _utf8("Entering"))) { - if (gizmos.current) - return true; - std::string topmost_redo; - wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); - if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { - const std::vector& snapshots = stack.snapshots(); - const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); - if (gizmos.is_used_and_modified(*leaving_snapshot)) + // returns true if the given snapshot is not saveable + auto skip_main = [&gizmos, last_save_main, &stack](const UndoRedo::Snapshot& snapshot) { + auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { + if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (gizmos.current) return true; - } - } - return false; - }; - auto skip_main = [&gizmos, last_save_main, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); + if (gizmos.is_used_and_modified(*leaving_snapshot)) + return true; + } + } + return false; + }; + if (snapshot.name == _utf8("New Project")) return true; else if (snapshot.name == _utf8("Reset Project")) @@ -77,7 +80,8 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con return false; }; - auto skip_gizmo = [&gizmos](const UndoRedo::Snapshot& snapshot) { + // returns true if the given snapshot is not saveable + auto skip_gizmo = [](const UndoRedo::Snapshot& snapshot) { // put here any needed condition to skip the snapshot return false; }; @@ -90,15 +94,18 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } - if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { - std::string topmost_redo; - wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); - if (boost::starts_with(topmost_redo, _utf8("Leaving"))) - curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + if (type == EStackType::Main) { + if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + } } return curr->timestamp > 0 ? curr : nullptr; } +// returns the name of the gizmo contained in the given string static std::string extract_gizmo_name(const std::string& s) { static const std::array prefixes = { _utf8("Entering"), _utf8("Leaving") }; @@ -150,6 +157,7 @@ bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +// returns true if the given snapshot is contained in any of the gizmos caches bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const { for (auto& [name, gizmo] : used) { @@ -166,11 +174,15 @@ void ProjectDirtyStateManager::DirtyState::Gizmos::reset() used.clear(); } -void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) +void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type) { if (!wxGetApp().initialized()) return; + const Plater* plater = wxGetApp().plater(); + const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); + const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); + if (&main_stack == &active_stack) update_from_undo_redo_main_stack(type, main_stack); else @@ -197,13 +209,11 @@ void ProjectDirtyStateManager::reset_after_save() const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); if (&main_stack == &active_stack) { - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); assert(saveable_snapshot != nullptr); m_last_save.main = saveable_snapshot->timestamp; } else { - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { if (m_state.gizmos.current) @@ -233,7 +243,7 @@ void ProjectDirtyStateManager::render_debug_window() const ImGuiWrapper& imgui = *wxGetApp().imgui(); auto color = [](bool value) { - return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) /* orange */: ImVec4(1.0f, 1.0f, 1.0f, 1.0f) /* white */; }; auto bool_to_text = [](bool value) { return value ? "true" : "false"; @@ -392,10 +402,8 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type m_state.gizmos.current = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == "Gizmos-Initial") { - m_state.gizmos.current = (m_last_save.gizmo != 0); + if (active_snapshot->name == "Gizmos-Initial") return; - } const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos, m_last_save.main); m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 79a16edc4..f7ce81a62 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -74,7 +74,7 @@ private: public: bool is_dirty() const { return m_state.is_dirty(); } - void update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); + void update_from_undo_redo_stack(UpdateType type); void update_from_presets(); void reset_after_save(); void reset_initial_presets(); From c8b83ae01164b6afc8e2fa70c86b55522352d166 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 21 Apr 2021 12:41:43 +0200 Subject: [PATCH 12/79] Tech ENABLE_ALLOW_NEGATIVE_Z -> Allow move gizmo to place an object under the printbed --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 303ffe927..369996fba 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,8 @@ #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) // Enable a modified version of automatic downscale on load of objects too big #define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) +// Enable to push object instances under the bed +#define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 97038723b..59a4a62a4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3572,12 +3572,25 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) wipe_tower_origin = v->get_volume_offset(); } +#if ENABLE_ALLOW_NEGATIVE_Z + // Fixes flying instances +#else // Fixes sinking/flying instances +#endif // ENABLE_ALLOW_NEGATIVE_Z for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; +#if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); + if (shift_z > 0.0) { + Vec3d shift(0.0, 0.0, -shift_z); +#else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); +#endif // ENABLE_ALLOW_NEGATIVE_Z m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (object_moved) From 8f385aac4408fb5272357021653081f21329303f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 22 Apr 2021 13:18:36 +0200 Subject: [PATCH 13/79] Tech ENABLE_ALLOW_NEGATIVE_Z -> Shading of sinking instances --- resources/shaders/gouraud.fs | 18 ++++++++++++++-- resources/shaders/gouraud.vs | 16 +++++++++------ src/slic3r/GUI/3DScene.cpp | 40 ++++++++++++++++++++++++++++++++---- src/slic3r/GUI/3DScene.hpp | 5 +++++ 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs index 850d69b08..ed03bfe64 100644 --- a/resources/shaders/gouraud.fs +++ b/resources/shaders/gouraud.fs @@ -4,7 +4,9 @@ const vec3 ZERO = vec3(0.0, 0.0, 0.0); const vec3 GREEN = vec3(0.0, 0.7, 0.0); const vec3 YELLOW = vec3(0.5, 0.7, 0.0); const vec3 RED = vec3(0.7, 0.0, 0.0); +const vec3 WHITE = vec3(1.0, 1.0, 1.0); const float EPSILON = 0.0001; +const float BANDS_WIDTH = 10.0; struct SlopeDetection { @@ -15,6 +17,7 @@ struct SlopeDetection uniform vec4 uniform_color; uniform SlopeDetection slope; +uniform bool sinking; #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -23,27 +26,38 @@ uniform SlopeDetection slope; varying vec3 clipping_planes_dots; -// x = tainted, y = specular; +// x = diffuse, y = specular; varying vec2 intensity; varying vec3 delta_box_min; varying vec3 delta_box_max; +varying vec4 model_pos; +varying float world_pos_z; varying float world_normal_z; varying vec3 eye_normal; +vec3 sinking_color(vec3 color) +{ + return (mod(model_pos.x + model_pos.y + model_pos.z, BANDS_WIDTH) < (0.5 * BANDS_WIDTH)) ? mix(color, ZERO, 0.6666) : color; +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) discard; vec3 color = uniform_color.rgb; float alpha = uniform_color.a; - if (slope.actived && world_normal_z < slope.normal_z - EPSILON) { + if (slope.actived && world_normal_z < slope.normal_z - EPSILON) + { color = vec3(0.7, 0.7, 1.0); alpha = 1.0; } // if the fragment is outside the print volume -> use darker color color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color; + // if the object is sinking, shade it with inclined bands or white around world z = 0 + if (sinking) + color = (abs(world_pos_z) < 0.05) ? WHITE : sinking_color(color); #ifdef ENABLE_ENVIRONMENT_MAP if (use_environment_tex) gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha); diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index ed7e3f56b..f2706b386 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -41,7 +41,7 @@ uniform vec2 z_range; // Clipping plane - general orientation. Used by the SLA gizmo. uniform vec4 clipping_plane; -// x = tainted, y = specular; +// x = diffuse, y = specular; varying vec2 intensity; varying vec3 delta_box_min; @@ -49,6 +49,8 @@ varying vec3 delta_box_max; varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying float world_pos_z; varying float world_normal_z; varying vec3 eye_normal; @@ -69,12 +71,16 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + model_pos = gl_Vertex; + // Point in homogenous coordinates. + vec4 world_pos = print_box.volume_world_matrix * gl_Vertex; + world_pos_z = world_pos.z; + // compute deltas for out of print volume detection (world coordinates) if (print_box.actived) { - vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz; - delta_box_min = v - print_box.min; - delta_box_max = v - print_box.max; + delta_box_min = world_pos.xyz - print_box.min; + delta_box_max = world_pos.xyz - print_box.max; } else { @@ -86,8 +92,6 @@ void main() world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0; gl_Position = ftransform(); - // Point in homogenous coordinates. - vec4 world_pos = print_box.volume_world_matrix * gl_Vertex; // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ba62576f2..57ec1e0d9 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -345,9 +345,16 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size) void GLVolume::set_render_color() { - if (force_native_color || force_neutral_color) - { +#if ENABLE_ALLOW_NEGATIVE_Z + bool outside = is_outside || is_below_printbed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z + + if (force_native_color || force_neutral_color) { +#if ENABLE_ALLOW_NEGATIVE_Z + if (outside && shader_outside_printer_detection_enabled) +#else if (is_outside && shader_outside_printer_detection_enabled) +#endif // ENABLE_ALLOW_NEGATIVE_Z set_render_color(OUTSIDE_COLOR, 4); else { if (force_native_color) @@ -362,17 +369,24 @@ void GLVolume::set_render_color() else if (hover == HS_Deselect) set_render_color(HOVER_DESELECT_COLOR, 4); else if (selected) +#if ENABLE_ALLOW_NEGATIVE_Z + set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); +#else set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); +#endif // ENABLE_ALLOW_NEGATIVE_Z else if (disabled) set_render_color(DISABLED_COLOR, 4); +#if ENABLE_ALLOW_NEGATIVE_Z + else if (outside && shader_outside_printer_detection_enabled) +#else else if (is_outside && shader_outside_printer_detection_enabled) +#endif // ENABLE_ALLOW_NEGATIVE_Z set_render_color(OUTSIDE_COLOR, 4); else set_render_color(color, 4); } - if (!printable) - { + if (!printable) { render_color[0] /= 4; render_color[1] /= 4; render_color[2] /= 4; @@ -504,6 +518,21 @@ void GLVolume::render() const bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); } bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); } +#if ENABLE_ALLOW_NEGATIVE_Z +bool GLVolume::is_sinking() const +{ + if (is_modifier) + return false; + const BoundingBoxf3& box = transformed_convex_hull_bounding_box(); + return box.min(2) < 0.0 && box.max(2) >= 0.0; +} + +bool GLVolume::is_below_printbed() const +{ + return transformed_convex_hull_bounding_box().max(2) < 0.0; +} +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -778,6 +807,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); +#if ENABLE_ALLOW_NEGATIVE_Z + shader->set_uniform("sinking", volume.first->is_sinking()); +#endif // ENABLE_ALLOW_NEGATIVE_Z volume.first->render(); } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 25c5443cd..2d46a4524 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -453,6 +453,11 @@ public: bool is_sla_support() const; bool is_sla_pad() const; +#if ENABLE_ALLOW_NEGATIVE_Z + bool is_sinking() const; + bool is_below_printbed() const; +#endif // ENABLE_ALLOW_NEGATIVE_Z + // Return an estimate of the memory consumed by this class. size_t cpu_memory_used() const { //FIXME what to do wih m_convex_hull? From d4695827ce0fc1e40590b448ccd934be79b930a2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 23 Apr 2021 08:29:29 +0200 Subject: [PATCH 14/79] Tech ENABLE_ALLOW_NEGATIVE_Z -> Keep sinking instances as sinking after applying rotate gizmo --- src/libslic3r/Model.cpp | 13 ++++------- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 43 +++++++++++++++++++++++------------ 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d48443181..208cf9b66 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1430,11 +1430,9 @@ double ModelObject::get_min_z() const { if (instances.empty()) return 0.0; - else - { + else { double min_z = DBL_MAX; - for (size_t i = 0; i < instances.size(); ++i) - { + for (size_t i = 0; i < instances.size(); ++i) { min_z = std::min(min_z, get_instance_min_z(i)); } return min_z; @@ -1445,15 +1443,14 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const { double min_z = DBL_MAX; - ModelInstance* inst = instances[instance_idx]; + const ModelInstance* inst = instances[instance_idx]; const Transform3d& mi = inst->get_matrix(true); - for (const ModelVolume* v : volumes) - { + for (const ModelVolume* v : volumes) { if (!v->is_model_part()) continue; - Transform3d mv = mi * v->get_matrix(); + const Transform3d mv = mi * v->get_matrix(); const TriangleMesh& hull = v->get_convex_hull(); for (const stl_facet &facet : hull.stl.facet_start) for (int i = 0; i < 3; ++ i) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 57ec1e0d9..c64c36ea8 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -524,7 +524,7 @@ bool GLVolume::is_sinking() const if (is_modifier) return false; const BoundingBoxf3& box = transformed_convex_hull_bounding_box(); - return box.min(2) < 0.0 && box.max(2) >= 0.0; + return box.min(2) < -EPSILON && box.max(2) >= -EPSILON; } bool GLVolume::is_below_printbed() const diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 59a4a62a4..1513c5a39 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3610,18 +3610,30 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(_(snapshot_type)); +#if ENABLE_ALLOW_NEGATIVE_Z + // stores current min_z of instances + std::map, double> min_zs; + if (!snapshot_type.empty()) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); - for (const GLVolume* v : m_volumes.volumes) - { + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); if (object_idx == 1000) { // the wipe tower Vec3d offset = v->get_volume_offset(); post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2)))); } - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; int instance_idx = v->instance_idx(); @@ -3631,15 +3643,12 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) // Rotate instances/volumes. ModelObject* model_object = m_model->objects[object_idx]; - if (model_object != nullptr) - { - if (selection_mode == Selection::Instance) - { + if (model_object != nullptr) { + if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); } - else if (selection_mode == Selection::Volume) - { + else if (selection_mode == Selection::Volume) { model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } @@ -3648,12 +3657,18 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) } // Fixes sinking/flying instances - for (const std::pair& i : done) - { + for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; - Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); - m_selection.translate(i.first, i.second, shift); - m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + // leave sinking instances as sinking + if (min_zs.empty() || min_zs.find({i.first, i.second})->second >= 0.0) { +#endif // ENABLE_ALLOW_NEGATIVE_Z + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (!done.empty()) From b6005404119f1a47f84599d2e79a01f70f415996 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 23 Apr 2021 09:11:55 +0200 Subject: [PATCH 15/79] Tech ENABLE_ALLOW_NEGATIVE_Z -> Keep sinking instances as sinking after applying scale gizmo --- src/slic3r/GUI/GLCanvas3D.cpp | 49 +++++++++++++++++++-------- src/slic3r/GUI/Selection.cpp | 62 ++++++++++++++--------------------- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1513c5a39..f4a0d7412 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3660,10 +3660,13 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; #if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking - if (min_zs.empty() || min_zs.find({i.first, i.second})->second >= 0.0) { + if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) { + Vec3d shift(0.0, 0.0, -shift_z); +#else + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); #endif // ENABLE_ALLOW_NEGATIVE_Z - Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); #if ENABLE_ALLOW_NEGATIVE_Z @@ -3685,14 +3688,26 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(_(snapshot_type)); +#if ENABLE_ALLOW_NEGATIVE_Z + // stores current min_z of instances + std::map, double> min_zs; + if (!snapshot_type.empty()) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); - for (const GLVolume* v : m_volumes.volumes) - { + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; int instance_idx = v->instance_idx(); @@ -3702,15 +3717,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) // Rotate instances/volumes ModelObject* model_object = m_model->objects[object_idx]; - if (model_object != nullptr) - { - if (selection_mode == Selection::Instance) - { + if (model_object != nullptr) { + if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); } - else if (selection_mode == Selection::Volume) - { + else if (selection_mode == Selection::Volume) { model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); @@ -3720,16 +3732,25 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) } // Fixes sinking/flying instances - for (const std::pair& i : done) - { + for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; +#if ENABLE_ALLOW_NEGATIVE_Z + double shift_z = m->get_instance_min_z(i.second); + // leave sinking instances as sinking + if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) { + Vec3d shift(0.0, 0.0, -shift_z); +#else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); +#endif // ENABLE_ALLOW_NEGATIVE_Z m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); +#if ENABLE_ALLOW_NEGATIVE_Z + } +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (!done.empty()) - post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); m_dirty = true; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2acb8cb85..2da521d47 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -866,12 +866,10 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; if (is_single_full_instance()) { - if (transformation_type.relative()) - { + if (transformation_type.relative()) { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation @@ -881,8 +879,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type volume.set_instance_scaling_factor(new_scale); } - else - { + else { if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { // Non-uniform scaling. Transform the scaling factors into the local coordinate system. // This is only possible, if the instance rotation is mulitples of ninety degrees. @@ -895,11 +892,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type } else if (is_single_volume() || is_single_modifier()) volume.set_volume_scaling_factor(scale); - else - { + else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); - if (m_mode == Instance) - { + if (m_mode == Instance) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); @@ -908,13 +903,11 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type volume.set_instance_scaling_factor(new_scale); } - else if (m_mode == Volume) - { + else if (m_mode == Volume) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - if (transformation_type.joint()) - { + if (transformation_type.joint()) { Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); } @@ -930,34 +923,34 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH +#if !ENABLE_ALLOW_NEGATIVE_Z ensure_on_bed(); +#endif // !ENABLE_ALLOW_NEGATIVE_Z this->set_bounding_boxes_dirty(); } void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) { - if (is_empty() || (m_mode == Volume)) + if (is_empty() || m_mode == Volume) return; // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones(); const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); - if (opt != nullptr) - { + if (opt != nullptr) { BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.opt_float("max_print_height"))); + BoundingBoxf3 print_volume({ unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0 }, { unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.opt_float("max_print_height") }); Vec3d print_volume_size = print_volume.size(); double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0; double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0; double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0; - if ((sx != 0.0) && (sy != 0.0) && (sz != 0.0)) + if (sx != 0.0 && sy != 0.0 && sz != 0.0) { double s = std::min(sx, std::min(sy, sz)); - if (s != 1.0) - { - wxGetApp().plater()->take_snapshot(_(L("Scale To Fit"))); + if (s != 1.0) { + wxGetApp().plater()->take_snapshot(_L("Scale To Fit")); TransformationType type; type.set_world(); @@ -987,8 +980,7 @@ void Selection::mirror(Axis axis) bool single_full_instance = is_single_full_instance(); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (single_full_instance) (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); else if (m_mode == Volume) @@ -1010,8 +1002,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == (int)object_idx) v->set_instance_offset(v->get_instance_offset() + displacement); @@ -1020,8 +1011,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; @@ -1030,8 +1020,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) continue; // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -1055,18 +1044,16 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) + if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) v->set_instance_offset(v->get_instance_offset() + displacement); } std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; @@ -1075,8 +1062,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -1084,7 +1070,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() != (int)instance_idx)) + if (v->object_idx() != object_idx || v->instance_idx() != (int)instance_idx) continue; v->set_instance_offset(v->get_instance_offset() + displacement); From a83cd647da7ca2c1d9ad4ce0f194b02ad67918e4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 28 Apr 2021 08:49:32 +0200 Subject: [PATCH 16/79] Small refactoring in GLCanvas3D::LayersEditing --- src/slic3r/GUI/GLCanvas3D.cpp | 34 ++++++------------------ src/slic3r/GUI/GLCanvas3D.hpp | 49 ++++++++++++++++------------------- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f4a0d7412..fcd6da83f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -135,27 +135,9 @@ void Size::set_scale_factor(int scale_factor) m_scale_factor = scale_factor; } -GLCanvas3D::LayersEditing::LayersEditing() - : m_enabled(false) - , m_z_texture_id(0) - , m_model_object(nullptr) - , m_object_max_z(0.f) - , m_slicing_parameters(nullptr) - , m_layer_height_profile_modified(false) - , m_adaptive_quality(0.5f) - , state(Unknown) - , band_width(2.0f) - , strength(0.005f) - , last_object_id(-1) - , last_z(0.0f) - , last_action(LAYER_HEIGHT_EDIT_ACTION_INCREASE) -{ -} - GLCanvas3D::LayersEditing::~LayersEditing() { - if (m_z_texture_id != 0) - { + if (m_z_texture_id != 0) { glsafe(::glDeleteTextures(1, &m_z_texture_id)); m_z_texture_id = 0; } @@ -219,7 +201,7 @@ void GLCanvas3D::LayersEditing::set_enabled(bool enabled) m_enabled = is_allowed() && enabled; } -float GLCanvas3D::LayersEditing::s_overelay_window_width; +float GLCanvas3D::LayersEditing::s_overlay_window_width; void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const { @@ -303,7 +285,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (imgui.button(_L("Reset"))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); - GLCanvas3D::LayersEditing::s_overelay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/; + GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/; imgui.end(); const Rect& bar_rect = get_bar_rect_viewport(canvas); @@ -320,7 +302,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) float t = rect.get_top(); float b = rect.get_bottom(); - return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ? + return (rect.get_left() <= x && x <= rect.get_right() && t <= y && y <= b) ? // Inside the bar. (b - y - 1.0f) / (b - t - 1.0f) : // Outside the bar. @@ -330,7 +312,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_bar_rect_screen(canvas); - return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); + return rect.get_left() <= x && x <= rect.get_right() && rect.get_top() <= y && y <= rect.get_bottom(); } Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) @@ -339,7 +321,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); - return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); + return { w - thickness_bar_width(canvas), 0.0f, w, h }; } Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) @@ -348,7 +330,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float half_w = 0.5f * (float)cnv_size.get_width(); float half_h = 0.5f * (float)cnv_size.get_height(); float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); + return { (half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom }; } bool GLCanvas3D::LayersEditing::is_initialized() const @@ -565,7 +547,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) { if (last_object_id >= 0) { if (m_layer_height_profile_modified) { - wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit"))); + wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit")); const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); wxGetApp().obj_list()->update_info_items(last_object_id); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9e9a2501e..bff8d6e05 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -154,53 +154,50 @@ class GLCanvas3D static const float THICKNESS_BAR_WIDTH; private: - bool m_enabled; - unsigned int m_z_texture_id; + bool m_enabled{ false }; + unsigned int m_z_texture_id{ 0 }; // Not owned by LayersEditing. - const DynamicPrintConfig *m_config; + const DynamicPrintConfig *m_config{ nullptr }; // ModelObject for the currently selected object (Model::objects[last_object_id]). - const ModelObject *m_model_object; + const ModelObject *m_model_object{ nullptr }; // Maximum z of the currently selected object (Model::objects[last_object_id]). - float m_object_max_z; + float m_object_max_z{ 0.0f }; // Owned by LayersEditing. - SlicingParameters *m_slicing_parameters; + SlicingParameters *m_slicing_parameters{ nullptr }; std::vector m_layer_height_profile; - bool m_layer_height_profile_modified; + bool m_layer_height_profile_modified{ false }; - mutable float m_adaptive_quality; + mutable float m_adaptive_quality{ 0.5f }; mutable HeightProfileSmoothingParams m_smooth_params; - static float s_overelay_window_width; + static float s_overlay_window_width; - class LayersTexture + struct LayersTexture { - public: - LayersTexture() : width(0), height(0), levels(0), cells(0), valid(false) {} - // Texture data std::vector data; // Width of the texture, top level. - size_t width; + size_t width{ 0 }; // Height of the texture, top level. - size_t height; + size_t height{ 0 }; // For how many levels of detail is the data allocated? - size_t levels; + size_t levels{ 0 }; // Number of texture cells allocated for the height texture. - size_t cells; + size_t cells{ 0 }; // Does it need to be refreshed? - bool valid; + bool valid{ false }; }; LayersTexture m_layers_texture; public: - EState state; - float band_width; - float strength; - int last_object_id; - float last_z; - LayerHeightEditActionType last_action; + EState state{ Unknown }; + float band_width{ 2.0f }; + float strength{ 0.005f }; + int last_object_id{ -1 }; + float last_z{ 0.0f }; + LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE }; - LayersEditing(); + LayersEditing() = default; ~LayersEditing(); void init(); @@ -226,7 +223,7 @@ class GLCanvas3D static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static Rect get_bar_rect_screen(const GLCanvas3D& canvas); static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); - static float get_overlay_window_width() { return LayersEditing::s_overelay_window_width; } + static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; } float object_max_z() const { return m_object_max_z; } From c58572deaa184fc9e4cdc1bcaec39457956cc8e6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 28 Apr 2021 11:07:15 +0200 Subject: [PATCH 17/79] Tech ENABLE_ALLOW_NEGATIVE_Z->Fixed object popping up after editing layer range fields --- src/libslic3r/Model.cpp | 40 ++++++++++++++--------------- src/libslic3r/Model.hpp | 4 +++ src/slic3r/GUI/GUI_ObjectLayers.cpp | 26 ++++++++----------- src/slic3r/GUI/GUI_ObjectList.cpp | 27 +++++++++---------- src/slic3r/GUI/Plater.cpp | 7 +++-- src/slic3r/GUI/Selection.cpp | 23 ++++++----------- 6 files changed, 59 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 208cf9b66..c82627b38 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -954,30 +954,39 @@ void ModelObject::center_around_origin(bool include_modifiers) { // calculate the displacements needed to // center this object around the origin - BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); + const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); // Shift is the vector from the center of the bounding box to the origin - Vec3d shift = -bb.center(); + const Vec3d shift = -bb.center(); this->translate(shift); this->origin_translation += shift; } +#if ENABLE_ALLOW_NEGATIVE_Z +void ModelObject::ensure_on_bed(bool allow_negative_z) +{ + const double min_z = get_min_z(); + if (!allow_negative_z || min_z > 0.0) + translate_instances({ 0.0, 0.0, -min_z }); +} +#else void ModelObject::ensure_on_bed() { - translate_instances(Vec3d(0.0, 0.0, -get_min_z())); + translate_instances({ 0.0, 0.0, -get_min_z() }); } +#endif // ENABLE_ALLOW_NEGATIVE_Z void ModelObject::translate_instances(const Vec3d& vector) { - for (size_t i = 0; i < instances.size(); ++i) - { + for (size_t i = 0; i < instances.size(); ++i) { translate_instance(i, vector); } } void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) { + assert(instance_idx < instances.size()); ModelInstance* i = instances[instance_idx]; i->set_offset(i->get_offset() + vector); invalidate_bounding_box(); @@ -985,8 +994,7 @@ void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) void ModelObject::translate(double x, double y, double z) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->translate(x, y, z); } @@ -996,8 +1004,7 @@ void ModelObject::translate(double x, double y, double z) void ModelObject::scale(const Vec3d &versor) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->scale(versor); } this->invalidate_bounding_box(); @@ -1005,41 +1012,34 @@ void ModelObject::scale(const Vec3d &versor) void ModelObject::rotate(double angle, Axis axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->rotate(angle, axis); } - center_around_origin(); this->invalidate_bounding_box(); } void ModelObject::rotate(double angle, const Vec3d& axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->rotate(angle, axis); } - center_around_origin(); this->invalidate_bounding_box(); } void ModelObject::mirror(Axis axis) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->mirror(axis); } - this->invalidate_bounding_box(); } // This method could only be called before the meshes of this ModelVolumes are not shared! void ModelObject::scale_mesh_after_creation(const Vec3d &versor) { - for (ModelVolume *v : this->volumes) - { + for (ModelVolume *v : this->volumes) { v->scale_geometry_after_creation(versor); v->set_offset(versor.cwiseProduct(v->get_offset())); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 868639ee8..3e2c9a3c5 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -308,7 +308,11 @@ public: void center_around_origin(bool include_modifiers = true); +#if ENABLE_ALLOW_NEGATIVE_Z + void ensure_on_bed(bool allow_negative_z = false); +#else void ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z void translate_instances(const Vec3d& vector); void translate_instance(size_t instance_idx, const Vec3d& vector); void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); } diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 725a396c3..d079a6ebd 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -142,7 +142,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(editor); - auto temp = new wxStaticText(m_parent, wxID_ANY, _(L("mm"))); + auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm")); temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->SetFont(wxGetApp().normal_font()); sizer->Add(temp, 0, wxLEFT, wxGetApp().em_unit()); @@ -154,15 +154,14 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus void ObjectLayers::create_layers_list() { - for (const auto &layer : m_object->layer_config_ranges) - { + for (const auto &layer : m_object->layer_config_ranges) { const t_layer_height_range& range = layer.first; auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range); - del_btn->SetToolTip(_(L("Remove layer range"))); + del_btn->SetToolTip(_L("Remove layer range")); auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range); wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range); - add_btn->SetToolTip(tooltip.IsEmpty() ? _(L("Add layer range")) : tooltip); + add_btn->SetToolTip(tooltip.IsEmpty() ? _L("Add layer range") : tooltip); add_btn->Enable(tooltip.IsEmpty()); auto sizer = create_layer(range, del_btn, add_btn); @@ -242,11 +241,9 @@ void ObjectLayers::msw_rescale() // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { + for (int i = 0; i < cells_cnt; ++i) { const wxSizerItem* item = m_grid_sizer->GetItem(i); - if (item->IsWindow()) - { + if (item->IsWindow()) { LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); if (editor != nullptr) editor->msw_rescale(); @@ -283,8 +280,7 @@ void ObjectLayers::sys_color_changed() // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { + for (int i = 0; i < cells_cnt; ++i) { const wxSizerItem* item = m_grid_sizer->GetItem(i); if (item->IsSizer()) {// case when we have editor with buttons const std::vector btns = {2, 3}; // del_btn, add_btn @@ -405,11 +401,9 @@ coordf_t LayerRangeEditor::get_value() str.Replace(",", ".", false); if (str == ".") layer_height = 0.0; - else - { - if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) - { - show_error(m_parent, _(L("Invalid numeric input."))); + else { + if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) { + show_error(m_parent, _L("Invalid numeric input.")); SetValue(double_to_string(layer_height)); } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 15c4578d8..b5c2bb221 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2113,18 +2113,15 @@ void ObjectList::part_selection_changed() const auto item = GetSelection(); - if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) - { - og_name = _(L("Group manipulation")); + if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { + og_name = _L("Group manipulation"); const Selection& selection = scene_selection(); // don't show manipulation panel for case of all Object's parts selection update_and_show_manipulations = !selection.is_single_full_instance(); } - else - { - if (item) - { + else { + if (item) { const ItemType type = m_objects_model->GetItemType(item); const wxDataViewItem parent = m_objects_model->GetParent(item); const ItemType parent_type = m_objects_model->GetItemType(parent); @@ -2132,7 +2129,7 @@ void ObjectList::part_selection_changed() if (parent == wxDataViewItem(nullptr) || type == itInfo) { - og_name = _(L("Object manipulation")); + og_name = _L("Object manipulation"); m_config = &(*m_objects)[obj_idx]->config; update_and_show_manipulations = true; @@ -2152,35 +2149,35 @@ void ObjectList::part_selection_changed() else { if (type & itSettings) { if (parent_type & itObject) { - og_name = _(L("Object Settings to modify")); + og_name = _L("Object Settings to modify"); m_config = &(*m_objects)[obj_idx]->config; } else if (parent_type & itVolume) { - og_name = _(L("Part Settings to modify")); + og_name = _L("Part Settings to modify"); volume_id = m_objects_model->GetVolumeIdByItem(parent); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; } else if (parent_type & itLayer) { - og_name = _(L("Layer range Settings to modify")); + og_name = _L("Layer range Settings to modify"); m_config = &get_item_config(parent); } update_and_show_settings = true; } else if (type & itVolume) { - og_name = _(L("Part manipulation")); + og_name = _L("Part manipulation"); volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; } else if (type & itInstance) { - og_name = _(L("Instance manipulation")); + og_name = _L("Instance manipulation"); update_and_show_manipulations = true; // fill m_config by object's values m_config = &(*m_objects)[obj_idx]->config; } else if (type & (itLayerRoot|itLayer)) { - og_name = type & itLayerRoot ? _(L("Height ranges")) : _(L("Settings for height range")); + og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range"); update_and_show_layers = true; if (type & itLayer) @@ -2815,7 +2812,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay const int obj_idx = m_selected_object_id; if (obj_idx < 0) return false; - take_snapshot(_(L("Edit Height Range"))); + take_snapshot(_L("Edit Height Range")); const ItemType sel_type = m_objects_model->GetItemType(GetSelection()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5db983f43..210753705 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5805,7 +5805,11 @@ void Plater::changed_object(int obj_idx) return; // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; +#if ENABLE_ALLOW_NEGATIVE_Z + model_object->ensure_on_bed(true); +#else model_object->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data, update the 3D scene. @@ -5823,8 +5827,7 @@ void Plater::changed_objects(const std::vector& object_idxs) if (object_idxs.empty()) return; - for (size_t obj_idx : object_idxs) - { + for (size_t obj_idx : object_idxs) { if (obj_idx < p->model.objects.size()) // recenter and re - align to Z = 0 p->model.objects[obj_idx]->ensure_on_bed(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2da521d47..f52e2f0aa 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -672,24 +672,20 @@ void Selection::translate(const Vec3d& displacement, bool local) EMode translation_type = m_mode; - for (unsigned int i : m_list) - { - if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) + for (unsigned int i : m_list) { + if (m_mode == Volume || (*m_volumes)[i]->is_wipe_tower) { if (local) (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); - else - { + else { Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); } } - else if (m_mode == Instance) - { + else if (m_mode == Instance) { if (is_from_fully_selected_instance(i)) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); - else - { + else { Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); translation_type = Volume; @@ -2153,10 +2149,8 @@ void Selection::ensure_on_bed() typedef std::map, double> InstancesToZMap; InstancesToZMap instances_min_z; - for (GLVolume* volume : *m_volumes) - { - if (!volume->is_wipe_tower && !volume->is_modifier) - { + for (GLVolume* volume : *m_volumes) { + if (!volume->is_wipe_tower && !volume->is_modifier) { double min_z = volume->transformed_convex_hull_bounding_box().min(2); std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); InstancesToZMap::iterator it = instances_min_z.find(instance); @@ -2167,8 +2161,7 @@ void Selection::ensure_on_bed() } } - for (GLVolume* volume : *m_volumes) - { + for (GLVolume* volume : *m_volumes) { std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); InstancesToZMap::iterator it = instances_min_z.find(instance); if (it != instances_min_z.end()) From a3f03ac188543ccedd61915a1b6d7441357fc4be Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 28 Apr 2021 16:02:51 +0200 Subject: [PATCH 18/79] Tech ENABLE_ALLOW_NEGATIVE_Z->Keep as sinking objects saved in project files --- src/slic3r/GUI/Plater.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 210753705..690ffdb16 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1555,7 +1555,11 @@ struct Plater::priv BoundingBox scaled_bed_shape_bb() const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config, bool used_inches = false); +#if ENABLE_ALLOW_NEGATIVE_Z + std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false); +#else std::vector load_model_objects(const ModelObjectPtrs &model_objects); +#endif // ENABLE_ALLOW_NEGATIVE_Z wxString get_export_file(GUI::FileType file_type); const Selection& get_selection() const; @@ -2303,11 +2307,19 @@ std::vector Plater::priv::load_files(const std::vector& input_ return obj_idxs; } +#if ENABLE_ALLOW_NEGATIVE_Z + for (ModelObject* model_object : model.objects) { + if (!type_3mf && !type_zip_amf) + model_object->center_around_origin(false); + model_object->ensure_on_bed(is_project_file); + } +#else for (ModelObject* model_object : model.objects) { if (!type_3mf && !type_zip_amf) model_object->center_around_origin(false); model_object->ensure_on_bed(); } +#endif // ENABLE_ALLOW_NEGATIVE_Z // check multi-part object adding for the SLA-printing if (printer_technology == ptSLA) { @@ -2321,7 +2333,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (one_by_one) { +#if ENABLE_ALLOW_NEGATIVE_Z + auto loaded_idxs = load_model_objects(model.objects, is_project_file); +#else auto loaded_idxs = load_model_objects(model.objects); +#endif // ENABLE_ALLOW_NEGATIVE_Z obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } else { // This must be an .stl or .obj file, which may contain a maximum of one volume. @@ -2373,7 +2389,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ // #define AUTOPLACEMENT_ON_LOAD +#if ENABLE_ALLOW_NEGATIVE_Z +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z) +#else std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) +#endif // ENABLE_ALLOW_NEGATIVE_Z { const BoundingBoxf bed_shape = bed_shape_bb(); const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); @@ -2450,7 +2470,11 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } #endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG +#if ENABLE_ALLOW_NEGATIVE_Z + object->ensure_on_bed(allow_negative_z); +#else object->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } #ifdef AUTOPLACEMENT_ON_LOAD From 628af89c4a8ba0c9bb85f143a6628d5506fe4d0a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 28 Apr 2021 16:04:24 +0200 Subject: [PATCH 19/79] Small refactoring into variable_layer_height.fs shader --- resources/shaders/variable_layer_height.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/variable_layer_height.fs b/resources/shaders/variable_layer_height.fs index f87e6627e..934eb7758 100644 --- a/resources/shaders/variable_layer_height.fs +++ b/resources/shaders/variable_layer_height.fs @@ -36,5 +36,5 @@ void main() texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); // Mix the final color. - gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); + gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); } From 1c3090b11f21cde2cf6f5829ce902cdc928fe5ea Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 May 2021 14:28:55 +0200 Subject: [PATCH 20/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Layers height editing related fixes --- resources/shaders/variable_layer_height.fs | 9 +++--- src/libslic3r/PrintObject.cpp | 16 +++++++++++ src/slic3r/GUI/GLCanvas3D.cpp | 33 ++++++++++++++++------ src/slic3r/GUI/Plater.cpp | 16 ++++++----- src/slic3r/GUI/Selection.cpp | 14 ++++----- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/resources/shaders/variable_layer_height.fs b/resources/shaders/variable_layer_height.fs index 934eb7758..693c1c6a0 100644 --- a/resources/shaders/variable_layer_height.fs +++ b/resources/shaders/variable_layer_height.fs @@ -24,7 +24,7 @@ void main() float z_texture_col = object_z_row - z_texture_row; float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; // Calculate level of detail from the object Z coordinate. - // This makes the slowly sloping surfaces to be show with high detail (with stripes), + // This makes the slowly sloping surfaces to be shown with high detail (with stripes), // and the vertical surfaces to be shown with low detail (no stripes) float z_in_cells = object_z_row * 190.; // Gradient of Z projected on the screen. @@ -32,9 +32,10 @@ void main() float dy_vtc = dFdy(z_in_cells); float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.); // Sample the Z texture. Texture coordinates are normalized to <0, 1>. - vec4 color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), - texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); - + vec4 color = vec4(0.25, 0.25, 0.25, 1.0); + if (z_texture_row >= 0.0) + color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), + texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); // Mix the final color. gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cbf3e71ab..ab39fbbde 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1639,9 +1639,15 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion void PrintObject::update_slicing_parameters() { +#if ENABLE_ALLOW_NEGATIVE_Z + if (!m_slicing_params.valid) + m_slicing_params = SlicingParameters::create_from_config( + this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders()); +#else if (! m_slicing_params.valid) m_slicing_params = SlicingParameters::create_from_config( this->print()->config(), m_config, unscale(this->height()), this->object_extruders()); +#endif // ENABLE_ALLOW_NEGATIVE_Z } SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) @@ -1703,6 +1709,15 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c updated = true; } +#if ENABLE_ALLOW_NEGATIVE_Z + // Verify the layer_height_profile. + if (!layer_height_profile.empty() && + // Must not be of even length. + ((layer_height_profile.size() & 1) != 0 || + // Last entry must be at the top of the object. + std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max) > 1e-3)) + layer_height_profile.clear(); +#else // Verify the layer_height_profile. if (! layer_height_profile.empty() && // Must not be of even length. @@ -1710,6 +1725,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Last entry must be at the top of the object. std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3)) layer_height_profile.clear(); +#endif // ENABLE_ALLOW_NEGATIVE_Z if (layer_height_profile.empty()) { //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fcd6da83f..cc8658fc2 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -169,11 +169,18 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) { const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr; +#if ENABLE_ALLOW_NEGATIVE_Z + // Maximum height of an object changes when the object gets rotated or scaled. + // Changing maximum height of an object will invalidate the layer heigth editing profile. + // m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently. + const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast(model_object_new->bounding_box().max.z()); +#else // Maximum height of an object changes when the object gets rotated or scaled. // Changing maximum height of an object will invalidate the layer heigth editing profile. // m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently. float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z(); - if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || +#endif // ENABLE_ALLOW_NEGATIVE_Z + if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || (model_object_new != nullptr && m_model_object->id() != model_object_new->id())) { m_layer_height_profile.clear(); m_layer_height_profile_modified = false; @@ -348,11 +355,12 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con float h = 0.0f; for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) { - float zi = m_layer_height_profile[i]; - float zi_1 = m_layer_height_profile[i - 2]; + const float zi = static_cast(m_layer_height_profile[i]); + const float zi_1 = static_cast(m_layer_height_profile[i - 2]); if (zi_1 <= z && z <= zi) { float dz = zi - zi_1; - h = (dz != 0.0f) ? lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz) : m_layer_height_profile[i + 1]; + h = (dz != 0.0f) ? static_cast(lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz)) : + static_cast(m_layer_height_profile[i + 1]); break; } } @@ -381,10 +389,10 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); // Render the color bar - float l = bar_rect.get_left(); - float r = bar_rect.get_right(); - float t = bar_rect.get_top(); - float b = bar_rect.get_bottom(); + const float l = bar_rect.get_left(); + const float r = bar_rect.get_right(); + const float t = bar_rect.get_top(); + const float b = bar_rect.get_bottom(); ::glBegin(GL_QUADS); ::glNormal3f(0.0f, 0.0f, 1.0f); @@ -560,7 +568,7 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters() { if (m_slicing_parameters == nullptr) { m_slicing_parameters = new SlicingParameters(); - *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); } } @@ -3575,6 +3583,13 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) #endif // ENABLE_ALLOW_NEGATIVE_Z } +#if ENABLE_ALLOW_NEGATIVE_Z + // if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running + // similar to void Plater::priv::selection_changed() + if (!wxGetApp().plater()->can_layers_editing() && is_layers_editing_enabled()) + post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); +#endif // ENABLE_ALLOW_NEGATIVE_Z + if (object_moved) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 690ffdb16..05090399d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2630,11 +2630,8 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running - bool enable_layer_editing = layers_height_allowed(); - if (!enable_layer_editing && view3D->is_layers_editing_enabled()) { - SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING); - on_action_layersediting(evt); - } + if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) + on_action_layersediting(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); @@ -4024,7 +4021,7 @@ void Plater::priv::reset_gcode_toolpaths() bool Plater::priv::can_set_instance_to_object() const { const int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->instances.size() > 1; } bool Plater::priv::can_split(bool to_objects) const @@ -4038,7 +4035,12 @@ bool Plater::priv::layers_height_allowed() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#if ENABLE_ALLOW_NEGATIVE_Z + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > 0.0 && + config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#else + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } bool Plater::priv::can_mirror() const diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index f52e2f0aa..3ab546abe 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -169,7 +169,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec if (!already_contained || needs_reset) { - wxGetApp().plater()->take_snapshot(_(L("Selection-Add"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Add")); if (needs_reset) clear(); @@ -210,7 +210,7 @@ void Selection::remove(unsigned int volume_idx) if (!contains_volume(volume_idx)) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove")); GLVolume* volume = (*m_volumes)[volume_idx]; @@ -242,7 +242,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection) (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Add Object"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Object")); // resets the current list if needed if (as_single_selection) @@ -261,7 +261,7 @@ void Selection::remove_object(unsigned int object_idx) if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Object"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object")); do_remove_object(object_idx); @@ -274,12 +274,12 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, if (!m_valid) return; - std::vector volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx); + const std::vector volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx); if ((!as_single_selection && contains_all_volumes(volume_idxs)) || (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Add Instance"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance")); // resets the current list if needed if (as_single_selection) @@ -298,7 +298,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Instance"))); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance")); do_remove_instance(object_idx, instance_idx); From 57199b41167f575894d99e733c4d286fcde7c727 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 May 2021 15:47:16 +0200 Subject: [PATCH 21/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Fixed build on non-Windows OS --- src/slic3r/GUI/Plater.cpp | 87 ++++++++++++++------------------------- 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 05090399d..d78b36924 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2630,8 +2630,10 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running - if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) - on_action_layersediting(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); + if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) { + SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING); + on_action_layersediting(evt); + } // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); @@ -3069,21 +3071,19 @@ void Plater::priv::reload_from_disk() int volume_idx; // operators needed by std::algorithms - bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); } - bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); } + bool operator < (const SelectedVolume& other) const { return object_idx < other.object_idx || (object_idx == other.object_idx && volume_idx < other.volume_idx); } + bool operator == (const SelectedVolume& other) const { return object_idx == other.object_idx && volume_idx == other.volume_idx; } }; std::vector selected_volumes; // collects selected ModelVolumes const std::set& selected_volumes_idxs = selection.get_volume_idxs(); - for (unsigned int idx : selected_volumes_idxs) - { + for (unsigned int idx : selected_volumes_idxs) { const GLVolume* v = selection.get_volume(idx); int v_idx = v->volume_idx(); - if (v_idx >= 0) - { + if (v_idx >= 0) { int o_idx = v->object_idx(); - if ((0 <= o_idx) && (o_idx < (int)model.objects.size())) + if (0 <= o_idx && o_idx < (int)model.objects.size()) selected_volumes.push_back({ o_idx, v_idx }); } } @@ -3093,13 +3093,11 @@ void Plater::priv::reload_from_disk() // collects paths of files to load std::vector input_paths; std::vector missing_input_paths; - for (const SelectedVolume& v : selected_volumes) - { + for (const SelectedVolume& v : selected_volumes) { const ModelObject* object = model.objects[v.object_idx]; const ModelVolume* volume = object->volumes[v.volume_idx]; - if (!volume->source.input_file.empty()) - { + if (!volume->source.input_file.empty()) { if (fs::exists(volume->source.input_file)) input_paths.push_back(volume->source.input_file); else @@ -3112,8 +3110,7 @@ void Plater::priv::reload_from_disk() std::sort(missing_input_paths.begin(), missing_input_paths.end()); missing_input_paths.erase(std::unique(missing_input_paths.begin(), missing_input_paths.end()), missing_input_paths.end()); - while (!missing_input_paths.empty()) - { + while (!missing_input_paths.empty()) { // ask user to select the missing file fs::path search = missing_input_paths.back(); wxString title = _L("Please select the file to reload"); @@ -3127,21 +3124,18 @@ void Plater::priv::reload_from_disk() std::string sel_filename_path = dialog.GetPath().ToUTF8().data(); std::string sel_filename = fs::path(sel_filename_path).filename().string(); - if (boost::algorithm::iequals(search.filename().string(), sel_filename)) - { + if (boost::algorithm::iequals(search.filename().string(), sel_filename)) { input_paths.push_back(sel_filename_path); missing_input_paths.pop_back(); fs::path sel_path = fs::path(sel_filename_path).remove_filename().string(); std::vector::iterator it = missing_input_paths.begin(); - while (it != missing_input_paths.end()) - { + while (it != missing_input_paths.end()) { // try to use the path of the selected file with all remaining missing files fs::path repathed_filename = sel_path; repathed_filename /= it->filename(); - if (fs::exists(repathed_filename)) - { + if (fs::exists(repathed_filename)) { input_paths.push_back(repathed_filename.string()); it = missing_input_paths.erase(it); } @@ -3149,8 +3143,7 @@ void Plater::priv::reload_from_disk() ++it; } } - else - { + else { wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?"; wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION); if (dlg.ShowModal() != wxID_YES) @@ -3164,8 +3157,7 @@ void Plater::priv::reload_from_disk() std::vector fail_list; // load one file at a time - for (size_t i = 0; i < input_paths.size(); ++i) - { + for (size_t i = 0; i < input_paths.size(); ++i) { const auto& path = input_paths[i].string(); wxBusyCursor wait; @@ -3175,8 +3167,7 @@ void Plater::priv::reload_from_disk() try { new_model = Model::read_from_file(path, nullptr, true, false); - for (ModelObject* model_object : new_model.objects) - { + for (ModelObject* model_object : new_model.objects) { model_object->center_around_origin(); model_object->ensure_on_bed(); } @@ -3188,34 +3179,27 @@ void Plater::priv::reload_from_disk() } // update the selected volumes whose source is the current file - for (const SelectedVolume& sel_v : selected_volumes) - { + for (const SelectedVolume& sel_v : selected_volumes) { ModelObject* old_model_object = model.objects[sel_v.object_idx]; ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx]; bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string()); bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string()); - if (has_source || has_name) - { + if (has_source || has_name) { int new_volume_idx = -1; int new_object_idx = -1; - if (has_source) - { + if (has_source) { // take idxs from source new_volume_idx = old_volume->source.volume_idx; new_object_idx = old_volume->source.object_idx; } - else - { + else { // take idxs from the 1st matching volume - for (size_t o = 0; o < new_model.objects.size(); ++o) - { + for (size_t o = 0; o < new_model.objects.size(); ++o) { ModelObject* obj = new_model.objects[o]; bool found = false; - for (size_t v = 0; v < obj->volumes.size(); ++v) - { - if (obj->volumes[v]->name == old_volume->name) - { + for (size_t v = 0; v < obj->volumes.size(); ++v) { + if (obj->volumes[v]->name == old_volume->name) { new_volume_idx = (int)v; new_object_idx = (int)o; found = true; @@ -3227,19 +3211,16 @@ void Plater::priv::reload_from_disk() } } - if ((new_object_idx < 0) && ((int)new_model.objects.size() <= new_object_idx)) - { + if (new_object_idx < 0 && (int)new_model.objects.size() <= new_object_idx) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } ModelObject* new_model_object = new_model.objects[new_object_idx]; - if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx)) - { + if (new_volume_idx < 0 && (int)new_model.objects.size() <= new_volume_idx) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } - if (new_volume_idx < (int)new_model_object->volumes.size()) - { + if (new_volume_idx < (int)new_model_object->volumes.size()) { old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]); ModelVolume* new_volume = old_model_object->volumes.back(); new_volume->set_new_unique_id(); @@ -3264,11 +3245,9 @@ void Plater::priv::reload_from_disk() } } - if (!fail_list.empty()) - { + if (!fail_list.empty()) { wxString message = _L("Unable to reload:") + "\n"; - for (const wxString& s : fail_list) - { + for (const wxString& s : fail_list) { message += s + "\n"; } wxMessageDialog dlg(q, message, _L("Error during reload"), wxOK | wxOK_DEFAULT | wxICON_WARNING); @@ -3279,8 +3258,7 @@ void Plater::priv::reload_from_disk() update(); // new GLVolumes have been created at this point, so update their printable state - for (size_t i = 0; i < model.objects.size(); ++i) - { + for (size_t i = 0; i < model.objects.size(); ++i) { view3D->get_canvas3d()->update_instance_printable_state_for_object(i); } } @@ -3300,8 +3278,7 @@ void Plater::priv::reload_all_from_disk() reload_from_disk(); // restore previous selection selection.clear(); - for (unsigned int idx : curr_idxs) - { + for (unsigned int idx : curr_idxs) { selection.add(idx, false); } } From 164af0255af7aee1c3c6495b7f0c2d2dde9c18e7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 May 2021 16:02:06 +0200 Subject: [PATCH 22/79] Tech ENABLE_ALLOW_NEGATIVE_Z -> Keep sinking objects as sinking after reload from disk --- src/slic3r/GUI/Plater.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d78b36924..cce2dd319 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3183,6 +3183,10 @@ void Plater::priv::reload_from_disk() ModelObject* old_model_object = model.objects[sel_v.object_idx]; ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx]; +#if ENABLE_ALLOW_NEGATIVE_Z + bool sinking = old_model_object->bounding_box().min.z() < 0.0; +#endif // ENABLE_ALLOW_NEGATIVE_Z + bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string()); bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string()); if (has_source || has_name) { @@ -3237,7 +3241,10 @@ void Plater::priv::reload_from_disk() new_volume->seam_facets.assign(old_volume->seam_facets); std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); - old_model_object->ensure_on_bed(); +#if ENABLE_ALLOW_NEGATIVE_Z + if (!sinking) +#endif // ENABLE_ALLOW_NEGATIVE_Z + old_model_object->ensure_on_bed(); sla::reproject_points_and_holes(old_model_object); } From 67572fad3f957bd5e40f3575e5e7f8fa3aebc66a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 May 2021 14:48:30 +0200 Subject: [PATCH 23/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Keep sinking objects and instances as sinking after copy/paste or add instance commands --- src/slic3r/GUI/Plater.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cce2dd319..32ecef438 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5838,9 +5838,17 @@ void Plater::changed_objects(const std::vector& object_idxs) return; for (size_t obj_idx : object_idxs) { +#if ENABLE_ALLOW_NEGATIVE_Z + if (obj_idx < p->model.objects.size()) { + if (p->model.objects[obj_idx]->bounding_box().min.z() >= 0.0) + // re - align to Z = 0 + p->model.objects[obj_idx]->ensure_on_bed(); + } +#else if (obj_idx < p->model.objects.size()) // recenter and re - align to Z = 0 p->model.objects[obj_idx]->ensure_on_bed(); +#endif // ENABLE_ALLOW_NEGATIVE_Z } if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() From 3f6123e653fea186523c6e895bc77f47dca66efa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 5 May 2021 09:15:33 +0200 Subject: [PATCH 24/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Added button in object manipulator to drop to bed a sinking object --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 37 ++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index cba7cef36..095a926ad 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -26,21 +26,26 @@ const double ObjectManipulation::mm_to_in = 0.0393700787; // Helper function to be used by drop to bed button. Returns lowest point of this // volume in world coordinate system. -static double get_volume_min_z(const GLVolume* volume) +static double get_volume_min_z(const GLVolume& volume) { - const Transform3f& world_matrix = volume->world_matrix().cast(); +#if ENABLE_ALLOW_NEGATIVE_Z + return volume.transformed_convex_hull_bounding_box().min.z(); +#else + const Transform3f& world_matrix = volume.world_matrix().cast(); // need to get the ModelVolume pointer - const ModelObject* mo = wxGetApp().model().objects[volume->composite_id.object_id]; - const ModelVolume* mv = mo->volumes[volume->composite_id.volume_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(); float min_z = std::numeric_limits::max(); for (const stl_facet& facet : hull.stl.facet_start) { - for (int i = 0; i < 3; ++ i) + for (int i = 0; i < 3; ++i) min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i])); } + return min_z; +#endif // ENABLE_ALLOW_NEGATIVE_Z } @@ -341,13 +346,27 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); - Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); + const Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(*volume)); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); change_position_value(0, diff.x()); change_position_value(1, diff.y()); change_position_value(2, diff.z()); } +#if ENABLE_ALLOW_NEGATIVE_Z + else if (selection.is_single_full_instance()) { + const ModelObjectPtrs& objects = wxGetApp().model().objects; + const int idx = selection.get_object_idx(); + if (0 <= idx && idx < static_cast(objects.size())) { + const ModelObject* mo = wxGetApp().model().objects[idx]; + const double min_z = mo->bounding_box().min.z(); + if (std::abs(min_z) > EPSILON) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); + change_position_value(2, m_cache.position.z() - min_z); + } + } + } +#endif // ENABLE_ALLOW_NEGATIVE_Z }); editors_grid_sizer->Add(m_drop_to_bed_button); @@ -671,11 +690,15 @@ void ObjectManipulation::update_reset_buttons_visibility() if (selection.is_single_full_instance()) { rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); +#if ENABLE_ALLOW_NEGATIVE_Z + min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z(); +#endif // ENABLE_ALLOW_NEGATIVE_Z + } else { rotation = volume->get_volume_rotation(); scale = volume->get_volume_scaling_factor(); - min_z = get_volume_min_z(volume); + min_z = get_volume_min_z(*volume); } show_rotation = !rotation.isApprox(Vec3d::Zero()); show_scale = !scale.isApprox(Vec3d::Ones()); From bb18edde0ab384276d1ee94219cd76b6d8f4310c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 5 May 2021 11:53:24 +0200 Subject: [PATCH 25/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Synchronize sinking instances --- src/slic3r/GUI/Selection.cpp | 178 +++++++++++++++-------------------- src/slic3r/GUI/Selection.hpp | 2 +- 2 files changed, 79 insertions(+), 101 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3ab546abe..8d7878650 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -58,13 +58,11 @@ bool Selection::Clipboard::is_sla_compliant() const if (m_mode == Selection::Volume) return false; - for (const ModelObject* o : m_model->objects) - { + for (const ModelObject* o : m_model->objects) { if (o->is_multiparts()) return false; - for (const ModelVolume* v : o->volumes) - { + for (const ModelVolume* v : o->volumes) { if (v->is_modifier()) return false; } @@ -78,7 +76,8 @@ Selection::Clipboard::Clipboard() m_model.reset(new Model); } -void Selection::Clipboard::reset() { +void Selection::Clipboard::reset() +{ m_model->clear_objects(); } @@ -149,7 +148,7 @@ void Selection::set_model(Model* model) void Selection::add(unsigned int volume_idx, bool as_single_selection, bool check_for_already_contained) { - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx) return; const GLVolume* volume = (*m_volumes)[volume_idx]; @@ -167,8 +166,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec needs_reset |= as_single_selection && !is_any_modifier() && volume->is_modifier; needs_reset |= is_any_modifier() && !volume->is_modifier; - if (!already_contained || needs_reset) - { + if (!already_contained || needs_reset) { wxGetApp().plater()->take_snapshot(_L("Selection-Add")); if (needs_reset) @@ -185,7 +183,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec { case Volume: { - if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx()))) + if (volume->volume_idx() >= 0 && (is_empty() || volume->instance_idx() == get_instance_idx())) do_add_volume(volume_idx); break; @@ -204,7 +202,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec void Selection::remove(unsigned int volume_idx) { - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx) return; if (!contains_volume(volume_idx)) @@ -333,10 +331,9 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) if (!m_valid) return; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) + if (v->object_idx() == (int)object_idx && v->volume_idx() == (int)volume_idx) do_remove_volume(i); } @@ -358,8 +355,7 @@ void Selection::add_volumes(EMode mode, const std::vector& volume_ clear(); m_mode = mode; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (i < (unsigned int)m_volumes->size()) do_add_volume(i); } @@ -374,8 +370,7 @@ void Selection::remove_volumes(EMode mode, const std::vector& volu return; m_mode = mode; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (i < (unsigned int)m_volumes->size()) do_remove_volume(i); } @@ -390,8 +385,7 @@ void Selection::add_all() return; unsigned int count = 0; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if (!(*m_volumes)[i]->is_wipe_tower) ++count; } @@ -404,8 +398,7 @@ void Selection::add_all() m_mode = Instance; clear(); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if (!(*m_volumes)[i]->is_wipe_tower) do_add_volume(i); } @@ -455,8 +448,7 @@ void Selection::clear() if (m_list.empty()) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { (*m_volumes)[i]->selected = false; } @@ -522,16 +514,15 @@ bool Selection::is_single_full_instance() const return false; int object_idx = m_valid ? get_object_idx() : -1; - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) return false; int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx(); std::set volumes_idxs; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; - if ((object_idx != v->object_idx()) || (instance_idx != v->instance_idx())) + if (object_idx != v->object_idx() || instance_idx != v->instance_idx()) return false; int volume_idx = v->volume_idx(); @@ -544,8 +535,8 @@ bool Selection::is_single_full_instance() const bool Selection::is_from_single_object() const { - int idx = get_object_idx(); - return (0 <= idx) && (idx < 1000); + const int idx = get_object_idx(); + return 0 <= idx && idx < 1000; } bool Selection::is_sla_compliant() const @@ -553,8 +544,7 @@ bool Selection::is_sla_compliant() const if (m_mode == Volume) return false; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if ((*m_volumes)[i]->is_modifier) return false; } @@ -564,8 +554,7 @@ bool Selection::is_sla_compliant() const bool Selection::contains_all_volumes(const std::vector& volume_idxs) const { - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) == m_list.end()) return false; } @@ -575,8 +564,7 @@ bool Selection::contains_all_volumes(const std::vector& volume_idx bool Selection::contains_any_volume(const std::vector& volume_idxs) const { - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) != m_list.end()) return true; } @@ -588,8 +576,7 @@ bool Selection::matches(const std::vector& volume_idxs) const { unsigned int count = 0; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) != m_list.end()) ++count; else @@ -614,8 +601,7 @@ int Selection::get_object_idx() const int Selection::get_instance_idx() const { - if (m_cache.content.size() == 1) - { + if (m_cache.content.size() == 1) { const InstanceIdxsList& idxs = m_cache.content.begin()->second; if (idxs.size() == 1) return *idxs.begin(); @@ -673,12 +659,11 @@ void Selection::translate(const Vec3d& displacement, bool local) EMode translation_type = m_mode; for (unsigned int i : m_list) { - if (m_mode == Volume || (*m_volumes)[i]->is_wipe_tower) - { + if (m_mode == Volume || (*m_volumes)[i]->is_wipe_tower) { if (local) (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); else { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); } } @@ -686,7 +671,7 @@ void Selection::translate(const Vec3d& displacement, bool local) if (is_from_fully_selected_instance(i)) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); else { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); translation_type = Volume; } @@ -714,18 +699,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ if (!is_wipe_tower()) { int rot_axis_max = 0; - if (rotation.isApprox(Vec3d::Zero())) - { - for (unsigned int i : m_list) - { + if (rotation.isApprox(Vec3d::Zero())) { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; - if (m_mode == Instance) - { + if (m_mode == Instance) { volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); } - else if (m_mode == Volume) - { + else if (m_mode == Volume) { volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); } @@ -742,14 +723,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. std::vector object_instance_first(m_model->objects.size(), -1); auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { - int first_volume_idx = object_instance_first[volume.object_idx()]; + const int first_volume_idx = object_instance_first[volume.object_idx()]; if (rot_axis_max != 2 && first_volume_idx != -1) { // Generic rotation, but no rotation around the Z axis. // Always do a local rotation (do not consider the selection to be a rigid body). assert(is_approx(rotation.z(), 0.0)); const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; const Vec3d &rotation = first_volume.get_instance_rotation(); - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); } else { @@ -759,7 +740,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); if (rot_axis_max == 2 && transformation_type.joint()) { // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); } volume.set_instance_rotation(new_rotation); @@ -767,19 +748,16 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ } }; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { GLVolume &volume = *(*m_volumes)[i]; if (is_single_full_instance()) rotate_instance(volume, i); - else if (is_single_volume() || is_single_modifier()) - { + else if (is_single_volume() || is_single_modifier()) { if (transformation_type.independent()) volume.set_volume_rotation(volume.get_volume_rotation() + rotation); - else - { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + else { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); volume.set_volume_rotation(new_rotation); } } @@ -787,15 +765,13 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ { if (m_mode == Instance) rotate_instance(volume, i); - else if (m_mode == Volume) - { + else if (m_mode == Volume) { // extracts rotations from the composed transformation Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - if (transformation_type.joint()) - { - Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); + if (transformation_type.joint()) { + const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; + const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); volume.set_volume_offset(local_pivot + offset); } volume.set_volume_rotation(new_rotation); @@ -816,8 +792,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // make sure the wipe tower rotates around its center, not origin // we can assume that only Z rotation changes - Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); - Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0, 0, 1)) * center_local; + const Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); + const Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0.0, 0.0, 1.0)) * center_local; volume.set_volume_rotation(rotation); volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); } @@ -835,8 +811,7 @@ void Selection::flattening_rotate(const Vec3d& normal) if (!m_valid) return; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { // Normal transformed from the object coordinate space to the world coordinate space. const auto &voldata = m_cache.volumes_data[i]; Vec3d tnormal = (Geometry::assemble_transform( @@ -1781,18 +1756,16 @@ void Selection::render_synchronized_volumes() const float color[3] = { 1.0f, 1.0f, 0.0f }; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* volume = (*m_volumes)[i]; int object_idx = volume->object_idx(); int volume_idx = volume->volume_idx(); - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (i == j) continue; const GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + if (v->object_idx() != object_idx || v->volume_idx() != volume_idx) continue; render_bounding_box(v->transformed_convex_hull_bounding_box(), color); @@ -1986,7 +1959,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); ::glBegin(GL_QUADS); - if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2))) + if ((camera_on_top && type == 1) || (!camera_on_top && type == 2)) ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); @@ -1997,7 +1970,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co glsafe(::glEnd()); ::glBegin(GL_QUADS); - if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1))) + if ((camera_on_top && type == 2) || (!camera_on_top && type == 1)) ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); @@ -2014,9 +1987,9 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co #ifndef NDEBUG static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { - Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); + const Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + const Vec3d axis = angle_axis.axis(); + const double angle = angle_axis.angle(); if (std::abs(angle) < 1e-8) return true; assert(std::abs(axis.x()) < 1e-8); @@ -2053,24 +2026,22 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ std::set done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if (done.size() == m_volumes->size()) break; const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); + const int object_idx = volume->object_idx(); if (object_idx >= 1000) continue; - int instance_idx = volume->instance_idx(); + const int instance_idx = volume->instance_idx(); const Vec3d& rotation = volume->get_instance_rotation(); const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); const Vec3d& mirror = volume->get_instance_mirror(); // Process unselected instances. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (done.size() == m_volumes->size()) break; @@ -2078,24 +2049,33 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) + if (v->object_idx() != object_idx || v->instance_idx() == instance_idx) continue; assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation())); switch (sync_rotation_type) { - case SYNC_ROTATION_NONE: + case SYNC_ROTATION_NONE: { +#if ENABLE_ALLOW_NEGATIVE_Z + // z only rotation -> synch instance z + // The X,Y rotations should be synchronized from start to end of the rotation. + assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); + v->set_instance_offset(Z, volume->get_instance_offset().z()); + break; +#else // z only rotation -> keep instance z // The X,Y rotations should be synchronized from start to end of the rotation. assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); break; +#endif // ENABLE_ALLOW_NEGATIVE_Z + } case SYNC_ROTATION_FULL: // rotation comes from place on face -> force given z - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2))); + v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() }); break; case SYNC_ROTATION_GENERAL: // generic rotation -> update instance z with the delta of the rotation. - double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); + v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() + z_diff }); break; } @@ -2113,27 +2093,25 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ void Selection::synchronize_unselected_volumes() { - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); + const int object_idx = volume->object_idx(); if (object_idx >= 1000) continue; - int volume_idx = volume->volume_idx(); + const int volume_idx = volume->volume_idx(); const Vec3d& offset = volume->get_volume_offset(); const Vec3d& rotation = volume->get_volume_rotation(); const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); const Vec3d& mirror = volume->get_volume_mirror(); // Process unselected volumes. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (j == i) continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + if (v->object_idx() != object_idx || v->volume_idx() != volume_idx) continue; v->set_volume_offset(offset); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8bb418baa..c28a6e867 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -129,7 +129,7 @@ private: TransformCache m_instance; public: - VolumeCache() {} + VolumeCache() = default; VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); const Vec3d& get_volume_position() const { return m_volume.position; } From a91306032ca5d3a6e04d11d18bb24f5b2b39d21d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 5 May 2021 13:17:20 +0200 Subject: [PATCH 26/79] Project dirty state manager -> Fixed crash when loading/saving a 3mf file --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index efb7ab486..1d4c63fb6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -67,7 +67,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 95cfa042e..30d41f808 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -210,8 +210,7 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); - assert(saveable_snapshot != nullptr); - m_last_save.main = saveable_snapshot->timestamp; + m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0; } else { const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); From 72ce8cb28d2fdf23f2a937b3a6d88ba1f7e8a0b7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 14:17:36 +0200 Subject: [PATCH 27/79] PrintRegion refactoring: Getting rid of the Print pointer. --- src/libslic3r/LayerRegion.cpp | 5 +++-- src/libslic3r/Print.cpp | 6 +++--- src/libslic3r/Print.hpp | 15 ++++++--------- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/PrintRegion.cpp | 15 ++++++++------- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index b36daf421..a3b0890d7 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -29,11 +29,12 @@ Flow LayerRegion::bridging_flow(FlowRole role) const { const PrintRegion ®ion = *this->region(); const PrintRegionConfig ®ion_config = region.config(); - if (this->layer()->object()->config().thick_bridges) { + const PrintObject &print_object = *this->layer()->object(); + if (print_object.config().thick_bridges) { // The old Slic3r way (different from all other slicers): Use rounded extrusions. // Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); + auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); // Applies default bridge spacing. return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter); } else { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 7fcb75297..e02bc65dd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -47,13 +47,13 @@ void Print::clear() PrintRegion* Print::add_region() { - m_regions.emplace_back(new PrintRegion(this)); + m_regions.emplace_back(new PrintRegion()); return m_regions.back(); } PrintRegion* Print::add_region(const PrintRegionConfig &config) { - m_regions.emplace_back(new PrintRegion(this, config)); + m_regions.emplace_back(new PrintRegion(config)); return m_regions.back(); } @@ -281,7 +281,7 @@ std::vector Print::object_extruders() const region_used[&volumes_per_region - &object->region_volumes.front()] = true; for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region) if (region_used[idx_region]) - m_regions[idx_region]->collect_object_printing_extruders(extruders); + m_regions[idx_region]->collect_object_printing_extruders(*this, extruders); sort_remove_duplicates(extruders); return extruders; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 91f86d010..ff4ae68f2 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -57,11 +57,13 @@ enum PrintObjectStep { // sharing the same config (including the same assigned extruder(s)) class PrintRegion { - friend class Print; +public: + PrintRegion() : m_refcnt(0) {} + PrintRegion(const PrintRegionConfig &config) : m_refcnt(0), m_config(config) {} + ~PrintRegion() = default; // Methods NOT modifying the PrintRegion's state: public: - const Print* print() const { return m_print; } const PrintRegionConfig& config() const { return m_config; } // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; @@ -72,27 +74,22 @@ public: coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Collect 0-based extruder indices used to print this region's object. - void collect_object_printing_extruders(std::vector &object_extruders) const; + void collect_object_printing_extruders(const Print &print, std::vector &object_extruders) const; static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, const bool has_brim, std::vector &object_extruders); // Methods modifying the PrintRegion's state: public: - Print* print() { return m_print; } void set_config(const PrintRegionConfig &config) { m_config = config; } void set_config(PrintRegionConfig &&config) { m_config = std::move(config); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } protected: + friend Print; size_t m_refcnt; private: - Print *m_print; PrintRegionConfig m_config; - - PrintRegion(Print* print) : m_refcnt(0), m_print(print) {} - PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {} - ~PrintRegion() = default; }; template diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0e9acbfdf..10eaf7c1f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1675,7 +1675,7 @@ std::vector PrintObject::object_extruders() const extruders.reserve(this->region_volumes.size() * 3); for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) if (! this->region_volumes[idx_region].empty()) - m_print->get_region(idx_region)->collect_object_printing_extruders(extruders); + m_print->get_region(idx_region)->collect_object_printing_extruders(*this->print(), extruders); sort_remove_duplicates(extruders); return extruders; } diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 837200984..5dba1316e 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -20,11 +20,12 @@ unsigned int PrintRegion::extruder(FlowRole role) const Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const { - ConfigOptionFloatOrPercent config_width; + const PrintConfig &print_config = object.print()->config(); + ConfigOptionFloatOrPercent config_width; // Get extrusion width from configuration. // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { - config_width = m_print->config().first_layer_extrusion_width; + if (first_layer && print_config.first_layer_extrusion_width.value > 0) { + config_width = print_config.first_layer_extrusion_width; } else if (role == frExternalPerimeter) { config_width = m_config.external_perimeter_extrusion_width; } else if (role == frPerimeter) { @@ -44,7 +45,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he // Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); + auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1)); return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); } @@ -76,17 +77,17 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con emplace_extruder(region_config.solid_infill_extruder); } -void PrintRegion::collect_object_printing_extruders(std::vector &object_extruders) const +void PrintRegion::collect_object_printing_extruders(const Print &print, std::vector &object_extruders) const { // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder. // If not, then there must be something wrong with the Print::apply() function. #ifndef NDEBUG - auto num_extruders = (int)print()->config().nozzle_diameter.size(); + auto num_extruders = int(print.config().nozzle_diameter.size()); assert(this->config().perimeter_extruder <= num_extruders); assert(this->config().infill_extruder <= num_extruders); assert(this->config().solid_infill_extruder <= num_extruders); #endif - collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders); + collect_object_printing_extruders(print.config(), this->config(), print.has_brim(), object_extruders); } } From d6c5961eb06af8c300225ad5dd29dc07c16a275e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 14:30:09 +0200 Subject: [PATCH 28/79] Factored out the Print::apply() method and its dependending free functions into PrintApply.cpp module. --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Model.hpp | 1 + src/libslic3r/Print.cpp | 824 ---------------------------------- src/libslic3r/Print.hpp | 9 - src/libslic3r/PrintApply.cpp | 835 +++++++++++++++++++++++++++++++++++ 5 files changed, 837 insertions(+), 833 deletions(-) create mode 100644 src/libslic3r/PrintApply.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 16299f442..0cf67597a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -163,6 +163,7 @@ add_library(libslic3r STATIC AppConfig.hpp Print.cpp Print.hpp + PrintApply.cpp PrintBase.cpp PrintBase.hpp PrintConfig.cpp diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 50797aeeb..db7043769 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -680,6 +680,7 @@ protected: friend class SLAPrint; friend class Model; friend class ModelObject; + friend static void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new); // Copies IDs of both the ModelVolume and its config. explicit ModelVolume(const ModelVolume &rhs) = default; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e02bc65dd..caf827702 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -345,242 +345,6 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } -// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new -// in the exact order and with the same IDs. -// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. -void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new) -{ - typedef std::pair ModelVolumeWithStatus; - std::vector old_volumes; - old_volumes.reserve(model_object_dst.volumes.size()); - for (const ModelVolume *model_volume : model_object_dst.volumes) - old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false)); - auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); }; - auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); }; - std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower); - model_object_dst.volumes.clear(); - model_object_dst.volumes.reserve(model_object_new.volumes.size()); - for (const ModelVolume *model_volume_src : model_object_new.volumes) { - ModelVolumeWithStatus key(model_volume_src, false); - auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower); - if (it != old_volumes.end() && model_volume_equal(*it, key)) { - // The volume was found in the old list. Just copy it. - assert(! it->second); // not consumed yet - it->second = true; - ModelVolume *model_volume_dst = const_cast(it->first); - // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. - assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type()); - model_object_dst.volumes.emplace_back(model_volume_dst); - if (model_volume_dst->is_support_modifier()) { - // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. - model_volume_dst->set_type(model_volume_src->type()); - model_volume_dst->set_transformation(model_volume_src->get_transformation()); - } - assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix())); - } else { - // The volume was not found in the old list. Create a new copy. - assert(model_volume_src->is_support_modifier()); - model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src)); - model_object_dst.volumes.back()->set_model_object(&model_object_dst); - } - } - // Release the non-consumed old volumes (those were deleted from the new list). - for (ModelVolumeWithStatus &mv_with_status : old_volumes) - if (! mv_with_status.second) - delete mv_with_status.first; -} - -static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type) -{ - size_t i_src, i_dst; - for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) { - const ModelVolume &mv_src = *model_object_src.volumes[i_src]; - ModelVolume &mv_dst = *model_object_dst.volumes[i_dst]; - if (mv_src.type() != type) { - ++ i_src; - continue; - } - if (mv_dst.type() != type) { - ++ i_dst; - continue; - } - assert(mv_src.id() == mv_dst.id()); - // Copy the ModelVolume data. - mv_dst.name = mv_src.name; - mv_dst.config.assign_config(mv_src.config); - assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id()); - mv_dst.supported_facets.assign(mv_src.supported_facets); - assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id()); - mv_dst.seam_facets.assign(mv_src.seam_facets); - //FIXME what to do with the materials? - // mv_dst.m_material_id = mv_src.m_material_id; - ++ i_src; - ++ i_dst; - } -} - -static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src) -{ - assert(lr_dst.size() == lr_src.size()); - auto it_src = lr_src.cbegin(); - for (auto &kvp_dst : lr_dst) { - const auto &kvp_src = *it_src ++; - assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON); - assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON); - // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile. - // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON); - kvp_dst.second = kvp_src.second; - } -} - -static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs) -{ - typedef Transform3d::Scalar T; - const T *lv = lhs.data(); - const T *rv = rhs.data(); - for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) { - if (*lv < *rv) - return true; - else if (*lv > *rv) - return false; - } - return false; -} - -static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs) -{ - typedef Transform3d::Scalar T; - const T *lv = lhs.data(); - const T *rv = rhs.data(); - for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) - if (*lv != *rv) - return false; - return true; -} - -struct PrintObjectTrafoAndInstances -{ - Transform3d trafo; - PrintInstances instances; - bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } -}; - -// Generate a list of trafos and XY offsets for instances of a ModelObject -static std::vector print_objects_from_model_object(const ModelObject &model_object) -{ - std::set trafos; - PrintObjectTrafoAndInstances trafo; - for (ModelInstance *model_instance : model_object.instances) - if (model_instance->is_printable()) { - trafo.trafo = model_instance->get_matrix(); - auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); - // Reset the XY axes of the transformation. - trafo.trafo.data()[12] = 0; - trafo.trafo.data()[13] = 0; - // Search or insert a trafo. - auto it = trafos.emplace(trafo).first; - const_cast(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift }); - } - return std::vector(trafos.begin(), trafos.end()); -} - -// Compare just the layer ranges and their layer heights, not the associated configs. -// Ignore the layer heights if check_layer_heights is false. -static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height) -{ - if (lr1.size() != lr2.size()) - return false; - auto it2 = lr2.begin(); - for (const auto &kvp1 : lr1) { - const auto &kvp2 = *it2 ++; - if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON || - std::abs(kvp1.first.second - kvp2.first.second) > EPSILON || - (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON)) - return false; - } - return true; -} - -// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored. -static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector &va, const std::vector &vb) -{ - auto it_a = va.begin(); - auto it_b = vb.begin(); - while (it_a != va.end() || it_b != vb.end()) { - if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) { - // Skip any CustomGCode items, which are not tool changes. - ++ it_a; - continue; - } - if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) { - // Skip any CustomGCode items, which are not tool changes. - ++ it_b; - continue; - } - if (it_a == va.end() || it_b == vb.end()) - // va or vb contains more Tool Changes than the other. - return true; - assert(it_a->type == CustomGCode::ToolChange); - assert(it_b->type == CustomGCode::ToolChange); - if (*it_a != *it_b) - // The two Tool Changes differ. - return true; - ++ it_a; - ++ it_b; - } - // There is no change in custom Tool Changes. - return false; -} - -// Collect diffs of configuration values at various containers, -// resolve the filament rectract overrides of extruder retract values. -void Print::config_diffs( - const DynamicPrintConfig &new_full_config, - t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, - t_config_option_keys &full_config_diff, - DynamicPrintConfig &filament_overrides) const -{ - // Collect changes to print config, account for overrides of extruder retract values by filament presets. - { - const std::vector &extruder_retract_keys = print_config_def.extruder_retract_keys(); - const std::string filament_prefix = "filament_"; - for (const t_config_option_key &opt_key : m_config.keys()) { - const ConfigOption *opt_old = m_config.option(opt_key); - assert(opt_old != nullptr); - const ConfigOption *opt_new = new_full_config.option(opt_key); - // assert(opt_new != nullptr); - if (opt_new == nullptr) - //FIXME This may happen when executing some test cases. - continue; - const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr; - if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) { - // An extruder retract override is available at some of the filament presets. - if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) { - auto opt_copy = opt_new->clone(); - opt_copy->apply_override(opt_new_filament); - if (*opt_old == *opt_copy) - delete opt_copy; - else { - filament_overrides.set_key_value(opt_key, opt_copy); - print_diff.emplace_back(opt_key); - } - } - } else if (*opt_new != *opt_old) - print_diff.emplace_back(opt_key); - } - } - // Collect changes to object and region configs. - object_diff = m_default_object_config.diff(new_full_config); - region_diff = m_default_region_config.diff(new_full_config); - // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser. - for (const t_config_option_key &opt_key : new_full_config.keys()) { - const ConfigOption *opt_old = m_full_print_config.option(opt_key); - const ConfigOption *opt_new = new_full_config.option(opt_key); - if (opt_old == nullptr || *opt_new != *opt_old) - full_config_diff.emplace_back(opt_key); - } -} - std::vector Print::print_object_ids() const { std::vector out; @@ -591,594 +355,6 @@ std::vector Print::print_object_ids() const return out; } -Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config) -{ -#ifdef _DEBUG - check_model_ids_validity(model); -#endif /* _DEBUG */ - - // Normalize the config. - new_full_config.option("print_settings_id", true); - new_full_config.option("filament_settings_id", true); - new_full_config.option("printer_settings_id", true); - new_full_config.option("physical_printer_settings_id", true); - new_full_config.normalize_fdm(); - - // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. - t_config_option_keys print_diff, object_diff, region_diff, full_config_diff; - DynamicPrintConfig filament_overrides; - this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides); - - // Do not use the ApplyStatus as we will use the max function when updating apply_status. - unsigned int apply_status = APPLY_STATUS_UNCHANGED; - auto update_apply_status = [&apply_status](bool invalidated) - { apply_status = std::max(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); }; - if (! (print_diff.empty() && object_diff.empty() && region_diff.empty())) - update_apply_status(false); - - // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->state_mutex()); - - // The following call may stop the background processing. - if (! print_diff.empty()) - update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff)); - - // Apply variables to placeholder parser. The placeholder parser is used by G-code export, - // which should be stopped if print_diff is not empty. - size_t num_extruders = m_config.nozzle_diameter.size(); - bool num_extruders_changed = false; - if (! full_config_diff.empty()) { - update_apply_status(this->invalidate_step(psGCodeExport)); - // Set the profile aliases for the PrintBase::output_filename() - m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); - m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); - m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone()); - m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone()); - // We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser. - // see "Placeholders do not respect filament overrides." GH issue #3649 - m_placeholder_parser.apply_config(filament_overrides); - // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. - m_config.apply_only(new_full_config, print_diff, true); - //FIXME use move semantics once ConfigBase supports it. - m_config.apply(filament_overrides); - // Handle changes to object config defaults - m_default_object_config.apply_only(new_full_config, object_diff, true); - // Handle changes to regions config defaults - m_default_region_config.apply_only(new_full_config, region_diff, true); - m_full_print_config = std::move(new_full_config); - if (num_extruders != m_config.nozzle_diameter.size()) { - num_extruders = m_config.nozzle_diameter.size(); - num_extruders_changed = true; - } - } - - class LayerRanges - { - public: - LayerRanges() {} - // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs. - void assign(const t_layer_config_ranges &in) { - m_ranges.clear(); - m_ranges.reserve(in.size()); - // Input ranges are sorted lexicographically. First range trims the other ranges. - coordf_t last_z = 0; - for (const std::pair &range : in) - if (range.first.second > last_z) { - coordf_t min_z = std::max(range.first.first, 0.); - if (min_z > last_z + EPSILON) { - m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr); - last_z = min_z; - } - if (range.first.second > last_z + EPSILON) { - const DynamicPrintConfig *cfg = &range.second.get(); - m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); - last_z = range.first.second; - } - } - if (m_ranges.empty()) - m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr); - else if (m_ranges.back().second == nullptr) - m_ranges.back().first.second = DBL_MAX; - else - m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); - } - - const DynamicPrintConfig* config(const t_layer_height_range &range) const { - auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); - // #ys_FIXME_COLOR - // assert(it != m_ranges.end()); - // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); - // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); - if (it == m_ranges.end() || - std::abs(it->first.first - range.first) > EPSILON || - std::abs(it->first.second - range.second) > EPSILON ) - return nullptr; // desired range doesn't found - return (it == m_ranges.end()) ? nullptr : it->second; - } - std::vector>::const_iterator begin() const { return m_ranges.cbegin(); } - std::vector>::const_iterator end() const { return m_ranges.cend(); } - private: - std::vector> m_ranges; - }; - struct ModelObjectStatus { - enum Status { - Unknown, - Old, - New, - Moved, - Deleted, - }; - ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} - ObjectID id; - Status status; - LayerRanges layer_ranges; - // Search by id. - bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } - }; - std::set model_object_status; - - // 1) Synchronize model objects. - if (model.id() != m_model.id()) { - // Kill everything, initialize from scratch. - // Stop background processing. - this->call_cancel_callback(); - update_apply_status(this->invalidate_all_steps()); - for (PrintObject *object : m_objects) { - model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); - update_apply_status(object->invalidate_all_steps()); - delete object; - } - m_objects.clear(); - for (PrintRegion *region : m_regions) - delete region; - m_regions.clear(); - m_model.assign_copy(model); - for (const ModelObject *model_object : m_model.objects) - model_object_status.emplace(model_object->id(), ModelObjectStatus::New); - } else { - if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { - update_apply_status(num_extruders_changed || - // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering. - //FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable - // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same. - (num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ? - // The Tool Ordering and the Wipe Tower are no more valid. - this->invalidate_steps({ psWipeTower, psGCodeExport }) : - // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering. - this->invalidate_step(psGCodeExport)); - m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; - } - if (model_object_list_equal(m_model, model)) { - // The object list did not change. - for (const ModelObject *model_object : m_model.objects) - model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); - } else if (model_object_list_extended(m_model, model)) { - // Add new objects. Their volumes and configs will be synchronized later. - update_apply_status(this->invalidate_step(psGCodeExport)); - for (const ModelObject *model_object : m_model.objects) - model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); - for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { - model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); - m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i])); - m_model.objects.back()->set_model(&m_model); - } - } else { - // Reorder the objects, add new objects. - // First stop background processing before shuffling or deleting the PrintObjects in the object list. - this->call_cancel_callback(); - update_apply_status(this->invalidate_step(psGCodeExport)); - // Second create a new list of objects. - std::vector model_objects_old(std::move(m_model.objects)); - m_model.objects.clear(); - m_model.objects.reserve(model.objects.size()); - auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); }; - std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower); - for (const ModelObject *mobj : model.objects) { - auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); - if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { - // New ModelObject added. - m_model.objects.emplace_back(ModelObject::new_copy(*mobj)); - m_model.objects.back()->set_model(&m_model); - model_object_status.emplace(mobj->id(), ModelObjectStatus::New); - } else { - // Existing ModelObject re-added (possibly moved in the list). - m_model.objects.emplace_back(*it); - model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved); - } - } - bool deleted_any = false; - for (ModelObject *&model_object : model_objects_old) { - if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) { - model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted); - deleted_any = true; - } else - // Do not delete this ModelObject instance. - model_object = nullptr; - } - if (deleted_any) { - // Delete PrintObjects of the deleted ModelObjects. - PrintObjectPtrs print_objects_old = std::move(m_objects); - m_objects.clear(); - m_objects.reserve(print_objects_old.size()); - for (PrintObject *print_object : print_objects_old) { - auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); - assert(it_status != model_object_status.end()); - if (it_status->status == ModelObjectStatus::Deleted) { - update_apply_status(print_object->invalidate_all_steps()); - delete print_object; - } else - m_objects.emplace_back(print_object); - } - for (ModelObject *model_object : model_objects_old) - delete model_object; - } - } - } - - // 2) Map print objects including their transformation matrices. - struct PrintObjectStatus { - enum Status { - Unknown, - Deleted, - Reused, - New - }; - PrintObjectStatus(PrintObject *print_object, Status status = Unknown) : - id(print_object->model_object()->id()), - print_object(print_object), - trafo(print_object->trafo()), - status(status) {} - PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} - // ID of the ModelObject & PrintObject - ObjectID id; - // Pointer to the old PrintObject - PrintObject *print_object; - // Trafo generated with model_object->world_matrix(true) - Transform3d trafo; - Status status; - // Search by id. - bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; } - }; - std::multiset print_object_status; - for (PrintObject *print_object : m_objects) - print_object_status.emplace(PrintObjectStatus(print_object)); - - // 3) Synchronize ModelObjects & PrintObjects. - for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { - ModelObject &model_object = *m_model.objects[idx_model_object]; - auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); - assert(it_status != model_object_status.end()); - assert(it_status->status != ModelObjectStatus::Deleted); - const ModelObject& model_object_new = *model.objects[idx_model_object]; - const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); - if (it_status->status == ModelObjectStatus::New) - // PrintObject instances will be added in the next loop. - continue; - // Update the ModelObject instance, possibly invalidate the linked PrintObjects. - assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); - // Check whether a model part volume was added or removed, their transformations or order changed. - // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked. - bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); - bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); - bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) || - model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); - if (model_parts_differ || modifiers_differ || - model_object.origin_translation != model_object_new.origin_translation || - ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) || - ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) { - // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. - auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); - for (auto it = range.first; it != range.second; ++ it) { - update_apply_status(it->print_object->invalidate_all_steps()); - const_cast(*it).status = PrintObjectStatus::Deleted; - } - // Copy content of the ModelObject including its ID, do not change the parent. - model_object.assign_copy(model_object_new); - } else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) { - // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. - if (supports_differ) { - this->call_cancel_callback(); - update_apply_status(false); - } - // Invalidate just the supports step. - auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); - for (auto it = range.first; it != range.second; ++ it) - update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); - if (supports_differ) { - // Copy just the support volumes. - model_volume_list_update_supports(model_object, model_object_new); - } - } else if (model_custom_seam_data_changed(model_object, model_object_new)) { - update_apply_status(this->invalidate_step(psGCodeExport)); - } - if (! model_parts_differ && ! modifiers_differ) { - // Synchronize Object's config. - bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config); - if (object_config_changed) - model_object.config.assign_config(model_object_new.config); - if (! object_diff.empty() || object_config_changed || num_extruders_changed) { - PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); - auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); - for (auto it = range.first; it != range.second; ++ it) { - t_config_option_keys diff = it->print_object->config().diff(new_config); - if (! diff.empty()) { - update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff)); - it->print_object->config_apply_only(new_config, diff, true); - } - } - } - // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data). - //FIXME What to do with m_material_id? - model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART); - model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); - layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */); - // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step. - model_object.name = model_object_new.name; - model_object.input_file = model_object_new.input_file; - // Only refresh ModelInstances if there is any change. - if (model_object.instances.size() != model_object_new.instances.size() || - ! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) { - // G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list. - update_apply_status(this->invalidate_step(psGCodeExport)); - model_object.clear_instances(); - model_object.instances.reserve(model_object_new.instances.size()); - for (const ModelInstance *model_instance : model_object_new.instances) { - model_object.instances.emplace_back(new ModelInstance(*model_instance)); - model_object.instances.back()->set_model_object(&model_object); - } - } else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), - [](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable && - l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) { - // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid. - // This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only. - model_object.invalidate_bounding_box(); - // Synchronize the content of instances. - auto new_instance = model_object_new.instances.begin(); - for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) { - (*old_instance)->set_transformation((*new_instance)->get_transformation()); - (*old_instance)->print_volume_state = (*new_instance)->print_volume_state; - (*old_instance)->printable = (*new_instance)->printable; - } - } - } - } - - // 4) Generate PrintObjects from ModelObjects and their instances. - { - PrintObjectPtrs print_objects_new; - print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); - bool new_objects = false; - // Walk over all new model objects and check, whether there are matching PrintObjects. - for (ModelObject *model_object : m_model.objects) { - auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); - std::vector old; - if (range.first != range.second) { - old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); - for (auto it = range.first; it != range.second; ++ it) - if (it->status != PrintObjectStatus::Deleted) - old.emplace_back(&(*it)); - } - // Generate a list of trafos and XY offsets for instances of a ModelObject - // Producing the config for PrintObject on demand, caching it at print_object_last. - const PrintObject *print_object_last = nullptr; - auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) { - print_object->config_apply(print_object_last ? - print_object_last->config() : - PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders)); - print_object_last = print_object; - }; - std::vector new_print_instances = print_objects_from_model_object(*model_object); - if (old.empty()) { - // Simple case, just generate new instances. - for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) { - PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances)); - print_object_apply_config(print_object); - print_objects_new.emplace_back(print_object); - // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); - new_objects = true; - } - continue; - } - // Complex case, try to merge the two lists. - // Sort the old lexicographically by their trafos. - std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); - // Merge the old / new lists. - auto it_old = old.begin(); - for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) { - for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); - if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { - // This is a new instance (or a set of instances with the same trafo). Just add it. - PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances)); - print_object_apply_config(print_object); - print_objects_new.emplace_back(print_object); - // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); - new_objects = true; - if (it_old != old.end()) - const_cast(*it_old)->status = PrintObjectStatus::Deleted; - } else { - // The PrintObject already exists and the copies differ. - PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances)); - if (status != PrintBase::APPLY_STATUS_UNCHANGED) - update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); - print_objects_new.emplace_back((*it_old)->print_object); - const_cast(*it_old)->status = PrintObjectStatus::Reused; - } - } - } - if (m_objects != print_objects_new) { - this->call_cancel_callback(); - update_apply_status(this->invalidate_all_steps()); - m_objects = print_objects_new; - // Delete the PrintObjects marked as Unknown or Deleted. - bool deleted_objects = false; - for (auto &pos : print_object_status) - if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { - update_apply_status(pos.print_object->invalidate_all_steps()); - delete pos.print_object; - deleted_objects = true; - } - if (new_objects || deleted_objects) - update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); - if (new_objects) - update_apply_status(false); - } - print_object_status.clear(); - } - - // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. - // Update reference counts of regions from the remaining PrintObjects and their volumes. - // Regions with zero references could and should be reused. - for (PrintRegion *region : m_regions) - region->m_refcnt = 0; - for (PrintObject *print_object : m_objects) { - int idx_region = 0; - for (const auto &volumes : print_object->region_volumes) { - if (! volumes.empty()) - ++ m_regions[idx_region]->m_refcnt; - ++ idx_region; - } - } - - // All regions now have distinct settings. - // Check whether applying the new region config defaults we'd get different regions. - for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { - PrintRegion ®ion = *m_regions[region_id]; - PrintRegionConfig this_region_config; - bool this_region_config_set = false; - for (PrintObject *print_object : m_objects) { - const LayerRanges *layer_ranges; - { - auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); - assert(it_status != model_object_status.end()); - assert(it_status->status != ModelObjectStatus::Deleted); - layer_ranges = &it_status->layer_ranges; - } - if (region_id < print_object->region_volumes.size()) { - for (const std::pair &volume_and_range : print_object->region_volumes[region_id]) { - const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; - const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); - if (this_region_config_set) { - // If the new config for this volume differs from the other - // volume configs currently associated to this region, it means - // the region subdivision does not make sense anymore. - if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders))) - // Regions were split. Reset this print_object. - goto print_object_end; - } else { - this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); - for (size_t i = 0; i < region_id; ++ i) { - const PrintRegion ®ion_other = *m_regions[i]; - if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) - // Regions were merged. Reset this print_object. - goto print_object_end; - } - this_region_config_set = true; - } - } - } - continue; - print_object_end: - update_apply_status(print_object->invalidate_all_steps()); - // Decrease the references to regions from this volume. - int ireg = 0; - for (const std::vector> &volumes : print_object->region_volumes) { - if (! volumes.empty()) - -- m_regions[ireg]->m_refcnt; - ++ ireg; - } - print_object->region_volumes.clear(); - } - if (this_region_config_set) { - t_config_option_keys diff = region.config().diff(this_region_config); - if (! diff.empty()) { - // Stop the background process before assigning new configuration to the regions. - for (PrintObject *print_object : m_objects) - if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) - update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff)); - region.config_apply_only(this_region_config, diff, false); - } - } - } - - // Possibly add new regions for the newly added or resetted PrintObjects. - for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) { - PrintObject &print_object0 = *m_objects[idx_print_object]; - const ModelObject &model_object = *print_object0.model_object(); - const LayerRanges *layer_ranges; - { - auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); - assert(it_status != model_object_status.end()); - assert(it_status->status != ModelObjectStatus::Deleted); - layer_ranges = &it_status->layer_ranges; - } - std::vector regions_in_object; - regions_in_object.reserve(64); - for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { - PrintObject &print_object = *m_objects[i]; - bool fresh = print_object.region_volumes.empty(); - unsigned int volume_id = 0; - unsigned int idx_region_in_object = 0; - for (const ModelVolume *volume : model_object.volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) { - ++ volume_id; - continue; - } - // Filter the layer ranges, so they do not overlap and they contain at least a single layer. - // Now insert a volume with a layer range to its own region. - for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) { - int region_id = -1; - if (&print_object == &print_object0) { - // Get the config applied to this volume. - PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders); - // Find an existing print region with the same config. - int idx_empty_slot = -1; - for (int i = 0; i < (int)m_regions.size(); ++ i) { - if (m_regions[i]->m_refcnt == 0) { - if (idx_empty_slot == -1) - idx_empty_slot = i; - } else if (config.equals(m_regions[i]->config())) { - region_id = i; - break; - } - } - // If no region exists with the same config, create a new one. - if (region_id == -1) { - if (idx_empty_slot == -1) { - region_id = (int)m_regions.size(); - this->add_region(config); - } else { - region_id = idx_empty_slot; - m_regions[region_id]->set_config(std::move(config)); - } - } - regions_in_object.emplace_back(region_id); - } else - region_id = regions_in_object[idx_region_in_object ++]; - // Assign volume to a region. - if (fresh) { - if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) - ++ m_regions[region_id]->m_refcnt; - print_object.add_region_volume(region_id, volume_id, it_range->first); - } - } - ++ volume_id; - } - } - } - - // Update SlicingParameters for each object where the SlicingParameters is not valid. - // If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use - // (posSlicing and posSupportMaterial was invalidated). - for (PrintObject *object : m_objects) - object->update_slicing_parameters(); - -#ifdef _DEBUG - check_model_ids_equal(m_model, model); -#endif /* _DEBUG */ - - return static_cast(apply_status); -} - bool Print::has_infinite_skirt() const { return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index ff4ae68f2..9677a585a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -509,12 +509,6 @@ protected: bool invalidate_step(PrintStep step); private: - void config_diffs( - const DynamicPrintConfig &new_full_config, - t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, - t_config_option_keys &full_config_diff, - DynamicPrintConfig &filament_overrides) const; - bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector &opt_keys); void _make_skirt(); @@ -526,9 +520,6 @@ private: // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. std::vector first_layer_wipe_tower_corners() const; - // Declared here to have access to Model / ModelObject / ModelInstance - static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); - PrintConfig m_config; PrintObjectConfig m_default_object_config; PrintRegionConfig m_default_region_config; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp new file mode 100644 index 000000000..978707a24 --- /dev/null +++ b/src/libslic3r/PrintApply.cpp @@ -0,0 +1,835 @@ +#include "Model.hpp" +#include "Print.hpp" + +namespace Slic3r { + +// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new +// in the exact order and with the same IDs. +// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. +// Friend to ModelVolume to allow copying. +static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new) +{ + typedef std::pair ModelVolumeWithStatus; + std::vector old_volumes; + old_volumes.reserve(model_object_dst.volumes.size()); + for (const ModelVolume *model_volume : model_object_dst.volumes) + old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false)); + auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); }; + auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); }; + std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower); + model_object_dst.volumes.clear(); + model_object_dst.volumes.reserve(model_object_new.volumes.size()); + for (const ModelVolume *model_volume_src : model_object_new.volumes) { + ModelVolumeWithStatus key(model_volume_src, false); + auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower); + if (it != old_volumes.end() && model_volume_equal(*it, key)) { + // The volume was found in the old list. Just copy it. + assert(! it->second); // not consumed yet + it->second = true; + ModelVolume *model_volume_dst = const_cast(it->first); + // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. + assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type()); + model_object_dst.volumes.emplace_back(model_volume_dst); + if (model_volume_dst->is_support_modifier()) { + // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. + model_volume_dst->set_type(model_volume_src->type()); + model_volume_dst->set_transformation(model_volume_src->get_transformation()); + } + assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix())); + } else { + // The volume was not found in the old list. Create a new copy. + assert(model_volume_src->is_support_modifier()); + model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src)); + model_object_dst.volumes.back()->set_model_object(&model_object_dst); + } + } + // Release the non-consumed old volumes (those were deleted from the new list). + for (ModelVolumeWithStatus &mv_with_status : old_volumes) + if (! mv_with_status.second) + delete mv_with_status.first; +} + +static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type) +{ + size_t i_src, i_dst; + for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) { + const ModelVolume &mv_src = *model_object_src.volumes[i_src]; + ModelVolume &mv_dst = *model_object_dst.volumes[i_dst]; + if (mv_src.type() != type) { + ++ i_src; + continue; + } + if (mv_dst.type() != type) { + ++ i_dst; + continue; + } + assert(mv_src.id() == mv_dst.id()); + // Copy the ModelVolume data. + mv_dst.name = mv_src.name; + mv_dst.config.assign_config(mv_src.config); + assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id()); + mv_dst.supported_facets.assign(mv_src.supported_facets); + assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id()); + mv_dst.seam_facets.assign(mv_src.seam_facets); + //FIXME what to do with the materials? + // mv_dst.m_material_id = mv_src.m_material_id; + ++ i_src; + ++ i_dst; + } +} + +static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src) +{ + assert(lr_dst.size() == lr_src.size()); + auto it_src = lr_src.cbegin(); + for (auto &kvp_dst : lr_dst) { + const auto &kvp_src = *it_src ++; + assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON); + assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON); + // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile. + // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON); + kvp_dst.second = kvp_src.second; + } +} + +static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs) +{ + typedef Transform3d::Scalar T; + const T *lv = lhs.data(); + const T *rv = rhs.data(); + for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) { + if (*lv < *rv) + return true; + else if (*lv > *rv) + return false; + } + return false; +} + +static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs) +{ + typedef Transform3d::Scalar T; + const T *lv = lhs.data(); + const T *rv = rhs.data(); + for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) + if (*lv != *rv) + return false; + return true; +} + +struct PrintObjectTrafoAndInstances +{ + Transform3d trafo; + PrintInstances instances; + bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } +}; + +// Generate a list of trafos and XY offsets for instances of a ModelObject +static std::vector print_objects_from_model_object(const ModelObject &model_object) +{ + std::set trafos; + PrintObjectTrafoAndInstances trafo; + for (ModelInstance *model_instance : model_object.instances) + if (model_instance->is_printable()) { + trafo.trafo = model_instance->get_matrix(); + auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); + // Reset the XY axes of the transformation. + trafo.trafo.data()[12] = 0; + trafo.trafo.data()[13] = 0; + // Search or insert a trafo. + auto it = trafos.emplace(trafo).first; + const_cast(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift }); + } + return std::vector(trafos.begin(), trafos.end()); +} + +// Compare just the layer ranges and their layer heights, not the associated configs. +// Ignore the layer heights if check_layer_heights is false. +static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height) +{ + if (lr1.size() != lr2.size()) + return false; + auto it2 = lr2.begin(); + for (const auto &kvp1 : lr1) { + const auto &kvp2 = *it2 ++; + if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON || + std::abs(kvp1.first.second - kvp2.first.second) > EPSILON || + (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON)) + return false; + } + return true; +} + +// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored. +static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector &va, const std::vector &vb) +{ + auto it_a = va.begin(); + auto it_b = vb.begin(); + while (it_a != va.end() || it_b != vb.end()) { + if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) { + // Skip any CustomGCode items, which are not tool changes. + ++ it_a; + continue; + } + if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) { + // Skip any CustomGCode items, which are not tool changes. + ++ it_b; + continue; + } + if (it_a == va.end() || it_b == vb.end()) + // va or vb contains more Tool Changes than the other. + return true; + assert(it_a->type == CustomGCode::ToolChange); + assert(it_b->type == CustomGCode::ToolChange); + if (*it_a != *it_b) + // The two Tool Changes differ. + return true; + ++ it_a; + ++ it_b; + } + // There is no change in custom Tool Changes. + return false; +} + +// Collect changes to print config, account for overrides of extruder retract values by filament presets. +static t_config_option_keys print_config_diffs( + const PrintConfig ¤t_config, + const DynamicPrintConfig &new_full_config, + DynamicPrintConfig &filament_overrides) +{ + const std::vector &extruder_retract_keys = print_config_def.extruder_retract_keys(); + const std::string filament_prefix = "filament_"; + t_config_option_keys print_diff; + for (const t_config_option_key &opt_key : current_config.keys()) { + const ConfigOption *opt_old = current_config.option(opt_key); + assert(opt_old != nullptr); + const ConfigOption *opt_new = new_full_config.option(opt_key); + // assert(opt_new != nullptr); + if (opt_new == nullptr) + //FIXME This may happen when executing some test cases. + continue; + const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr; + if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) { + // An extruder retract override is available at some of the filament presets. + if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) { + auto opt_copy = opt_new->clone(); + opt_copy->apply_override(opt_new_filament); + if (*opt_old == *opt_copy) + delete opt_copy; + else { + filament_overrides.set_key_value(opt_key, opt_copy); + print_diff.emplace_back(opt_key); + } + } + } else if (*opt_new != *opt_old) + print_diff.emplace_back(opt_key); + } + + return print_diff; + } + +// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser. +static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig ¤t_full_config, const DynamicPrintConfig &new_full_config) +{ + t_config_option_keys full_config_diff; + for (const t_config_option_key &opt_key : new_full_config.keys()) { + const ConfigOption *opt_old = current_full_config.option(opt_key); + const ConfigOption *opt_new = new_full_config.option(opt_key); + if (opt_old == nullptr || *opt_new != *opt_old) + full_config_diff.emplace_back(opt_key); + } + return full_config_diff; +} + +Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config) +{ +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + // Normalize the config. + new_full_config.option("print_settings_id", true); + new_full_config.option("filament_settings_id", true); + new_full_config.option("printer_settings_id", true); + new_full_config.option("physical_printer_settings_id", true); + new_full_config.normalize_fdm(); + + // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. + DynamicPrintConfig filament_overrides; + t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides); + t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config); + // Collect changes to object and region configs. + t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); + t_config_option_keys region_diff = m_default_region_config.diff(new_full_config); + + // Do not use the ApplyStatus as we will use the max function when updating apply_status. + unsigned int apply_status = APPLY_STATUS_UNCHANGED; + auto update_apply_status = [&apply_status](bool invalidated) + { apply_status = std::max(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); }; + if (! (print_diff.empty() && object_diff.empty() && region_diff.empty())) + update_apply_status(false); + + // Grab the lock for the Print / PrintObject milestones. + tbb::mutex::scoped_lock lock(this->state_mutex()); + + // The following call may stop the background processing. + if (! print_diff.empty()) + update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff)); + + // Apply variables to placeholder parser. The placeholder parser is used by G-code export, + // which should be stopped if print_diff is not empty. + size_t num_extruders = m_config.nozzle_diameter.size(); + bool num_extruders_changed = false; + if (! full_config_diff.empty()) { + update_apply_status(this->invalidate_step(psGCodeExport)); + // Set the profile aliases for the PrintBase::output_filename() + m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); + m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); + m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone()); + m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone()); + // We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser. + // see "Placeholders do not respect filament overrides." GH issue #3649 + m_placeholder_parser.apply_config(filament_overrides); + // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. + m_config.apply_only(new_full_config, print_diff, true); + //FIXME use move semantics once ConfigBase supports it. + m_config.apply(filament_overrides); + // Handle changes to object config defaults + m_default_object_config.apply_only(new_full_config, object_diff, true); + // Handle changes to regions config defaults + m_default_region_config.apply_only(new_full_config, region_diff, true); + m_full_print_config = std::move(new_full_config); + if (num_extruders != m_config.nozzle_diameter.size()) { + num_extruders = m_config.nozzle_diameter.size(); + num_extruders_changed = true; + } + } + + class LayerRanges + { + public: + LayerRanges() {} + // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs. + void assign(const t_layer_config_ranges &in) { + m_ranges.clear(); + m_ranges.reserve(in.size()); + // Input ranges are sorted lexicographically. First range trims the other ranges. + coordf_t last_z = 0; + for (const std::pair &range : in) + if (range.first.second > last_z) { + coordf_t min_z = std::max(range.first.first, 0.); + if (min_z > last_z + EPSILON) { + m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr); + last_z = min_z; + } + if (range.first.second > last_z + EPSILON) { + const DynamicPrintConfig *cfg = &range.second.get(); + m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); + last_z = range.first.second; + } + } + if (m_ranges.empty()) + m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr); + else if (m_ranges.back().second == nullptr) + m_ranges.back().first.second = DBL_MAX; + else + m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); + } + + const DynamicPrintConfig* config(const t_layer_height_range &range) const { + auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); + // #ys_FIXME_COLOR + // assert(it != m_ranges.end()); + // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); + // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); + if (it == m_ranges.end() || + std::abs(it->first.first - range.first) > EPSILON || + std::abs(it->first.second - range.second) > EPSILON ) + return nullptr; // desired range doesn't found + return (it == m_ranges.end()) ? nullptr : it->second; + } + std::vector>::const_iterator begin() const { return m_ranges.cbegin(); } + std::vector>::const_iterator end() const { return m_ranges.cend(); } + private: + std::vector> m_ranges; + }; + struct ModelObjectStatus { + enum Status { + Unknown, + Old, + New, + Moved, + Deleted, + }; + ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} + ObjectID id; + Status status; + LayerRanges layer_ranges; + // Search by id. + bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } + }; + std::set model_object_status; + + // 1) Synchronize model objects. + if (model.id() != m_model.id()) { + // Kill everything, initialize from scratch. + // Stop background processing. + this->call_cancel_callback(); + update_apply_status(this->invalidate_all_steps()); + for (PrintObject *object : m_objects) { + model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); + update_apply_status(object->invalidate_all_steps()); + delete object; + } + m_objects.clear(); + for (PrintRegion *region : m_regions) + delete region; + m_regions.clear(); + m_model.assign_copy(model); + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::New); + } else { + if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { + update_apply_status(num_extruders_changed || + // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering. + //FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable + // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same. + (num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ? + // The Tool Ordering and the Wipe Tower are no more valid. + this->invalidate_steps({ psWipeTower, psGCodeExport }) : + // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering. + this->invalidate_step(psGCodeExport)); + m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; + } + if (model_object_list_equal(m_model, model)) { + // The object list did not change. + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + } else if (model_object_list_extended(m_model, model)) { + // Add new objects. Their volumes and configs will be synchronized later. + update_apply_status(this->invalidate_step(psGCodeExport)); + for (const ModelObject *model_object : m_model.objects) + model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { + model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); + m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i])); + m_model.objects.back()->set_model(&m_model); + } + } else { + // Reorder the objects, add new objects. + // First stop background processing before shuffling or deleting the PrintObjects in the object list. + this->call_cancel_callback(); + update_apply_status(this->invalidate_step(psGCodeExport)); + // Second create a new list of objects. + std::vector model_objects_old(std::move(m_model.objects)); + m_model.objects.clear(); + m_model.objects.reserve(model.objects.size()); + auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); }; + std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower); + for (const ModelObject *mobj : model.objects) { + auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); + if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { + // New ModelObject added. + m_model.objects.emplace_back(ModelObject::new_copy(*mobj)); + m_model.objects.back()->set_model(&m_model); + model_object_status.emplace(mobj->id(), ModelObjectStatus::New); + } else { + // Existing ModelObject re-added (possibly moved in the list). + m_model.objects.emplace_back(*it); + model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved); + } + } + bool deleted_any = false; + for (ModelObject *&model_object : model_objects_old) { + if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) { + model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted); + deleted_any = true; + } else + // Do not delete this ModelObject instance. + model_object = nullptr; + } + if (deleted_any) { + // Delete PrintObjects of the deleted ModelObjects. + PrintObjectPtrs print_objects_old = std::move(m_objects); + m_objects.clear(); + m_objects.reserve(print_objects_old.size()); + for (PrintObject *print_object : print_objects_old) { + auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); + assert(it_status != model_object_status.end()); + if (it_status->status == ModelObjectStatus::Deleted) { + update_apply_status(print_object->invalidate_all_steps()); + delete print_object; + } else + m_objects.emplace_back(print_object); + } + for (ModelObject *model_object : model_objects_old) + delete model_object; + } + } + } + + // 2) Map print objects including their transformation matrices. + struct PrintObjectStatus { + enum Status { + Unknown, + Deleted, + Reused, + New + }; + PrintObjectStatus(PrintObject *print_object, Status status = Unknown) : + id(print_object->model_object()->id()), + print_object(print_object), + trafo(print_object->trafo()), + status(status) {} + PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + // ID of the ModelObject & PrintObject + ObjectID id; + // Pointer to the old PrintObject + PrintObject *print_object; + // Trafo generated with model_object->world_matrix(true) + Transform3d trafo; + Status status; + // Search by id. + bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; } + }; + std::multiset print_object_status; + for (PrintObject *print_object : m_objects) + print_object_status.emplace(PrintObjectStatus(print_object)); + + // 3) Synchronize ModelObjects & PrintObjects. + for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { + ModelObject &model_object = *m_model.objects[idx_model_object]; + auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + const ModelObject& model_object_new = *model.objects[idx_model_object]; + const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); + if (it_status->status == ModelObjectStatus::New) + // PrintObject instances will be added in the next loop. + continue; + // Update the ModelObject instance, possibly invalidate the linked PrintObjects. + assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); + // Check whether a model part volume was added or removed, their transformations or order changed. + // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked. + bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); + bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); + bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) || + model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); + if (model_parts_differ || modifiers_differ || + model_object.origin_translation != model_object_new.origin_translation || + ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) || + ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) { + // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) { + update_apply_status(it->print_object->invalidate_all_steps()); + const_cast(*it).status = PrintObjectStatus::Deleted; + } + // Copy content of the ModelObject including its ID, do not change the parent. + model_object.assign_copy(model_object_new); + } else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) { + // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. + if (supports_differ) { + this->call_cancel_callback(); + update_apply_status(false); + } + // Invalidate just the supports step. + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) + update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); + if (supports_differ) { + // Copy just the support volumes. + model_volume_list_update_supports(model_object, model_object_new); + } + } else if (model_custom_seam_data_changed(model_object, model_object_new)) { + update_apply_status(this->invalidate_step(psGCodeExport)); + } + if (! model_parts_differ && ! modifiers_differ) { + // Synchronize Object's config. + bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config); + if (object_config_changed) + model_object.config.assign_config(model_object_new.config); + if (! object_diff.empty() || object_config_changed || num_extruders_changed) { + PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) { + t_config_option_keys diff = it->print_object->config().diff(new_config); + if (! diff.empty()) { + update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff)); + it->print_object->config_apply_only(new_config, diff, true); + } + } + } + // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data). + //FIXME What to do with m_material_id? + model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART); + model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); + layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */); + // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step. + model_object.name = model_object_new.name; + model_object.input_file = model_object_new.input_file; + // Only refresh ModelInstances if there is any change. + if (model_object.instances.size() != model_object_new.instances.size() || + ! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) { + // G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list. + update_apply_status(this->invalidate_step(psGCodeExport)); + model_object.clear_instances(); + model_object.instances.reserve(model_object_new.instances.size()); + for (const ModelInstance *model_instance : model_object_new.instances) { + model_object.instances.emplace_back(new ModelInstance(*model_instance)); + model_object.instances.back()->set_model_object(&model_object); + } + } else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), + [](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable && + l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) { + // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid. + // This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only. + model_object.invalidate_bounding_box(); + // Synchronize the content of instances. + auto new_instance = model_object_new.instances.begin(); + for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) { + (*old_instance)->set_transformation((*new_instance)->get_transformation()); + (*old_instance)->print_volume_state = (*new_instance)->print_volume_state; + (*old_instance)->printable = (*new_instance)->printable; + } + } + } + } + + // 4) Generate PrintObjects from ModelObjects and their instances. + { + PrintObjectPtrs print_objects_new; + print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); + bool new_objects = false; + // Walk over all new model objects and check, whether there are matching PrintObjects. + for (ModelObject *model_object : m_model.objects) { + auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); + std::vector old; + if (range.first != range.second) { + old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); + for (auto it = range.first; it != range.second; ++ it) + if (it->status != PrintObjectStatus::Deleted) + old.emplace_back(&(*it)); + } + // Generate a list of trafos and XY offsets for instances of a ModelObject + // Producing the config for PrintObject on demand, caching it at print_object_last. + const PrintObject *print_object_last = nullptr; + auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) { + print_object->config_apply(print_object_last ? + print_object_last->config() : + PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders)); + print_object_last = print_object; + }; + std::vector new_print_instances = print_objects_from_model_object(*model_object); + if (old.empty()) { + // Simple case, just generate new instances. + for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) { + PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances)); + print_object_apply_config(print_object); + print_objects_new.emplace_back(print_object); + // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + new_objects = true; + } + continue; + } + // Complex case, try to merge the two lists. + // Sort the old lexicographically by their trafos. + std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); + // Merge the old / new lists. + auto it_old = old.begin(); + for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) { + for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); + if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { + // This is a new instance (or a set of instances with the same trafo). Just add it. + PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances)); + print_object_apply_config(print_object); + print_objects_new.emplace_back(print_object); + // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); + new_objects = true; + if (it_old != old.end()) + const_cast(*it_old)->status = PrintObjectStatus::Deleted; + } else { + // The PrintObject already exists and the copies differ. + PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances)); + if (status != PrintBase::APPLY_STATUS_UNCHANGED) + update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); + print_objects_new.emplace_back((*it_old)->print_object); + const_cast(*it_old)->status = PrintObjectStatus::Reused; + } + } + } + if (m_objects != print_objects_new) { + this->call_cancel_callback(); + update_apply_status(this->invalidate_all_steps()); + m_objects = print_objects_new; + // Delete the PrintObjects marked as Unknown or Deleted. + bool deleted_objects = false; + for (auto &pos : print_object_status) + if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { + update_apply_status(pos.print_object->invalidate_all_steps()); + delete pos.print_object; + deleted_objects = true; + } + if (new_objects || deleted_objects) + update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); + if (new_objects) + update_apply_status(false); + } + print_object_status.clear(); + } + + // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. + // Update reference counts of regions from the remaining PrintObjects and their volumes. + // Regions with zero references could and should be reused. + for (PrintRegion *region : m_regions) + region->m_refcnt = 0; + for (PrintObject *print_object : m_objects) { + int idx_region = 0; + for (const auto &volumes : print_object->region_volumes) { + if (! volumes.empty()) + ++ m_regions[idx_region]->m_refcnt; + ++ idx_region; + } + } + + // All regions now have distinct settings. + // Check whether applying the new region config defaults we'd get different regions. + for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_regions[region_id]; + PrintRegionConfig this_region_config; + bool this_region_config_set = false; + for (PrintObject *print_object : m_objects) { + const LayerRanges *layer_ranges; + { + auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + layer_ranges = &it_status->layer_ranges; + } + if (region_id < print_object->region_volumes.size()) { + for (const std::pair &volume_and_range : print_object->region_volumes[region_id]) { + const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; + const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); + if (this_region_config_set) { + // If the new config for this volume differs from the other + // volume configs currently associated to this region, it means + // the region subdivision does not make sense anymore. + if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders))) + // Regions were split. Reset this print_object. + goto print_object_end; + } else { + this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); + for (size_t i = 0; i < region_id; ++ i) { + const PrintRegion ®ion_other = *m_regions[i]; + if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + } + this_region_config_set = true; + } + } + } + continue; + print_object_end: + update_apply_status(print_object->invalidate_all_steps()); + // Decrease the references to regions from this volume. + int ireg = 0; + for (const std::vector> &volumes : print_object->region_volumes) { + if (! volumes.empty()) + -- m_regions[ireg]->m_refcnt; + ++ ireg; + } + print_object->region_volumes.clear(); + } + if (this_region_config_set) { + t_config_option_keys diff = region.config().diff(this_region_config); + if (! diff.empty()) { + // Stop the background process before assigning new configuration to the regions. + for (PrintObject *print_object : m_objects) + if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) + update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff)); + region.config_apply_only(this_region_config, diff, false); + } + } + } + + // Possibly add new regions for the newly added or resetted PrintObjects. + for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) { + PrintObject &print_object0 = *m_objects[idx_print_object]; + const ModelObject &model_object = *print_object0.model_object(); + const LayerRanges *layer_ranges; + { + auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + layer_ranges = &it_status->layer_ranges; + } + std::vector regions_in_object; + regions_in_object.reserve(64); + for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { + PrintObject &print_object = *m_objects[i]; + bool fresh = print_object.region_volumes.empty(); + unsigned int volume_id = 0; + unsigned int idx_region_in_object = 0; + for (const ModelVolume *volume : model_object.volumes) { + if (! volume->is_model_part() && ! volume->is_modifier()) { + ++ volume_id; + continue; + } + // Filter the layer ranges, so they do not overlap and they contain at least a single layer. + // Now insert a volume with a layer range to its own region. + for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) { + int region_id = -1; + if (&print_object == &print_object0) { + // Get the config applied to this volume. + PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders); + // Find an existing print region with the same config. + int idx_empty_slot = -1; + for (int i = 0; i < (int)m_regions.size(); ++ i) { + if (m_regions[i]->m_refcnt == 0) { + if (idx_empty_slot == -1) + idx_empty_slot = i; + } else if (config.equals(m_regions[i]->config())) { + region_id = i; + break; + } + } + // If no region exists with the same config, create a new one. + if (region_id == -1) { + if (idx_empty_slot == -1) { + region_id = (int)m_regions.size(); + this->add_region(config); + } else { + region_id = idx_empty_slot; + m_regions[region_id]->set_config(std::move(config)); + } + } + regions_in_object.emplace_back(region_id); + } else + region_id = regions_in_object[idx_region_in_object ++]; + // Assign volume to a region. + if (fresh) { + if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) + ++ m_regions[region_id]->m_refcnt; + print_object.add_region_volume(region_id, volume_id, it_range->first); + } + } + ++ volume_id; + } + } + } + + // Update SlicingParameters for each object where the SlicingParameters is not valid. + // If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use + // (posSlicing and posSupportMaterial was invalidated). + for (PrintObject *object : m_objects) + object->update_slicing_parameters(); + +#ifdef _DEBUG + check_model_ids_equal(m_model, model); +#endif /* _DEBUG */ + + return static_cast(apply_status); +} + +} // namespace Slic3r From 18001fbb4e8aa473f08188c7f1abc0f03ab57ab5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 14:32:19 +0200 Subject: [PATCH 29/79] Fixing compilation on gcc --- src/libslic3r/Model.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index db7043769..f66300abc 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -680,7 +680,7 @@ protected: friend class SLAPrint; friend class Model; friend class ModelObject; - friend static void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new); + friend void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new); // Copies IDs of both the ModelVolume and its config. explicit ModelVolume(const ModelVolume &rhs) = default; From a7c67415c77e3bdf08d945ae3dd33cfe376b0578 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 14:34:54 +0200 Subject: [PATCH 30/79] Another try of fixing compilation on gcc. --- src/libslic3r/PrintApply.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 978707a24..df576fe42 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -7,7 +7,8 @@ namespace Slic3r { // in the exact order and with the same IDs. // It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. // Friend to ModelVolume to allow copying. -static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new) +// static is not accepted by gcc if declared as a friend of ModelObject. +/* static */ void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new) { typedef std::pair ModelVolumeWithStatus; std::vector old_volumes; From d21b9aa089e23d347b1c4b7e15eee92708c9bdd9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 15:05:52 +0200 Subject: [PATCH 31/79] Fixing perl integration --- xs/xsp/Print.xsp | 1 - 1 file changed, 1 deletion(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 9e632bd53..a7b4e562f 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -32,7 +32,6 @@ _constant() Ref config() %code%{ RETVAL = &THIS->config(); %}; - Ref print(); }; %name{Slic3r::Print::Object} class PrintObject { From 714149dab2466e5f1a9e26a0efa0b3daaf425d89 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 16:21:55 +0200 Subject: [PATCH 32/79] WIP: Moving ownership of PrintRegions to PrintObjects. --- src/libslic3r/Fill/FillAdaptive.cpp | 6 +- src/libslic3r/GCode.cpp | 43 ++++---- src/libslic3r/GCode/ToolOrdering.cpp | 12 +-- src/libslic3r/Print.cpp | 43 +++----- src/libslic3r/Print.hpp | 38 ++++--- src/libslic3r/PrintApply.cpp | 46 ++++---- src/libslic3r/PrintObject.cpp | 152 +++++++++++++++------------ src/libslic3r/SupportMaterial.cpp | 15 ++- xs/xsp/Print.xsp | 4 +- 9 files changed, 184 insertions(+), 175 deletions(-) diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 6b303e636..3eabc6106 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -291,13 +291,13 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob double extrusion_width; }; std::vector region_fill_data; - region_fill_data.reserve(print_object.print()->regions().size()); + region_fill_data.reserve(print_object.num_printing_regions()); bool build_octree = false; const std::vector &nozzle_diameters = print_object.print()->config().nozzle_diameter.values; double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); - for (const PrintRegion *region : print_object.print()->regions()) { - const PrintRegionConfig &config = region->config(); + for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { + const PrintRegionConfig &config = print_object.printing_region(region_id).config(); bool nonempty = config.fill_density > 0; bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic; bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3b1575f8f..5e5d2957a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -796,19 +796,19 @@ namespace DoExport { // get the minimum cross-section used in the print std::vector mm3_per_mm; for (auto object : print.objects()) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { - const PrintRegion* region = print.regions()[region_id]; + for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = object->printing_region(region_id); for (auto layer : object->layers()) { const LayerRegion* layerm = layer->regions()[region_id]; - if (region->config().get_abs_value("perimeter_speed") == 0 || - region->config().get_abs_value("small_perimeter_speed") == 0 || - region->config().get_abs_value("external_perimeter_speed") == 0 || - region->config().get_abs_value("bridge_speed") == 0) + if (region.config().get_abs_value("perimeter_speed") == 0 || + region.config().get_abs_value("small_perimeter_speed") == 0 || + region.config().get_abs_value("external_perimeter_speed") == 0 || + region.config().get_abs_value("bridge_speed") == 0) mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config().get_abs_value("infill_speed") == 0 || - region->config().get_abs_value("solid_infill_speed") == 0 || - region->config().get_abs_value("top_solid_infill_speed") == 0 || - region->config().get_abs_value("bridge_speed") == 0) + if (region.config().get_abs_value("infill_speed") == 0 || + region.config().get_abs_value("solid_infill_speed") == 0 || + region.config().get_abs_value("top_solid_infill_speed") == 0 || + region.config().get_abs_value("bridge_speed") == 0) { // Minimal volumetric flow should not be calculated over ironing extrusions. // Use following lambda instead of the built-it method. @@ -1112,16 +1112,17 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu const double layer_height = first_object->config().layer_height.value; assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; - for (const PrintRegion* region : print.regions()) { - _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width()); - _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width()); - _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width()); - _write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width()); - _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width()); + for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { + const PrintRegion ®ion = *print.get_print_region(region_id); + _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + _write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + _write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + _write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + _write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); if (print.has_support_material()) _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); if (print.config().first_layer_extrusion_width.value > 0) - _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width()); + _write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); _write_format(file, "\n"); } print.throw_if_canceled(); @@ -2109,7 +2110,7 @@ void GCode::process_layer( const LayerRegion *layerm = layer.regions()[region_id]; if (layerm == nullptr) continue; - const PrintRegion ®ion = *print.regions()[region_id]; + const PrintRegion ®ion = *layerm->region(); // Now we must process perimeters and infills and create islands of extrusions in by_region std::map. // It is also necessary to save which extrusions are part of MM wiping and which are not. @@ -2167,7 +2168,7 @@ void GCode::process_layer( // extrusions->first_point fits inside ith slice point_inside_surface(island_idx, extrusions->first_point())) { if (islands[island_idx].by_region.empty()) - islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region()); islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides); break; } @@ -2570,7 +2571,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig()); + m_config.apply(print.get_print_region(®ion - &by_region.front())->config()); for (const ExtrusionEntity *ee : region.perimeters) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } @@ -2591,7 +2592,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorrole() == erIroning) == ironing) extrusions.emplace_back(ee); if (! extrusions.empty()) { - m_config.apply(print.regions()[®ion - &by_region.front()]->config()); + m_config.apply(print.get_print_region(®ion - &by_region.front())->config()); chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); for (const ExtrusionEntity *fill : extrusions) { auto *eec = dynamic_cast(fill); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index c45e26015..8005e57bf 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -223,11 +223,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto layer_tools.extruder_override = extruder_override; // What extruders are required to print this object layer? - for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) { const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; if (layerm == nullptr) continue; - const PrintRegion ®ion = *object.print()->regions()[region_id]; + const PrintRegion ®ion = *layerm->region(); if (! layerm->perimeters.entities.empty()) { bool something_nonoverriddable = true; @@ -689,8 +689,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves for (unsigned int copy = 0; copy < num_of_copies; ++copy) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { - const auto& region = *object->print()->regions()[region_id]; + for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = object->printing_region(region_id); if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; @@ -762,8 +762,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) size_t num_of_copies = object->instances().size(); for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { - const auto& region = *object->print()->regions()[region_id]; + for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { + const auto& region = object->printing_region(region_id); if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index caf827702..77ac3ee7b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -39,22 +39,22 @@ void Print::clear() for (PrintObject *object : m_objects) delete object; m_objects.clear(); - for (PrintRegion *region : m_regions) + for (PrintRegion *region : m_print_regions) delete region; - m_regions.clear(); + m_print_regions.clear(); m_model.clear_objects(); } -PrintRegion* Print::add_region() +PrintRegion* Print::add_print_region() { - m_regions.emplace_back(new PrintRegion()); - return m_regions.back(); + m_print_regions.emplace_back(new PrintRegion()); + return m_print_regions.back(); } -PrintRegion* Print::add_region(const PrintRegionConfig &config) +PrintRegion* Print::add_print_region(const PrintRegionConfig &config) { - m_regions.emplace_back(new PrintRegion(config)); - return m_regions.back(); + m_print_regions.emplace_back(new PrintRegion(config)); + return m_print_regions.back(); } // Called by Print::apply(). @@ -273,15 +273,10 @@ bool Print::is_step_done(PrintObjectStep step) const std::vector Print::object_extruders() const { std::vector extruders; - extruders.reserve(m_regions.size() * 3); - std::vector region_used(m_regions.size(), false); + extruders.reserve(m_print_regions.size() * m_objects.size() * 3); for (const PrintObject *object : m_objects) - for (const std::vector> &volumes_per_region : object->region_volumes) - if (! volumes_per_region.empty()) - region_used[&volumes_per_region - &object->region_volumes.front()] = true; - for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region) - if (region_used[idx_region]) - m_regions[idx_region]->collect_object_printing_extruders(*this, extruders); + for (const auto *region : object->all_regions()) + region->collect_object_printing_extruders(*this, extruders); sort_remove_duplicates(extruders); return extruders; } @@ -451,11 +446,7 @@ std::string Print::validate(std::string* warning) const return L("Only a single object may be printed at a time in Spiral Vase mode. " "Either remove all but the last object, or enable sequential mode by \"complete_objects\"."); assert(m_objects.size() == 1); - size_t num_regions = 0; - for (const std::vector> &volumes_per_region : m_objects.front()->region_volumes) - if (! volumes_per_region.empty()) - ++ num_regions; - if (num_regions > 1) + if (m_objects.front()->all_regions().size() > 1) return L("The Spiral Vase option can only be used when printing single material objects."); } @@ -670,8 +661,8 @@ std::string Print::validate(std::string* warning) const if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg)) return err_msg; for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" }) - for (size_t i = 0; i < object->region_volumes.size(); ++ i) - if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg)) + for (const PrintRegion *region : object->all_regions()) + if (! validate_extrusion_width(region->config(), opt_key, layer_height, err_msg)) return err_msg; } } @@ -746,7 +737,7 @@ Flow Print::brim_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; if (width.value == 0) - width = m_regions.front()->config().perimeter_extrusion_width; + width = m_print_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) width = m_objects.front()->config().extrusion_width; @@ -758,7 +749,7 @@ Flow Print::brim_flow() const return Flow::new_from_config_width( frPerimeter, width, - (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), + (float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().perimeter_extruder-1), (float)this->skirt_first_layer_height()); } @@ -766,7 +757,7 @@ Flow Print::skirt_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; if (width.value == 0) - width = m_regions.front()->config().perimeter_extrusion_width; + width = m_print_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) width = m_objects.front()->config().extrusion_width; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9677a585a..2a9f3fe6e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -148,9 +148,6 @@ private: // Prevents erroneous use by other classes. typedef PrintObjectBaseWithState Inherited; public: - // vector of (layer height ranges and vectors of volume ids), indexed by region_id - std::vector>> region_volumes; - // Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane. const Vec3crd& size() const { return m_size; } const PrintObjectConfig& config() const { return m_config; } @@ -177,9 +174,9 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { - if (region_id >= region_volumes.size()) - region_volumes.resize(region_id + 1); - region_volumes[region_id].emplace_back(layer_range, volume_id); + if (region_id >= m_region_volumes.size()) + m_region_volumes.resize(region_id + 1); + m_region_volumes[region_id].emplace_back(layer_range, volume_id); } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id @@ -210,7 +207,7 @@ public: // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters. // Returns true, if the layer_height_profile was changed. - static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector &layer_height_profile); + static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector &layer_height_profile); // Collect the slicing parameters, to be used by variable layer thickness algorithm, // by the interactive layer height editor and by the printing process itself. @@ -219,6 +216,11 @@ public: const SlicingParameters& slicing_parameters() const { return m_slicing_params; } static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); + size_t num_printing_regions() const throw() { return m_region_volumes.size(); } + const PrintRegion& printing_region(size_t idx) const throw(); + //FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end. + std::vector all_regions() const; + bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; } bool has_raft() const { return m_config.raft_layers > 0; } bool has_support_material() const { return this->has_support() || this->has_raft(); } @@ -293,6 +295,9 @@ private: // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system. Point m_center_offset; + // vector of (layer height ranges and vectors of volume ids), indexed by region_id + std::vector>> m_region_volumes; + SlicingParameters m_slicing_params; LayerPtrs m_layers; SupportLayerPtrs m_support_layers; @@ -392,11 +397,13 @@ class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor }; typedef std::vector PrintRegionPtrs; +/* typedef std::vector ConstPrintRegionPtrs; class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor { friend Print; ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor(data) {} }; +*/ // The complete print tray with possibly multiple objects. class Print : public PrintBaseWithState @@ -467,14 +474,14 @@ public: [object_id](const PrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } - ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); } +// ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); } // How many of PrintObject::copies() over all print objects are there? // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; // For Perl bindings. PrintObjectPtrs& objects_mutable() { return m_objects; } - PrintRegionPtrs& regions_mutable() { return m_regions; } + PrintRegionPtrs& print_regions_mutable() { return m_print_regions; } const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } @@ -496,14 +503,15 @@ public: std::string output_filename(const std::string &filename_base = std::string()) const override; // Accessed by SupportMaterial - const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } - const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing + size_t num_print_regions() const throw() { return m_print_regions.size(); } + const PrintRegion* get_print_region(size_t idx) const { return m_print_regions[idx]; } + const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing protected: // methods for handling regions - PrintRegion* get_region(size_t idx) { return m_regions[idx]; } - PrintRegion* add_region(); - PrintRegion* add_region(const PrintRegionConfig &config); + PrintRegion* get_print_region(size_t idx) { return m_print_regions[idx]; } + PrintRegion* add_print_region(); + PrintRegion* add_print_region(const PrintRegionConfig &config); // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); @@ -524,7 +532,7 @@ private: PrintObjectConfig m_default_object_config; PrintRegionConfig m_default_region_config; PrintObjectPtrs m_objects; - PrintRegionPtrs m_regions; + PrintRegionPtrs m_print_regions; // Ordered collections of extrusion paths to build skirt loops and brim. ExtrusionEntityCollection m_skirt; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index df576fe42..630d3a999 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -383,9 +383,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ delete object; } m_objects.clear(); - for (PrintRegion *region : m_regions) + for (PrintRegion *region : m_print_regions) delete region; - m_regions.clear(); + m_print_regions.clear(); m_model.assign_copy(model); for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::New); @@ -682,21 +682,21 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. // Update reference counts of regions from the remaining PrintObjects and their volumes. // Regions with zero references could and should be reused. - for (PrintRegion *region : m_regions) + for (PrintRegion *region : m_print_regions) region->m_refcnt = 0; for (PrintObject *print_object : m_objects) { int idx_region = 0; - for (const auto &volumes : print_object->region_volumes) { + for (const auto &volumes : print_object->m_region_volumes) { if (! volumes.empty()) - ++ m_regions[idx_region]->m_refcnt; + ++ m_print_regions[idx_region]->m_refcnt; ++ idx_region; } } // All regions now have distinct settings. // Check whether applying the new region config defaults we'd get different regions. - for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { - PrintRegion ®ion = *m_regions[region_id]; + for (size_t region_id = 0; region_id < m_print_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_print_regions[region_id]; PrintRegionConfig this_region_config; bool this_region_config_set = false; for (PrintObject *print_object : m_objects) { @@ -707,8 +707,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status->status != ModelObjectStatus::Deleted); layer_ranges = &it_status->layer_ranges; } - if (region_id < print_object->region_volumes.size()) { - for (const std::pair &volume_and_range : print_object->region_volumes[region_id]) { + if (region_id < print_object->m_region_volumes.size()) { + for (const std::pair &volume_and_range : print_object->m_region_volumes[region_id]) { const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); if (this_region_config_set) { @@ -721,7 +721,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } else { this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); for (size_t i = 0; i < region_id; ++ i) { - const PrintRegion ®ion_other = *m_regions[i]; + const PrintRegion ®ion_other = *m_print_regions[i]; if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) // Regions were merged. Reset this print_object. goto print_object_end; @@ -735,19 +735,19 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(print_object->invalidate_all_steps()); // Decrease the references to regions from this volume. int ireg = 0; - for (const std::vector> &volumes : print_object->region_volumes) { + for (const std::vector> &volumes : print_object->m_region_volumes) { if (! volumes.empty()) - -- m_regions[ireg]->m_refcnt; + -- m_print_regions[ireg]->m_refcnt; ++ ireg; } - print_object->region_volumes.clear(); + print_object->m_region_volumes.clear(); } if (this_region_config_set) { t_config_option_keys diff = region.config().diff(this_region_config); if (! diff.empty()) { // Stop the background process before assigning new configuration to the regions. for (PrintObject *print_object : m_objects) - if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) + if (region_id < print_object->m_region_volumes.size() && ! print_object->m_region_volumes[region_id].empty()) update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff)); region.config_apply_only(this_region_config, diff, false); } @@ -769,7 +769,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ regions_in_object.reserve(64); for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { PrintObject &print_object = *m_objects[i]; - bool fresh = print_object.region_volumes.empty(); + bool fresh = print_object.m_region_volumes.empty(); unsigned int volume_id = 0; unsigned int idx_region_in_object = 0; for (const ModelVolume *volume : model_object.volumes) { @@ -786,11 +786,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders); // Find an existing print region with the same config. int idx_empty_slot = -1; - for (int i = 0; i < (int)m_regions.size(); ++ i) { - if (m_regions[i]->m_refcnt == 0) { + for (int i = 0; i < int(m_print_regions.size()); ++ i) { + if (m_print_regions[i]->m_refcnt == 0) { if (idx_empty_slot == -1) idx_empty_slot = i; - } else if (config.equals(m_regions[i]->config())) { + } else if (config.equals(m_print_regions[i]->config())) { region_id = i; break; } @@ -798,11 +798,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // If no region exists with the same config, create a new one. if (region_id == -1) { if (idx_empty_slot == -1) { - region_id = (int)m_regions.size(); - this->add_region(config); + region_id = int(m_print_regions.size()); + this->add_print_region(config); } else { region_id = idx_empty_slot; - m_regions[region_id]->set_config(std::move(config)); + m_print_regions[region_id]->set_config(std::move(config)); } } regions_in_object.emplace_back(region_id); @@ -810,8 +810,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ region_id = regions_in_object[idx_region_in_object ++]; // Assign volume to a region. if (fresh) { - if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) - ++ m_regions[region_id]->m_refcnt; + if ((size_t)region_id >= print_object.m_region_volumes.size() || print_object.m_region_volumes[region_id].empty()) + ++ m_print_regions[region_id]->m_refcnt; print_object.add_region_volume(region_id, volume_id, it_range->first); } } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 10eaf7c1f..c7339fb5d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -97,6 +97,21 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) return status; } +const PrintRegion& PrintObject::printing_region(size_t idx) const throw() +{ + return *m_print->get_print_region(idx); +} + +std::vector PrintObject::all_regions() const +{ + std::vector out; + out.reserve(m_region_volumes.size()); + for (size_t i = 0; i < m_region_volumes.size(); ++ i) + if (! m_region_volumes[i].empty()) + out.emplace_back(m_print->get_print_region(i)); + return out; +} + // Called by make_perimeters() // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions @@ -173,8 +188,8 @@ void PrintObject::make_perimeters() // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const PrintRegion ®ion = *m_print->regions()[region_id]; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = this->printing_region(region_id); if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) continue; @@ -294,7 +309,7 @@ void PrintObject::prepare_infill() // Debugging output. #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); @@ -313,7 +328,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); @@ -332,7 +347,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); @@ -351,7 +366,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); @@ -736,19 +751,11 @@ bool PrintObject::invalidate_all_steps() // First call the "invalidate" functions, which may cancel background processing. bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); // Then reset some of the depending values. - this->m_slicing_params.valid = false; - this->region_volumes.clear(); + m_slicing_params.valid = false; + m_region_volumes.clear(); return result; } -static const PrintRegion* first_printing_region(const PrintObject &print_object) -{ - for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region) - if (!print_object.region_volumes.empty()) - return print_object.print()->regions()[idx_region]; - return nullptr; -} - // This function analyzes slices of a region (SurfaceCollection slices). // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Initially all slices are of type stInternal. @@ -769,9 +776,9 @@ void PrintObject::detect_surfaces_type() // should be visible. bool spiral_vase = this->print()->config().spiral_vase.value; bool interface_shells = ! spiral_vase && m_config.interface_shells.value; - size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); + size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size(); - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) @@ -966,8 +973,8 @@ void PrintObject::process_external_surfaces() // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only // over voids, which are supported by the layer below. bool has_voids = false; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) - if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) + if (this->printing_region(region_id).config().fill_density == 0) { has_voids = true; break; } @@ -1016,7 +1023,7 @@ void PrintObject::process_external_surfaces() BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; } - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), @@ -1024,7 +1031,7 @@ void PrintObject::process_external_surfaces() for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; - m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces( + m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces( (layer_idx == 0) ? nullptr : m_layers[layer_idx - 1], (layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]); } @@ -1049,7 +1056,7 @@ void PrintObject::discover_vertical_shells() Polygons holes; }; bool spiral_vase = this->print()->config().spiral_vase.value; - size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); + size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size(); coordf_t min_layer_height = this->slicing_parameters().min_layer_height; // Does this region possibly produce more than 1 top or bottom layer? auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) { @@ -1064,14 +1071,14 @@ void PrintObject::discover_vertical_shells() num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0; }; std::vector cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry()); - bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; + bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value; if (top_bottom_surfaces_all_regions) { // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // is calculated over all materials. // Is the "ensure vertical wall thickness" applicable to any region? bool has_extra_layers = false; - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) { - const PrintRegionConfig &config = m_print->get_region(idx_region)->config(); + for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++idx_region) { + const PrintRegionConfig &config = this->printing_region(idx_region).config(); if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) { has_extra_layers = true; break; @@ -1087,7 +1094,7 @@ void PrintObject::discover_vertical_shells() tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; - const size_t num_regions = this->region_volumes.size(); + const size_t num_regions = this->num_printing_regions(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); const Layer &layer = *m_layers[idx_layer]; @@ -1148,10 +1155,10 @@ void PrintObject::discover_vertical_shells() BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++ idx_region) { PROFILE_BLOCK(discover_vertical_shells_region); - const PrintRegion ®ion = *m_print->get_region(idx_region); + const PrintRegion ®ion = this->printing_region(idx_region); if (! region.config().ensure_vertical_shell_thickness.value) // This region will be handled by discover_horizontal_shells(). continue; @@ -1445,8 +1452,8 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const PrintRegion ®ion = *m_print->regions()[region_id]; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = this->printing_region(region_id); // skip bridging in case there are no voids if (region.config().fill_density.value == 100) @@ -1672,10 +1679,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full std::vector PrintObject::object_extruders() const { std::vector extruders; - extruders.reserve(this->region_volumes.size() * 3); - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) - if (! this->region_volumes[idx_region].empty()) - m_print->get_region(idx_region)->collect_object_printing_extruders(*this->print(), extruders); + extruders.reserve(this->all_regions().size() * 3); + for (const PrintRegion *region : this->all_regions()) + region->collect_object_printing_extruders(*this->print(), extruders); sort_remove_duplicates(extruders); return extruders; } @@ -1743,8 +1749,8 @@ void PrintObject::_slice(const std::vector &layer_height_profile) layer->lower_layer = prev; } // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) - layer->add_region(this->print()->get_region(region_id)); + for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) + layer->add_region(this->print()->get_print_region(region_id)); prev = layer; } } @@ -1754,9 +1760,9 @@ void PrintObject::_slice(const std::vector &layer_height_profile) bool has_z_ranges = false; size_t num_volumes = 0; size_t num_modifiers = 0; - for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { + for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) { int last_volume_id = -1; - for (const std::pair &volume_and_range : this->region_volumes[region_id]) { + for (const std::pair &volume_and_range : m_region_volumes[region_id]) { const int volume_id = volume_and_range.second; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; if (model_volume->is_model_part()) { @@ -1786,14 +1792,14 @@ void PrintObject::_slice(const std::vector &layer_height_profile) if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { // Cheap path: Slice regions without mutual clipping. // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; // slicing in parallel size_t slicing_mode_normal_below_layer = 0; if (spiral_vase) { // Slice the bottom layers with SlicingMode::Regular. // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! - const PrintRegionConfig &config = this->print()->regions()[region_id]->config(); + const PrintRegionConfig &config = this->print()->get_print_region(region_id)->config(); slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; ++ slicing_mode_normal_below_layer); @@ -1819,8 +1825,8 @@ void PrintObject::_slice(const std::vector &layer_height_profile) }; std::vector sliced_volumes; sliced_volumes.reserve(num_volumes); - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; + for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { + const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; @@ -1871,7 +1877,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } } // Collect and union volumes of a single region. - for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { + for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) { ExPolygons expolygons; size_t num_volumes = 0; for (SlicedVolume &sliced_volume : sliced_volumes) @@ -1892,8 +1898,8 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } // Slice all modifier volumes. - if (this->region_volumes.size() > 1) { - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + if (m_region_volumes.size() > 1) { + for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; // slicing in parallel std::vector expolygons_by_layer = this->slice_modifiers(region_id, slice_zs); @@ -1906,7 +1912,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) tbb::blocked_range(0, m_layers.size()), [this, &expolygons_by_layer, region_id](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) { + for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) { if (region_id == other_region_id) continue; Layer *layer = m_layers[layer_id]; @@ -2046,8 +2052,8 @@ end: std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const { std::vector volumes; - if (region_id < this->region_volumes.size()) { - for (const std::pair &volume_and_range : this->region_volumes[region_id]) { + if (region_id < m_region_volumes.size()) { + for (const std::pair &volume_and_range : m_region_volumes[region_id]) { const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; if (volume->is_model_part()) volumes.emplace_back(volume); @@ -2060,10 +2066,10 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v std::vector PrintObject::slice_modifiers(size_t region_id, const std::vector &slice_zs) const { std::vector out; - if (region_id < this->region_volumes.size()) + if (region_id < m_region_volumes.size()) { std::vector> volume_ranges; - const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; + const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; volume_ranges.reserve(volumes_and_ranges.size()); for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; @@ -2098,7 +2104,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) { // No modifier in this region was split to layer spans. std::vector volumes; - for (const std::pair &volume_and_range : this->region_volumes[region_id]) { + for (const std::pair &volume_and_range : m_region_volumes[region_id]) { const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; if (volume->is_modifier()) volumes.emplace_back(volume); @@ -2107,8 +2113,8 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std } else { // Some modifier in this region was split to layer spans. std::vector merge; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; + for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { + const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; @@ -2395,9 +2401,15 @@ void PrintObject::simplify_slices(double distance) // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::clip_fill_surfaces() { - if (! m_config.infill_only_where_needed.value || - ! std::any_of(this->print()->regions().begin(), this->print()->regions().end(), - [](const PrintRegion *region) { return region->config().fill_density > 0; })) + if (! m_config.infill_only_where_needed.value) + return; + bool has_infill = false; + for (size_t i = 0; i < this->num_printing_regions(); ++ i) + if (this->printing_region(i).config().fill_density > 0) { + has_infill = true; + break; + } + if (! has_infill) return; // We only want infill under ceilings; this is almost like an @@ -2475,7 +2487,7 @@ void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); Layer *layer = m_layers[i]; @@ -2656,7 +2668,7 @@ void PrintObject::discover_horizontal_shells() } // for each region #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (const Layer *layer : m_layers) { const LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); @@ -2672,16 +2684,16 @@ void PrintObject::discover_horizontal_shells() void PrintObject::combine_infill() { // Work on each region separately. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const PrintRegion *region = this->print()->regions()[region_id]; - const size_t every = region->config().infill_every_layers.value; - if (every < 2 || region->config().fill_density == 0.) + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = this->printing_region(region_id); + const size_t every = region.config().infill_every_layers.value; + if (every < 2 || region.config().fill_density == 0.) continue; // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. //FIXME limit the layer height to max_layer_height double nozzle_diameter = std::min( - this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1), - this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1)); + this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1), + this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1)); // define the combinations std::vector combine(m_layers.size(), 0); { @@ -2745,11 +2757,11 @@ void PrintObject::combine_infill() 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. - ((region->config().fill_pattern == ipRectilinear || - region->config().fill_pattern == ipMonotonic || - region->config().fill_pattern == ipGrid || - region->config().fill_pattern == ipLine || - region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + ((region.config().fill_pattern == ipRectilinear || + region.config().fill_pattern == ipMonotonic || + region.config().fill_pattern == ipGrid || + region.config().fill_pattern == ipLine || + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index ca5657618..63ce31bd9 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -345,17 +345,14 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Evaluate the XY gap between the object outer perimeters and the support structures. // Evaluate the XY gap between the object outer perimeters and the support structures. coordf_t external_perimeter_width = 0.; - size_t num_nonempty_regions = 0; coordf_t bridge_flow_ratio = 0; - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) - if (! object->region_volumes[region_id].empty()) { - ++ num_nonempty_regions; - const PrintRegion ®ion = *object->print()->get_region(region_id); - external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); - bridge_flow_ratio += region.config().bridge_flow_ratio; - } + for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = object->printing_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); + bridge_flow_ratio += region.config().bridge_flow_ratio; + } m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); - bridge_flow_ratio /= num_nonempty_regions; + bridge_flow_ratio /= object->num_printing_regions(); m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index a7b4e562f..6ac387b60 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -98,9 +98,9 @@ _constant() %code%{ RETVAL = THIS->objects().size(); %}; PrintRegionPtrs* regions() - %code%{ RETVAL = const_cast(&THIS->regions_mutable()); %}; + %code%{ RETVAL = const_cast(&THIS->print_regions_mutable()); %}; Ref get_region(int idx) - %code%{ RETVAL = THIS->regions_mutable()[idx]; %}; + %code%{ RETVAL = THIS->print_regions_mutable()[idx]; %}; size_t region_count() %code%{ RETVAL = THIS->regions().size(); %}; From b8db1922f7810d58e26da966d91b09d3f42842cd Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 17:03:11 +0200 Subject: [PATCH 33/79] Fixing perl bindings --- xs/xsp/Print.xsp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 6ac387b60..a6ea590f5 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -37,9 +37,6 @@ _constant() %name{Slic3r::Print::Object} class PrintObject { // owned by Print, no constructor/destructor - int region_count() - %code%{ RETVAL = THIS->print()->regions().size(); %}; - Ref print(); Ref model_object(); Ref config() @@ -99,10 +96,6 @@ _constant() PrintRegionPtrs* regions() %code%{ RETVAL = const_cast(&THIS->print_regions_mutable()); %}; - Ref get_region(int idx) - %code%{ RETVAL = THIS->print_regions_mutable()[idx]; %}; - size_t region_count() - %code%{ RETVAL = THIS->regions().size(); %}; bool step_done(PrintStep step) %code%{ RETVAL = THIS->is_step_done(step); %}; From ee15f00574877a20784b5eeaf974f15558b0526b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 5 May 2021 18:13:58 +0200 Subject: [PATCH 34/79] FDM backend refactoring: Return PrintRegion by reference, not by pointer. Added PrintRegion hashing. --- src/libslic3r/Fill/Fill.cpp | 16 ++++++++-------- src/libslic3r/GCode.cpp | 10 +++++----- src/libslic3r/GCode/ToolOrdering.cpp | 2 +- src/libslic3r/Layer.cpp | 8 ++++---- src/libslic3r/Layer.hpp | 6 +++--- src/libslic3r/LayerRegion.cpp | 16 ++++++++-------- src/libslic3r/Print.cpp | 8 ++++---- src/libslic3r/Print.hpp | 25 +++++++++++++++++-------- src/libslic3r/PrintObject.cpp | 24 ++++++++++++------------ src/libslic3r/SupportMaterial.cpp | 8 ++++---- src/slic3r/GUI/GLCanvas3D.cpp | 10 +++++----- 11 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index c5d00135a..7ba6de7d4 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -122,10 +122,10 @@ std::vector group_fills(const Layer &layer) if (surface.surface_type == stInternalVoid) has_internal_voids = true; else { - const PrintRegionConfig ®ion_config = layerm.region()->config(); + const PrintRegionConfig ®ion_config = layerm.region().config(); FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); bool is_bridge = layer.id() > 0 && surface.is_bridge(); - params.extruder = layerm.region()->extruder(extrusion_role); + params.extruder = layerm.region().extruder(extrusion_role); params.pattern = region_config.fill_pattern.value; params.density = float(region_config.fill_density); @@ -162,7 +162,7 @@ std::vector group_fills(const Layer &layer) } else { // Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status, // so that internall infill will be aligned over all layers of the current region. - params.spacing = layerm.region()->flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing(); + params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing(); // Anchor a sparse infill to inner perimeters with the following anchor length: params.anchor_length = float(region_config.infill_anchor); if (region_config.infill_anchor.percent) @@ -274,11 +274,11 @@ std::vector group_fills(const Layer &layer) } if (internal_solid_fill == nullptr) { // Produce another solid fill. - params.extruder = layerm.region()->extruder(frSolidInfill); - params.pattern = layerm.region()->config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; + params.extruder = layerm.region().extruder(frSolidInfill); + params.pattern = layerm.region().config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; params.density = 100.f; params.extrusion_role = erInternalInfill; - params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); + params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value)); // calculate the actual flow we'll be using for this infill params.flow = layerm.flow(frSolidInfill); params.spacing = params.flow.spacing(); @@ -501,7 +501,7 @@ void Layer::make_ironing() for (LayerRegion *layerm : m_regions) if (! layerm->slices.empty()) { IroningParams ironing_params; - const PrintRegionConfig &config = layerm->region()->config(); + const PrintRegionConfig &config = layerm->region().config(); if (config.ironing && (config.ironing_type == IroningType::AllSolid || (config.top_solid_layers > 0 && @@ -556,7 +556,7 @@ void Layer::make_ironing() Polygons infills; for (size_t k = i; k < j; ++ k) { const IroningParams &ironing_params = by_extruder[k]; - const PrintRegionConfig ®ion_config = ironing_params.layerm->region()->config(); + const PrintRegionConfig ®ion_config = ironing_params.layerm->region().config(); bool iron_everything = region_config.ironing_type == IroningType::AllSolid; bool iron_completely = iron_everything; if (iron_everything) { diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5e5d2957a..ab68f00eb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1113,7 +1113,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { - const PrintRegion ®ion = *print.get_print_region(region_id); + const PrintRegion ®ion = print.get_print_region(region_id); _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); _write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); @@ -1936,7 +1936,7 @@ void GCode::process_layer( bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt()); if (enable) { for (const LayerRegion *layer_region : layer.regions()) - if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() || + if (size_t(layer_region->region().config().bottom_solid_layers.value) > layer.id() || layer_region->perimeters.items_count() > 1u || layer_region->fills.items_count() > 0) { enable = false; @@ -2110,7 +2110,7 @@ void GCode::process_layer( const LayerRegion *layerm = layer.regions()[region_id]; if (layerm == nullptr) continue; - const PrintRegion ®ion = *layerm->region(); + const PrintRegion ®ion = layerm->region(); // Now we must process perimeters and infills and create islands of extrusions in by_region std::map. // It is also necessary to save which extrusions are part of MM wiping and which are not. @@ -2571,7 +2571,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig()); + m_config.apply(print.get_print_region(®ion - &by_region.front()).config()); for (const ExtrusionEntity *ee : region.perimeters) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } @@ -2592,7 +2592,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorrole() == erIroning) == ironing) extrusions.emplace_back(ee); if (! extrusions.empty()) { - m_config.apply(print.get_print_region(®ion - &by_region.front())->config()); + m_config.apply(print.get_print_region(®ion - &by_region.front()).config()); chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); for (const ExtrusionEntity *fill : extrusions) { auto *eec = dynamic_cast(fill); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 8005e57bf..728a26957 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -227,7 +227,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; if (layerm == nullptr) continue; - const PrintRegion ®ion = *layerm->region(); + const PrintRegion ®ion = layerm->region(); if (! layerm->perimeters.entities.empty()) { bool something_nonoverriddable = true; diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index e8e3c4275..c3dcad162 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -102,7 +102,7 @@ ExPolygons Layer::merged(float offset_scaled) const } Polygons polygons; for (LayerRegion *layerm : m_regions) { - const PrintRegionConfig &config = layerm->region()->config(); + const PrintRegionConfig &config = layerm->region().config(); // Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty. if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0) append(polygons, offset(layerm->slices.surfaces, offset_scaled)); @@ -134,7 +134,7 @@ void Layer::make_perimeters() continue; BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; done[region_id] = true; - const PrintRegionConfig &config = (*layerm)->region()->config(); + const PrintRegionConfig &config = (*layerm)->region().config(); // find compatible regions LayerRegionPtrs layerms; @@ -142,7 +142,7 @@ void Layer::make_perimeters() for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) if (! (*it)->slices.empty()) { LayerRegion* other_layerm = *it; - const PrintRegionConfig &other_config = other_layerm->region()->config(); + const PrintRegionConfig &other_config = other_layerm->region().config(); if (config.perimeter_extruder == other_config.perimeter_extruder && config.perimeters == other_config.perimeters && config.perimeter_speed == other_config.perimeter_speed @@ -180,7 +180,7 @@ void Layer::make_perimeters() for (LayerRegion *layerm : layerms) { for (Surface &surface : layerm->slices.surfaces) slices[surface.extra_perimeters].emplace_back(surface); - if (layerm->region()->config().fill_density > layerm_config->region()->config().fill_density) + if (layerm->region().config().fill_density > layerm_config->region().config().fill_density) layerm_config = layerm; } // merge the surfaces assigned to each group diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 2e3e29eab..f2cce4880 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -22,8 +22,8 @@ class LayerRegion public: Layer* layer() { return m_layer; } const Layer* layer() const { return m_layer; } - PrintRegion* region() { return m_region; } - const PrintRegion* region() const { return m_region; } + PrintRegion& region() { return *m_region; } + const PrintRegion& region() const { return *m_region; } // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal @@ -126,7 +126,7 @@ public: std::vector lslices_bboxes; size_t region_count() const { return m_regions.size(); } - const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } + const LayerRegion* get_region(int idx) const { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; } LayerRegion* add_region(PrintRegion* print_region); const LayerRegionPtrs& regions() const { return m_regions; } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index a3b0890d7..356811b74 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -27,7 +27,7 @@ Flow LayerRegion::flow(FlowRole role, double layer_height) const Flow LayerRegion::bridging_flow(FlowRole role) const { - const PrintRegion ®ion = *this->region(); + const PrintRegion ®ion = this->region(); const PrintRegionConfig ®ion_config = region.config(); const PrintObject &print_object = *this->layer()->object(); if (print_object.config().thick_bridges) { @@ -70,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec this->thin_fills.clear(); const PrintConfig &print_config = this->layer()->object()->print()->config(); - const PrintRegionConfig ®ion_config = this->region()->config(); + const PrintRegionConfig ®ion_config = this->region().config(); // This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer! bool spiral_vase = print_config.spiral_vase && //FIXME account for raft layers. @@ -111,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered) { - const bool has_infill = this->region()->config().fill_density.value > 0.; + const bool has_infill = this->region().config().fill_density.value > 0.; const float margin = float(scale_(EXTERNAL_INFILL_MARGIN)); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -285,7 +285,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly #ifdef SLIC3R_DEBUG printf("Processing bridge at layer %zu:\n", this->layer()->id()); #endif - double custom_angle = Geometry::deg2rad(this->region()->config().bridge_angle.value); + double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value); if (bd.detect_angle(custom_angle)) { bridges[idx_last].bridge_angle = bd.angle; if (this->layer()->object()->has_support()) { @@ -384,21 +384,21 @@ void LayerRegion::prepare_fill_surfaces() bool spiral_vase = this->layer()->object()->print()->config().spiral_vase; // if no solid layers are requested, turn top/bottom surfaces to internal - if (! spiral_vase && this->region()->config().top_solid_layers == 0) { + if (! spiral_vase && this->region().config().top_solid_layers == 0) { for (Surface &surface : this->fill_surfaces.surfaces) if (surface.is_top()) surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; } - if (this->region()->config().bottom_solid_layers == 0) { + if (this->region().config().bottom_solid_layers == 0) { for (Surface &surface : this->fill_surfaces.surfaces) if (surface.is_bottom()) // (surface.surface_type == stBottom) surface.surface_type = stInternal; } // turn too small internal regions into solid regions according to the user setting - if (! spiral_vase && this->region()->config().fill_density.value > 0) { + if (! spiral_vase && this->region().config().fill_density.value > 0) { // scaling an area requires two calls! - double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); + double min_area = scale_(scale_(this->region().config().solid_infill_below_area.value)); for (Surface &surface : this->fill_surfaces.surfaces) if (surface.surface_type == stInternal && surface.area() <= min_area) surface.surface_type = stInternalSolid; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 77ac3ee7b..bd1b1f053 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -275,8 +275,8 @@ std::vector Print::object_extruders() const std::vector extruders; extruders.reserve(m_print_regions.size() * m_objects.size() * 3); for (const PrintObject *object : m_objects) - for (const auto *region : object->all_regions()) - region->collect_object_printing_extruders(*this, extruders); + for (const PrintRegion ®ion : object->all_regions()) + region.collect_object_printing_extruders(*this, extruders); sort_remove_duplicates(extruders); return extruders; } @@ -661,8 +661,8 @@ std::string Print::validate(std::string* warning) const if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg)) return err_msg; for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" }) - for (const PrintRegion *region : object->all_regions()) - if (! validate_extrusion_width(region->config(), opt_key, layer_height, err_msg)) + for (const PrintRegion ®ion : object->all_regions()) + if (! validate_extrusion_width(region.config(), opt_key, layer_height, err_msg)) return err_msg; } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 2a9f3fe6e..f3d31896c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -15,6 +15,9 @@ #include "libslic3r.h" +#include +#include + namespace Slic3r { class Print; @@ -59,12 +62,13 @@ class PrintRegion { public: PrintRegion() : m_refcnt(0) {} - PrintRegion(const PrintRegionConfig &config) : m_refcnt(0), m_config(config) {} + PrintRegion(const PrintRegionConfig &config) : m_refcnt(0), m_config(config), m_config_hash(config.hash()) {} ~PrintRegion() = default; // Methods NOT modifying the PrintRegion's state: public: - const PrintRegionConfig& config() const { return m_config; } + const PrintRegionConfig& config() const throw() { return m_config; } + size_t config_hash() const throw() { return m_config_hash; } // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; @@ -79,10 +83,10 @@ public: // Methods modifying the PrintRegion's state: public: - void set_config(const PrintRegionConfig &config) { m_config = config; } - void set_config(PrintRegionConfig &&config) { m_config = std::move(config); } + void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); } + void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) - { this->m_config.apply_only(other, keys, ignore_nonexistent); } + { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); } protected: friend Print; @@ -90,8 +94,12 @@ protected: private: PrintRegionConfig m_config; + size_t m_config_hash; }; +inline bool operator==(const PrintRegion &lhs, const PrintRegion &rhs) { return lhs.config_hash() == rhs.config_hash() && lhs.config() == rhs.config(); } +inline bool operator!=(const PrintRegion &lhs, const PrintRegion &rhs) { return ! (lhs == rhs); } + template class ConstVectorOfPtrsAdaptor { public: @@ -219,7 +227,7 @@ public: size_t num_printing_regions() const throw() { return m_region_volumes.size(); } const PrintRegion& printing_region(size_t idx) const throw(); //FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end. - std::vector all_regions() const; + std::vector> all_regions() const; bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; } bool has_raft() const { return m_config.raft_layers > 0; } @@ -295,6 +303,7 @@ private: // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system. Point m_center_offset; + std::set m_map_regions; // vector of (layer height ranges and vectors of volume ids), indexed by region_id std::vector>> m_region_volumes; @@ -504,12 +513,12 @@ public: // Accessed by SupportMaterial size_t num_print_regions() const throw() { return m_print_regions.size(); } - const PrintRegion* get_print_region(size_t idx) const { return m_print_regions[idx]; } + const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing protected: // methods for handling regions - PrintRegion* get_print_region(size_t idx) { return m_print_regions[idx]; } + PrintRegion& get_print_region(size_t idx) { return *m_print_regions[idx]; } PrintRegion* add_print_region(); PrintRegion* add_print_region(const PrintRegionConfig &config); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c7339fb5d..c96fe28cf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -99,12 +99,12 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) const PrintRegion& PrintObject::printing_region(size_t idx) const throw() { - return *m_print->get_print_region(idx); + return m_print->get_print_region(idx); } -std::vector PrintObject::all_regions() const +std::vector> PrintObject::all_regions() const { - std::vector out; + std::vector> out; out.reserve(m_region_volumes.size()); for (size_t i = 0; i < m_region_volumes.size(); ++ i) if (! m_region_volumes[i].empty()) @@ -1010,7 +1010,7 @@ void PrintObject::process_external_surfaces() m_print->throw_if_canceled(); Polygons voids; for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) { - if (layerm->region()->config().fill_density.value == 0.) + if (layerm->region().config().fill_density.value == 0.) for (const Surface &surface : layerm->fill_surfaces.surfaces) // Shrink the holes, let the layer above expand slightly inside the unsupported areas. polygons_append(voids, offset(surface.expolygon, unsupported_width)); @@ -1120,7 +1120,7 @@ void PrintObject::discover_vertical_shells() unsigned int perimeters = 0; for (Surface &s : layerm.slices.surfaces) perimeters = std::max(perimeters, s.extra_perimeters); - perimeters += layerm.region()->config().perimeters.value; + perimeters += layerm.region().config().perimeters.value; // Then calculate the infill offset. if (perimeters > 0) { Flow extflow = layerm.flow(frExternalPerimeter); @@ -1216,7 +1216,7 @@ void PrintObject::discover_vertical_shells() Layer *layer = m_layers[idx_layer]; LayerRegion *layerm = layer->m_regions[idx_region]; - const PrintRegionConfig ®ion_config = layerm->region()->config(); + const PrintRegionConfig ®ion_config = layerm->region().config(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); @@ -1680,8 +1680,8 @@ std::vector PrintObject::object_extruders() const { std::vector extruders; extruders.reserve(this->all_regions().size() * 3); - for (const PrintRegion *region : this->all_regions()) - region->collect_object_printing_extruders(*this->print(), extruders); + for (const PrintRegion ®ion : this->all_regions()) + region.collect_object_printing_extruders(*this->print(), extruders); sort_remove_duplicates(extruders); return extruders; } @@ -1750,7 +1750,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } // Make sure all layers contain layer region objects for all regions. for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) - layer->add_region(this->print()->get_print_region(region_id)); + layer->add_region(&this->print()->get_print_region(region_id)); prev = layer; } } @@ -1799,7 +1799,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) if (spiral_vase) { // Slice the bottom layers with SlicingMode::Regular. // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! - const PrintRegionConfig &config = this->print()->get_print_region(region_id)->config(); + const PrintRegionConfig &config = this->print()->get_print_region(region_id).config(); slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; ++ slicing_mode_normal_below_layer); @@ -2462,7 +2462,7 @@ void PrintObject::clip_fill_surfaces() upper_internal = intersection(overhangs, lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { - if (layerm->region()->config().fill_density.value == 0) + if (layerm->region().config().fill_density.value == 0) continue; SurfaceType internal_surface_types[] = { stInternal, stInternalVoid }; Polygons internal; @@ -2492,7 +2492,7 @@ void PrintObject::discover_horizontal_shells() m_print->throw_if_canceled(); Layer *layer = m_layers[i]; LayerRegion *layerm = layer->regions()[region_id]; - const PrintRegionConfig ®ion_config = layerm->region()->config(); + const PrintRegionConfig ®ion_config = layerm->region().config(); if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && (i % region_config.solid_infill_every_layers) == 0) { // Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge. diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 63ce31bd9..5b6cbd6bc 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1251,7 +1251,7 @@ namespace SupportMaterialInternal { // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. Polygons lower_grown_slices = offset(lower_layer_polygons, //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. - 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))), + 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region().config().perimeter_extruder-1))), SUPPORT_SURFACES_OFFSET_PARAMETERS); // Collect perimeters of this layer. //FIXME split_at_first_point() could split a bridge mid-way @@ -1637,7 +1637,7 @@ static inline std::pairregion()->bridging_height_avg(print_config); + bridging_height += region->region().bridging_height_avg(print_config); bridging_height /= coordf_t(layer.regions().size()); coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object; if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) { @@ -2764,13 +2764,13 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; for (LayerRegion *region : object_layer.regions()) { - coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config); + coordf_t bridging_height = region->region().bridging_height_avg(*this->m_print_config); if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; polygons_append(polygons_trimming, offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (region->region()->config().overhangs.value) + if (region->region().config().overhangs.value) // Add bridging perimeters. SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6dcd30c29..c38ed3dcd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5935,7 +5935,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c { if (layerm->slices.surfaces.empty()) continue; - const PrintRegionConfig& cfg = layerm->region()->config(); + const PrintRegionConfig& cfg = layerm->region().config(); if (cfg.perimeter_extruder.value == m_selected_extruder || cfg.infill_extruder.value == m_selected_extruder || cfg.solid_infill_extruder.value == m_selected_extruder ) { @@ -5958,7 +5958,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c for (const LayerRegion *layerm : layer->regions()) { if (is_selected_separate_extruder) { - const PrintRegionConfig& cfg = layerm->region()->config(); + const PrintRegionConfig& cfg = layerm->region().config(); if (cfg.perimeter_extruder.value != m_selected_extruder || cfg.infill_extruder.value != m_selected_extruder || cfg.solid_infill_extruder.value != m_selected_extruder) @@ -5966,7 +5966,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } if (ctxt.has_perimeters) _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - volume(idx_layer, layerm->region()->config().perimeter_extruder.value, 0)); + volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); if (ctxt.has_infill) { for (const ExtrusionEntity *ee : layerm->fills.entities) { // fill represents infill extrusions of a single island. @@ -5975,8 +5975,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, volume(idx_layer, is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config().solid_infill_extruder : - layerm->region()->config().infill_extruder, + layerm->region().config().solid_infill_extruder : + layerm->region().config().infill_extruder, 1)); } } From 123c5af34743e5a639f862936ae4af46689fecda Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 May 2021 10:11:53 +0200 Subject: [PATCH 35/79] #6473 - Update titlebar when opening a project file by double-clicking the file icon --- src/slic3r/GUI/GUI_App.cpp | 14 ++++++++++++-- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f18a1fbe4..62961a867 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -633,8 +634,17 @@ void GUI_App::post_init() //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. this->mainframe->load_config_file(this->init_params->load_configs.back()); // If loading a 3MF file, the config is loaded from the last one. - if (! this->init_params->input_files.empty()) - this->plater()->load_files(this->init_params->input_files, true, true); + if (!this->init_params->input_files.empty()) { + const std::vector res = this->plater()->load_files(this->init_params->input_files, true, true); + if (!res.empty() && this->init_params->input_files.size() == 1) { + // Update application titlebar when opening a project file + const std::string& filename = this->init_params->input_files.front(); + if (boost::algorithm::iends_with(filename, ".amf") || + boost::algorithm::iends_with(filename, ".amf.xml") || + boost::algorithm::iends_with(filename, ".3mf")) + this->plater()->set_project_filename(filename); + } + } if (! this->init_params->extra_config.empty()) this->mainframe->load_config(this->init_params->extra_config); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 841a07513..60f829728 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5722,7 +5722,7 @@ wxString Plater::get_project_filename(const wxString& extension) const void Plater::set_project_filename(const wxString& filename) { - return p->set_project_filename(filename); + p->set_project_filename(filename); } bool Plater::is_export_gcode_scheduled() const From 0ca6b12da116bf59d0534d4f26637d8d1ed6636b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 13:00:57 +0200 Subject: [PATCH 36/79] Print/PrintObject/PrintRegion refactoring: Newly the PrintObjects own PrintRegions and Print contains references to PrintRegions owned by PrintObjects, so that a PrintRegion of the same content is referenced by Print only once. The refactoring is a WIP to support multi-material painting. --- src/libslic3r/GCode.cpp | 6 +- src/libslic3r/Print.cpp | 17 +-- src/libslic3r/Print.hpp | 24 ++-- src/libslic3r/PrintApply.cpp | 212 ++++++++++++++++------------------ src/libslic3r/PrintObject.cpp | 12 +- 5 files changed, 122 insertions(+), 149 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ab68f00eb..01740ca7b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2110,7 +2110,9 @@ void GCode::process_layer( const LayerRegion *layerm = layer.regions()[region_id]; if (layerm == nullptr) continue; - const PrintRegion ®ion = layerm->region(); + // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not + // identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion. + const PrintRegion ®ion = print.get_print_region(layerm->region().print_region_id()); // Now we must process perimeters and infills and create islands of extrusions in by_region std::map. // It is also necessary to save which extrusions are part of MM wiping and which are not. @@ -2169,7 +2171,7 @@ void GCode::process_layer( point_inside_surface(island_idx, extrusions->first_point())) { if (islands[island_idx].by_region.empty()) islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region()); - islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides); + islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides); break; } } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index bd1b1f053..d0235c40e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -31,6 +31,9 @@ namespace Slic3r { template class PrintState; template class PrintState; +PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {} +PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {} + void Print::clear() { tbb::mutex::scoped_lock lock(this->state_mutex()); @@ -39,24 +42,10 @@ void Print::clear() for (PrintObject *object : m_objects) delete object; m_objects.clear(); - for (PrintRegion *region : m_print_regions) - delete region; m_print_regions.clear(); m_model.clear_objects(); } -PrintRegion* Print::add_print_region() -{ - m_print_regions.emplace_back(new PrintRegion()); - return m_print_regions.back(); -} - -PrintRegion* Print::add_print_region(const PrintRegionConfig &config) -{ - m_print_regions.emplace_back(new PrintRegion(config)); - return m_print_regions.back(); -} - // Called by Print::apply(). // This method only accepts PrintConfig option keys. bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector &opt_keys) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index f3d31896c..d341b7eec 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -61,14 +61,19 @@ enum PrintObjectStep { class PrintRegion { public: - PrintRegion() : m_refcnt(0) {} - PrintRegion(const PrintRegionConfig &config) : m_refcnt(0), m_config(config), m_config_hash(config.hash()) {} + PrintRegion() = default; + PrintRegion(const PrintRegionConfig &config); + PrintRegion(const PrintRegionConfig &config, const size_t config_hash) : m_config(config), m_config_hash(config_hash) {} + PrintRegion(PrintRegionConfig &&config); + PrintRegion(PrintRegionConfig &&config, const size_t config_hash) : m_config(std::move(config)), m_config_hash(config_hash) {} ~PrintRegion() = default; // Methods NOT modifying the PrintRegion's state: public: const PrintRegionConfig& config() const throw() { return m_config; } size_t config_hash() const throw() { return m_config_hash; } + // Identifier of this PrintRegion in the list of Print::m_print_regions. + int print_region_id() const throw() { return m_print_region_id; } // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; @@ -87,14 +92,11 @@ public: void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); } - -protected: - friend Print; - size_t m_refcnt; - private: + friend Print; PrintRegionConfig m_config; size_t m_config_hash; + int m_print_region_id = -1; }; inline bool operator==(const PrintRegion &lhs, const PrintRegion &rhs) { return lhs.config_hash() == rhs.config_hash() && lhs.config() == rhs.config(); } @@ -224,8 +226,8 @@ public: const SlicingParameters& slicing_parameters() const { return m_slicing_params; } static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); - size_t num_printing_regions() const throw() { return m_region_volumes.size(); } - const PrintRegion& printing_region(size_t idx) const throw(); + size_t num_printing_regions() const throw() { return m_all_regions.size(); } + const PrintRegion& printing_region(size_t idx) const throw() { return *m_all_regions[idx]; } //FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end. std::vector> all_regions() const; @@ -303,7 +305,7 @@ private: // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system. Point m_center_offset; - std::set m_map_regions; + std::vector> m_all_regions; // vector of (layer height ranges and vectors of volume ids), indexed by region_id std::vector>> m_region_volumes; @@ -519,8 +521,6 @@ public: protected: // methods for handling regions PrintRegion& get_print_region(size_t idx) { return *m_print_regions[idx]; } - PrintRegion* add_print_region(); - PrintRegion* add_print_region(const PrintRegionConfig &config); // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 630d3a999..3679080e6 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -383,9 +383,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ delete object; } m_objects.clear(); - for (PrintRegion *region : m_print_regions) - delete region; - m_print_regions.clear(); m_model.assign_copy(model); for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::New); @@ -598,6 +595,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // 4) Generate PrintObjects from ModelObjects and their instances. + bool print_regions_reshuffled = false; { PrintObjectPtrs print_objects_new; print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); @@ -675,87 +673,72 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); if (new_objects) update_apply_status(false); + print_regions_reshuffled = true; } print_object_status.clear(); } - // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions. - // Update reference counts of regions from the remaining PrintObjects and their volumes. - // Regions with zero references could and should be reused. - for (PrintRegion *region : m_print_regions) - region->m_refcnt = 0; - for (PrintObject *print_object : m_objects) { - int idx_region = 0; - for (const auto &volumes : print_object->m_region_volumes) { - if (! volumes.empty()) - ++ m_print_regions[idx_region]->m_refcnt; - ++ idx_region; - } - } - // All regions now have distinct settings. // Check whether applying the new region config defaults we'd get different regions. - for (size_t region_id = 0; region_id < m_print_regions.size(); ++ region_id) { - PrintRegion ®ion = *m_print_regions[region_id]; - PrintRegionConfig this_region_config; - bool this_region_config_set = false; - for (PrintObject *print_object : m_objects) { - const LayerRanges *layer_ranges; - { - auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); - assert(it_status != model_object_status.end()); - assert(it_status->status != ModelObjectStatus::Deleted); - layer_ranges = &it_status->layer_ranges; - } - if (region_id < print_object->m_region_volumes.size()) { - for (const std::pair &volume_and_range : print_object->m_region_volumes[region_id]) { - const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; - const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); - if (this_region_config_set) { - // If the new config for this volume differs from the other - // volume configs currently associated to this region, it means - // the region subdivision does not make sense anymore. - if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders))) - // Regions were split. Reset this print_object. - goto print_object_end; - } else { - this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); - for (size_t i = 0; i < region_id; ++ i) { - const PrintRegion ®ion_other = *m_print_regions[i]; - if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) - // Regions were merged. Reset this print_object. - goto print_object_end; - } - this_region_config_set = true; + for (PrintObject *print_object : m_objects) { + const LayerRanges *layer_ranges; + { + auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); + assert(it_status != model_object_status.end()); + assert(it_status->status != ModelObjectStatus::Deleted); + layer_ranges = &it_status->layer_ranges; + } + bool some_object_region_modified = false; + bool regions_merged = false; + for (size_t region_id = 0; region_id < print_object->m_region_volumes.size(); ++ region_id) { + PrintRegion ®ion = *print_object->m_all_regions[region_id]; + PrintRegionConfig region_config; + bool region_config_set = false; + for (const std::pair &volume_and_range : print_object->m_region_volumes[region_id]) { + const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; + const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); + PrintRegionConfig this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); + if (region_config_set) { + if (this_region_config != region_config) { + regions_merged = true; + break; } + } else { + region_config = std::move(this_region_config); + region_config_set = true; } } - continue; - print_object_end: - update_apply_status(print_object->invalidate_all_steps()); - // Decrease the references to regions from this volume. - int ireg = 0; - for (const std::vector> &volumes : print_object->m_region_volumes) { - if (! volumes.empty()) - -- m_print_regions[ireg]->m_refcnt; - ++ ireg; - } - print_object->m_region_volumes.clear(); - } - if (this_region_config_set) { - t_config_option_keys diff = region.config().diff(this_region_config); - if (! diff.empty()) { + if (regions_merged) + break; + size_t region_config_hash = region_config.hash(); + bool modified = region.config_hash() != region_config_hash || region.config() != region_config; + some_object_region_modified |= modified; + if (some_object_region_modified) + // Verify whether this region was not merged with some other region. + for (size_t i = 0; i < region_id; ++ i) { + const PrintRegion ®ion_other = *print_object->m_all_regions[i]; + if (region_other.config_hash() == region_config_hash && region_other.config() == region_config) { + // Regions were merged. Reset this print_object. + regions_merged = true; + break; + } + } + if (modified) { // Stop the background process before assigning new configuration to the regions. - for (PrintObject *print_object : m_objects) - if (region_id < print_object->m_region_volumes.size() && ! print_object->m_region_volumes[region_id].empty()) - update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff)); - region.config_apply_only(this_region_config, diff, false); + t_config_option_keys diff = region.config().diff(region_config); + update_apply_status(print_object->invalidate_state_by_config_options(region.config(), region_config, diff)); + region.config_apply_only(region_config, diff, false); } } + if (regions_merged) { + // Two regions of a single object were either split or merged. This invalidates the whole slicing. + update_apply_status(print_object->invalidate_all_steps()); + print_object->m_region_volumes.clear(); + } } // Possibly add new regions for the newly added or resetted PrintObjects. - for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) { + for (size_t idx_print_object = 0; idx_print_object < m_objects.size();) { PrintObject &print_object0 = *m_objects[idx_print_object]; const ModelObject &model_object = *print_object0.model_object(); const LayerRanges *layer_ranges; @@ -765,59 +748,64 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status->status != ModelObjectStatus::Deleted); layer_ranges = &it_status->layer_ranges; } - std::vector regions_in_object; - regions_in_object.reserve(64); - for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) { - PrintObject &print_object = *m_objects[i]; - bool fresh = print_object.m_region_volumes.empty(); + if (print_object0.m_region_volumes.empty()) { + // Fresh or completely invalidated print_object. Assign regions. unsigned int volume_id = 0; - unsigned int idx_region_in_object = 0; for (const ModelVolume *volume : model_object.volumes) { if (! volume->is_model_part() && ! volume->is_modifier()) { - ++ volume_id; - continue; - } + ++ volume_id; + continue; + } // Filter the layer ranges, so they do not overlap and they contain at least a single layer. // Now insert a volume with a layer range to its own region. for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) { int region_id = -1; - if (&print_object == &print_object0) { - // Get the config applied to this volume. - PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders); - // Find an existing print region with the same config. - int idx_empty_slot = -1; - for (int i = 0; i < int(m_print_regions.size()); ++ i) { - if (m_print_regions[i]->m_refcnt == 0) { - if (idx_empty_slot == -1) - idx_empty_slot = i; - } else if (config.equals(m_print_regions[i]->config())) { - region_id = i; - break; - } - } - // If no region exists with the same config, create a new one. - if (region_id == -1) { - if (idx_empty_slot == -1) { - region_id = int(m_print_regions.size()); - this->add_print_region(config); - } else { - region_id = idx_empty_slot; - m_print_regions[region_id]->set_config(std::move(config)); - } + // Get the config applied to this volume. + PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders); + size_t hash = config.hash(); + for (size_t i = 0; i < print_object0.m_all_regions.size(); ++ i) + if (hash == print_object0.m_all_regions[i]->config_hash() && config == *print_object0.m_all_regions[i]) { + region_id = int(i); + break; } - regions_in_object.emplace_back(region_id); - } else - region_id = regions_in_object[idx_region_in_object ++]; - // Assign volume to a region. - if (fresh) { - if ((size_t)region_id >= print_object.m_region_volumes.size() || print_object.m_region_volumes[region_id].empty()) - ++ m_print_regions[region_id]->m_refcnt; - print_object.add_region_volume(region_id, volume_id, it_range->first); - } + // If no region exists with the same config, create a new one. + if (region_id == -1) { + region_id = int(print_object0.m_all_regions.size()); + print_object0.m_all_regions.emplace_back(std::make_unique(std::move(config), hash)); + } + print_object0.add_region_volume(region_id, volume_id, it_range->first); } - ++ volume_id; - } + ++ volume_id; + } + print_regions_reshuffled = true; } + for (++ idx_print_object; idx_print_object < m_objects.size() && m_objects[idx_print_object]->model_object() == &model_object; ++ idx_print_object) { + PrintObject &print_object = *m_objects[idx_print_object]; + if (print_object.m_region_volumes.empty()) { + // Copy region volumes and regions from print_object0. + print_object.m_region_volumes = print_object0.m_region_volumes; + print_object.m_all_regions.reserve(print_object0.m_all_regions.size()); + for (const std::unique_ptr ®ion : print_object0.m_all_regions) + print_object.m_all_regions.emplace_back(std::make_unique(*region)); + print_regions_reshuffled = true; + } + } + } + + if (print_regions_reshuffled) { + // Update Print::m_print_regions from objects. + struct cmp { bool operator() (const PrintRegion *l, const PrintRegion *r) const { return l->config_hash() == r->config_hash() && l->config() == r->config(); } }; + std::set region_set; + m_print_regions.clear(); + for (PrintObject *print_object : m_objects) + for (std::unique_ptr &print_region : print_object->m_all_regions) + if (auto it = region_set.find(print_region.get()); it == region_set.end()) { + int print_region_id = int(m_print_regions.size()); + m_print_regions.emplace_back(print_region.get()); + print_region->m_print_region_id = print_region_id; + } else { + print_region->m_print_region_id = (*it)->print_region_id(); + } } // Update SlicingParameters for each object where the SlicingParameters is not valid. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c96fe28cf..2986ea511 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -97,18 +97,12 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) return status; } -const PrintRegion& PrintObject::printing_region(size_t idx) const throw() -{ - return m_print->get_print_region(idx); -} - std::vector> PrintObject::all_regions() const { std::vector> out; - out.reserve(m_region_volumes.size()); - for (size_t i = 0; i < m_region_volumes.size(); ++ i) - if (! m_region_volumes[i].empty()) - out.emplace_back(m_print->get_print_region(i)); + out.reserve(m_all_regions.size()); + for (size_t i = 0; i < m_all_regions.size(); ++ i) + out.emplace_back(*m_all_regions[i]); return out; } From 1c6333e5577ce9b125345a3d8900dbd4ec2ae5a9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 13:58:37 +0200 Subject: [PATCH 37/79] Fixing Perl integration --- xs/xsp/Layer.xsp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 50ddfd9a1..b97e340bd 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -10,7 +10,8 @@ // owned by Layer, no constructor/destructor Ref layer(); - Ref region(); + Ref region() + %code%{ RETVAL = &THIS->region(); %}; Ref slices() %code%{ RETVAL = &THIS->slices; %}; From ff632a9ff242db41bfebf34aff1f53039bf888fc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 May 2021 14:04:07 +0200 Subject: [PATCH 38/79] Tech ENABLE_ALLOW_NEGATIVE_Z-> Disable sinking objects for SLA printer --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 7 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++++ src/slic3r/GUI/Plater.cpp | 4 ++++ src/slic3r/GUI/Selection.cpp | 17 ++++++++++++----- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 369996fba..316cff195 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -61,6 +61,7 @@ #define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) // Enable to push object instances under the bed #define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0) +#define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index c64c36ea8..00bad52ff 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -23,6 +23,9 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/AppConfig.hpp" +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA +#include "libslic3r/PresetBundle.hpp" +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #include #include @@ -521,7 +524,11 @@ bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int( #if ENABLE_ALLOW_NEGATIVE_Z bool GLVolume::is_sinking() const { +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (is_modifier || GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) +#else if (is_modifier) +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA return false; const BoundingBoxf3& box = transformed_convex_hull_bounding_box(); return box.min(2) < -EPSILON && box.max(2) >= -EPSILON; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cc8658fc2..c8e402a5d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3571,7 +3571,11 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) ModelObject* m = m_model->objects[i.first]; #if ENABLE_ALLOW_NEGATIVE_Z double shift_z = m->get_instance_min_z(i.second); +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (m_process->current_printer_technology() == ptSLA || shift_z > 0.0) { +#else if (shift_z > 0.0) { +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA Vec3d shift(0.0, 0.0, -shift_z); #else Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 32ecef438..ab1d657e4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5816,7 +5816,11 @@ void Plater::changed_object(int obj_idx) // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; #if ENABLE_ALLOW_NEGATIVE_Z +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + model_object->ensure_on_bed(this->p->printer_technology != ptSLA); +#else model_object->ensure_on_bed(true); +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #else model_object->ensure_on_bed(); #endif // ENABLE_ALLOW_NEGATIVE_Z diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 8d7878650..13512d534 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -12,6 +12,9 @@ #include "Plater.hpp" #include "libslic3r/Model.hpp" +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA +#include "libslic3r/PresetBundle.hpp" +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #include @@ -893,10 +896,11 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH - -#if !ENABLE_ALLOW_NEGATIVE_Z - ensure_on_bed(); -#endif // !ENABLE_ALLOW_NEGATIVE_Z + +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) + ensure_on_bed(); +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA this->set_bounding_boxes_dirty(); } @@ -2059,7 +2063,10 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ // z only rotation -> synch instance z // The X,Y rotations should be synchronized from start to end of the rotation. assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); - v->set_instance_offset(Z, volume->get_instance_offset().z()); +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + v->set_instance_offset(Z, volume->get_instance_offset().z()); break; #else // z only rotation -> keep instance z From f16d4953be032cf1d2e229d43e46a3f9cd013dde Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 14:29:20 +0200 Subject: [PATCH 39/79] Removing offset2 from Perl bindings and other minor cleanup. --- lib/Slic3r/Geometry/Clipper.pm | 2 +- lib/Slic3r/Print/Object.pm | 2 +- src/libslic3r/Geometry.cpp | 3 +-- src/libslic3r/PNGReadWrite.cpp | 1 + .../test_elephant_foot_compensation.cpp | 16 ---------------- xs/xsp/Clipper.xsp | 12 ------------ 6 files changed, 4 insertions(+), 32 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 0c35c93d9..b7a7da772 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -5,7 +5,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( - offset offset2 + offset offset_ex offset2_ex diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER JT_SQUARE diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 7370881ea..0385f88b8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -7,7 +7,7 @@ use List::Util qw(min max sum first); use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(scale epsilon); use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex - offset offset2 offset_ex offset2_ex JT_MITER); + offset offset_ex offset2_ex JT_MITER); use Slic3r::Print::State ':steps'; use Slic3r::Surface ':types'; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index d5ef41125..e60eb01b6 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1083,8 +1083,7 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol } } -bool -MedialAxis::validate_edge(const VD::edge_type* edge) +bool MedialAxis::validate_edge(const VD::edge_type* edge) { // prevent overflows and detect almost-infinite edges #ifndef CLIPPERLIB_INT32 diff --git a/src/libslic3r/PNGReadWrite.cpp b/src/libslic3r/PNGReadWrite.cpp index 3308f1fd4..51bf7de7c 100644 --- a/src/libslic3r/PNGReadWrite.cpp +++ b/src/libslic3r/PNGReadWrite.cpp @@ -103,6 +103,7 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img) // Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes. // Based on https://www.lemoda.net/c/write-png/ // png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY +//FIXME maybe better to use tdefl_write_image_to_png_file_in_memory() instead? static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data) { bool result = false; diff --git a/tests/libslic3r/test_elephant_foot_compensation.cpp b/tests/libslic3r/test_elephant_foot_compensation.cpp index a1c23f4a9..09ad33f41 100644 --- a/tests/libslic3r/test_elephant_foot_compensation.cpp +++ b/tests/libslic3r/test_elephant_foot_compensation.cpp @@ -15,22 +15,6 @@ using namespace Slic3r; namespace Slic3r { ClipperLib::Path mittered_offset_path_scaled(const Points& contour, const std::vector& deltas, double miter_limit); - -#if 0 - static Points mittered_offset_path_scaled_points(const Points& contour, const std::vector& deltas, double miter_limit) - { - Points out; - ClipperLib::Path scaled = mittered_offset_path_scaled(contour, deltas, miter_limit); - for (ClipperLib::IntPoint& pt : scaled) { - pt.X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pt.Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pt.X >>= CLIPPER_OFFSET_POWER_OF_2; - pt.Y >>= CLIPPER_OFFSET_POWER_OF_2; - out.emplace_back(coord_t(pt.x()), coord_t(pt.y())); - } - return out; - } -#endif } static ExPolygon spirograph_gear_1mm() diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 40ee6480b..bae6a103e 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -41,18 +41,6 @@ offset_ex(polygons, delta, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = OUTPUT: RETVAL -Polygons -offset2(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3) - Polygons polygons - const float delta1 - const float delta2 - Slic3r::ClipperLib::JoinType joinType - double miterLimit - CODE: - RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit); - OUTPUT: - RETVAL - ExPolygons offset2_ex(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3) Polygons polygons From b5573f959b2f830516617abd1123501f3e28b31f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 14:43:36 +0200 Subject: [PATCH 40/79] Refactoring for code clarity: Replaced this->m_xxx with m_xxx as the m_ prefix already signifies a class local variable. --- src/libslic3r/EdgeGrid.cpp | 2 +- src/libslic3r/GCode/WipeTower.cpp | 8 ++++---- src/libslic3r/GCode/WipeTower.hpp | 7 +++---- src/libslic3r/Model.cpp | 4 ++-- src/libslic3r/Model.hpp | 8 ++++---- src/libslic3r/MutablePolygon.hpp | 10 +++++----- src/libslic3r/Optimize/NLoptOptimizer.hpp | 4 ++-- src/libslic3r/Preset.cpp | 4 ++-- src/libslic3r/Print.hpp | 4 ++-- src/libslic3r/PrintObject.cpp | 6 +++--- src/libslic3r/SLAPrint.hpp | 2 +- src/libslic3r/SupportMaterial.cpp | 6 +++--- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 8 ++++---- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/OptionsGroup.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 8 ++++---- src/slic3r/GUI/PresetComboBoxes.cpp | 10 +++++----- src/slic3r/GUI/Tab.cpp | 6 +++--- src/slic3r/Utils/UndoRedo.cpp | 4 ++-- xs/xsp/Print.xsp | 1 - 20 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index b9e9ec3dd..1385a51d8 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -546,7 +546,7 @@ bool EdgeGrid::Grid::inside(const Point &pt_src) return false; coord_t ix = p(0) / m_resolution; coord_t iy = p(1) / m_resolution; - if (ix >= this->m_cols || iy >= this->m_rows) + if (ix >= m_cols || iy >= m_rows) return false; size_t i_closest = (size_t)-1; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index ae800a5ff..fc6a15b65 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -500,9 +500,9 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer, ToolChangeResult result; result.priming = priming; result.initial_tool = int(old_tool); - result.new_tool = int(this->m_current_tool); - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; + result.new_tool = int(m_current_tool); + result.print_z = m_z_pos; + result.layer_height = m_layer_height; result.elapsed_time = writer.elapsed_time(); result.start_pos = writer.start_pos_rotated(); result.end_pos = priming ? writer.pos() : writer.pos_rotated(); @@ -630,7 +630,7 @@ std::vector WipeTower::prime( bool /*last_wipe_inside_wipe_tower*/) { this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false); - this->m_current_tool = tools.front(); + m_current_tool = tools.front(); // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210]. // Due to the XYZ calibration, this working space may shrink slightly from all directions, diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 6fd7a8e21..b0c5111aa 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -164,10 +164,9 @@ public: m_current_layer_finished = false; m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL; if (is_first_layer) { - this->m_num_layer_changes = 0; - this->m_num_tool_changes = 0; - } - else + m_num_layer_changes = 0; + m_num_tool_changes = 0; + } else ++ m_num_layer_changes; // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height: diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 8b829fc13..bfad61a90 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1813,7 +1813,7 @@ void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_le this->set_mesh(std::move(mesh)); TriangleMesh convex_hull = this->get_convex_hull(); convex_hull.transform(mesh_trafo, fix_left_handed); - this->m_convex_hull = std::make_shared(std::move(convex_hull)); + m_convex_hull = std::make_shared(std::move(convex_hull)); // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. this->set_new_unique_id(); } @@ -1825,7 +1825,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand this->set_mesh(std::move(mesh)); TriangleMesh convex_hull = this->get_convex_hull(); convex_hull.transform(matrix, fix_left_handed); - this->m_convex_hull = std::make_shared(std::move(convex_hull)); + m_convex_hull = std::make_shared(std::move(convex_hull)); // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. this->set_new_unique_id(); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f66300abc..a65332272 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -180,8 +180,8 @@ private: class LayerHeightProfile final : public ObjectWithTimestamp { public: // Assign the content if the timestamp differs, don't assign an ObjectID. - void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } } - void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } + void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } + void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } std::vector get() const throw() { return m_data; } bool empty() const throw() { return m_data.empty(); } @@ -504,8 +504,8 @@ enum class ConversionType : int { class FacetsAnnotation final : public ObjectWithTimestamp { public: // Assign the content if the timestamp differs, don't assign an ObjectID. - void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } } - void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } + void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } + void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } const std::map>& get_data() const throw() { return m_data; } bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp index ef2b61221..14d7787cf 100644 --- a/src/libslic3r/MutablePolygon.hpp +++ b/src/libslic3r/MutablePolygon.hpp @@ -52,7 +52,7 @@ public: PointType* operator->() const { return &m_data->at(m_idx).point; } MutablePolygon& polygon() const { assert(this->valid()); return *m_data; } IndexType size() const { assert(this->valid()); return m_data->size(); } - iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; } + iterator& remove() { m_idx = m_data->remove(*this).m_idx; return *this; } iterator insert(const PointType pt) const { return m_data->insert(*this, pt); } private: iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {} @@ -162,10 +162,10 @@ public: return out; }; - bool empty() const { return this->m_size == 0; } - size_t size() const { return this->m_size; } - size_t capacity() const { return this->m_data.capacity(); } - bool valid() const { return this->m_size >= 3; } + bool empty() const { return m_size == 0; } + size_t size() const { return m_size; } + size_t capacity() const { return m_data.capacity(); } + bool valid() const { return m_size >= 3; } void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); } iterator begin() { return { this, m_head }; } diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp index 826b1632a..f5d314046 100644 --- a/src/libslic3r/Optimize/NLoptOptimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -121,8 +121,8 @@ protected: if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff); if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval); - if(this->m_stopcr.max_iterations() > 0) - nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations()); + if(m_stopcr.max_iterations() > 0) + nlopt_set_maxeval(nl.ptr, m_stopcr.max_iterations()); } template diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c6a86b719..92aa979e4 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1070,7 +1070,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl size_t PresetCollection::first_visible_idx() const { size_t idx = m_default_suppressed ? m_num_default_presets : 0; - for (; idx < this->m_presets.size(); ++ idx) + for (; idx < m_presets.size(); ++ idx) if (m_presets[idx].is_visible) break; if (idx == m_presets.size()) @@ -1282,7 +1282,7 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe assert(it != new_vendors.end()); preset.vendor = &it->second; } - this->m_presets.emplace(it, std::move(preset)); + m_presets.emplace(it, std::move(preset)); } else duplicates.emplace_back(std::move(preset.name)); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d341b7eec..25fd0dfc3 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -256,8 +256,8 @@ private: PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances); ~PrintObject() = default; - void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } - void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } + void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); } PrintBase::ApplyStatus set_instances(PrintInstances &&instances); // Invalidates the step, and its depending steps in PrintObject and Print. bool invalidate_step(PrintObjectStep step); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2986ea511..e8f2284fb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -725,10 +725,10 @@ bool PrintObject::invalidate_step(PrintObjectStep step) } else if (step == posSlice) { invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); - this->m_slicing_params.valid = false; + m_slicing_params.valid = false; } else if (step == posSupportMaterial) { invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); - this->m_slicing_params.valid = false; + m_slicing_params.valid = false; } // Wipe tower depends on the ordering of extruders, which in turn depends on everything. @@ -1009,7 +1009,7 @@ void PrintObject::process_external_surfaces() // Shrink the holes, let the layer above expand slightly inside the unsupported areas. polygons_append(voids, offset(surface.expolygon, unsupported_width)); } - surfaces_covered[layer_idx] = diff(this->m_layers[layer_idx]->lslices, voids); + surfaces_covered[layer_idx] = diff(m_layers[layer_idx]->lslices, voids); } } ); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index a94eb35fa..f5f422b3d 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -267,7 +267,7 @@ protected: void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) - { this->m_config.apply_only(other, keys, ignore_nonexistent); } + { m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo, bool left_handed) { m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; }); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 5b6cbd6bc..5eb9c9433 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -361,7 +361,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { // One of the support extruders is of "don't care" type. - auto object_extruders = m_object->print()->object_extruders(); + auto object_extruders = m_object->object_extruders(); if (object_extruders.size() == 1 && *object_extruders.begin() == std::max(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value)) // Object is printed with the same extruder as the support. @@ -2764,7 +2764,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; for (LayerRegion *region : object_layer.regions()) { - coordf_t bridging_height = region->region().bridging_height_avg(*this->m_print_config); + coordf_t bridging_height = region->region().bridging_height_avg(*m_print_config); if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; @@ -3182,7 +3182,7 @@ struct MyLayerExtruded MyLayerExtruded& operator=(MyLayerExtruded &&rhs) { this->layer = rhs.layer; this->extrusions = std::move(rhs.extrusions); - this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); + m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); rhs.layer = nullptr; return *this; } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 498391beb..2869f11c8 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -361,7 +361,7 @@ bool BackgroundSlicingProcess::stop() // m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it. std::unique_lock lck(m_mutex); if (m_state == STATE_INITIAL) { -// this->m_export_path.clear(); +// m_export_path.clear(); return false; } // assert(this->running()); @@ -379,7 +379,7 @@ bool BackgroundSlicingProcess::stop() m_state = STATE_IDLE; m_print->set_cancel_callback([](){}); } -// this->m_export_path.clear(); +// m_export_path.clear(); return true; } @@ -496,7 +496,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn assert(config.opt_enum("printer_technology") == m_print->technology()); Print::ApplyStatus invalidated = m_print->apply(model, config); if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && - !this->m_fff_print->is_step_done(psGCodeExport)) { + !m_fff_print->is_step_done(psGCodeExport)) { // Some FFF status was invalidated, and the G-code was not exported yet. // Let the G-code preview UI know that the final G-code preview is not valid. // In addition, this early memory deallocation reduces memory footprint. @@ -621,7 +621,7 @@ ThumbnailsList BackgroundSlicingProcess::render_thumbnails(const ThumbnailsParam { ThumbnailsList thumbnails; if (m_thumbnail_cb) - this->execute_ui_task([this, ¶ms, &thumbnails](){ thumbnails = this->m_thumbnail_cb(params); }); + this->execute_ui_task([this, ¶ms, &thumbnails](){ thumbnails = m_thumbnail_cb(params); }); return thumbnails; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c38ed3dcd..26c4a314c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3157,7 +3157,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } if (m_gizmos.on_mouse(evt)) { - if (wxWindow::FindFocus() != this->m_canvas) + if (wxWindow::FindFocus() != m_canvas) // Grab keyboard focus for input in gizmo dialogs. m_canvas->SetFocus(); @@ -3180,7 +3180,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_move_start_threshold_position_2D_as_invalid(); } - if (evt.ButtonDown() && wxWindow::FindFocus() != this->m_canvas) + if (evt.ButtonDown() && wxWindow::FindFocus() != m_canvas) // Grab keyboard focus on any mouse click event. m_canvas->SetFocus(); @@ -6201,7 +6201,7 @@ void GLCanvas3D::_load_sla_shells() #else v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.finalize_geometry(this->m_initialized); + v.indexed_vertex_array.finalize_geometry(m_initialized); v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0)); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 11a52d648..96fe94e0f 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -228,7 +228,7 @@ public: int config_type() const throw() { return m_config_type; } const t_opt_map& opt_map() const throw() { return m_opt_map; } - void set_config_category_and_type(const wxString &category, int type) { this->m_config_category = category; this->m_config_type = type; } + void set_config_category_and_type(const wxString &category, int type) { m_config_category = category; m_config_type = type; } void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; } Option get_option(const std::string& opt_key, int opt_index = -1); Line create_single_option_line(const std::string& title, const wxString& path = wxEmptyString, int idx = -1) /*const*/{ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 60f829728..aebec14ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1588,8 +1588,8 @@ struct Plater::priv void redo(); void undo_redo_to(size_t time_to_load); - void suppress_snapshots() { this->m_prevent_snapshots++; } - void allow_snapshots() { this->m_prevent_snapshots--; } + void suppress_snapshots() { m_prevent_snapshots++; } + void allow_snapshots() { m_prevent_snapshots--; } void process_validation_warning(const std::string& warning) const; @@ -4198,9 +4198,9 @@ int Plater::priv::get_active_snapshot_index() void Plater::priv::take_snapshot(const std::string& snapshot_name) { - if (this->m_prevent_snapshots > 0) + if (m_prevent_snapshots > 0) return; - assert(this->m_prevent_snapshots >= 0); + assert(m_prevent_snapshots >= 0); UndoRedo::SnapshotData snapshot_data; snapshot_data.printer_technology = this->printer_technology; if (this->view3D->is_layers_editing_enabled()) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index c6a3006b7..7630c44bb 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -134,7 +134,7 @@ void PresetComboBox::OnSelect(wxCommandEvent& evt) auto marker = reinterpret_cast(this->GetClientData(selected_item)); if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) - this->SetSelection(this->m_last_selected); + this->SetSelection(m_last_selected); else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty())) { m_last_selected = selected_item; on_selection_changed(selected_item); @@ -698,7 +698,7 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt) auto marker = reinterpret_cast(this->GetClientData(selected_item)); if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { - this->SetSelection(this->m_last_selected); + this->SetSelection(m_last_selected); evt.StopPropagation(); if (marker == LABEL_ITEM_MARKER) return; @@ -715,8 +715,8 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt) } return; } - else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || this->m_last_selected != selected_item || m_collection->current_is_dirty()) - this->m_last_selected = selected_item; + else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || m_last_selected != selected_item || m_collection->current_is_dirty()) + m_last_selected = selected_item; evt.Skip(); } @@ -973,7 +973,7 @@ void TabPresetComboBox::OnSelect(wxCommandEvent &evt) auto marker = reinterpret_cast(this->GetClientData(selected_item)); if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) { - this->SetSelection(this->m_last_selected); + this->SetSelection(m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) wxTheApp->CallAfter([this]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0e954a906..a037a3b47 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -940,7 +940,7 @@ void Tab::update_visibility() page->update_visibility(m_mode, page.get() == m_active_page); rebuild_page_tree(); - if (this->m_type == Preset::TYPE_SLA_PRINT) + if (m_type == Preset::TYPE_SLA_PRINT) update_description_lines(); Layout(); @@ -3141,8 +3141,8 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (preset_name.empty()) { if (delete_current) { // Find an alternate preset to be selected after the current preset is deleted. - const std::deque &presets = this->m_presets->get_presets(); - size_t idx_current = this->m_presets->get_idx_selected(); + const std::deque &presets = m_presets->get_presets(); + size_t idx_current = m_presets->get_idx_selected(); // Find the next visible preset. size_t idx_new = idx_current + 1; if (idx_new < presets.size()) diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 697c1209d..6d6753ea2 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -755,7 +755,7 @@ namespace UndoRedo { template std::shared_ptr& ImmutableObjectHistory::shared_ptr(StackImpl &stack) { - if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) { + if (m_shared_object.get() == nullptr && ! m_serialized.empty()) { // Deserialize the object. std::istringstream iss(m_serialized); { @@ -897,7 +897,7 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU 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; + m_active_snapshot_time = timestamp; assert(this->valid()); } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index a6ea590f5..44d58266b 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -115,7 +115,6 @@ _constant() RETVAL = newRV_noinc((SV*)hv); } %}; - double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object); std::string output_filepath(std::string path = "") From dd72016159a6bd38c715d163908969797dab4550 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 15:08:57 +0200 Subject: [PATCH 41/79] FDM backend refactoring for const correctness, clarity ... --- src/libslic3r/GCode/ToolOrdering.cpp | 24 ++++------ src/libslic3r/Layer.cpp | 2 +- src/libslic3r/Layer.hpp | 9 ++-- src/libslic3r/Print.cpp | 14 +++--- src/libslic3r/PrintObject.cpp | 71 ++++++++++++++-------------- 5 files changed, 57 insertions(+), 63 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 728a26957..9dc9f3f96 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -223,10 +223,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto layer_tools.extruder_override = extruder_override; // What extruders are required to print this object layer? - for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) { - const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; - if (layerm == nullptr) - continue; + for (const LayerRegion *layerm : layer->regions()) { const PrintRegion ®ion = layerm->region(); if (! layerm->perimeters.entities.empty()) { @@ -688,16 +685,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves for (unsigned int copy = 0; copy < num_of_copies; ++copy) { - - for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = object->printing_region(region_id); - + for (const LayerRegion *layerm : this_layer->regions()) { + const auto ®ion = layerm->region(); if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill; if (print.config().infill_first != perimeters_done || wipe_into_infill_only) { - for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections + for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections auto* fill = dynamic_cast(ee); if (!is_overriddable(*fill, print.config(), *object, region)) @@ -721,7 +716,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int // Now the same for perimeters - see comments above for explanation: if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done) { - for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { + for (const ExtrusionEntity* ee : layerm->perimeters.entities) { auto* fill = dynamic_cast(ee); if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) { set_extruder_override(fill, copy, new_extruder, num_of_copies); @@ -762,13 +757,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) size_t num_of_copies = object->instances().size(); for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves - for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { - const auto& region = object->printing_region(region_id); - + for (const LayerRegion *layerm : this_layer->regions()) { + const auto ®ion = layerm->region(); if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; - for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections + for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections auto* fill = dynamic_cast(ee); if (!is_overriddable(*fill, print.config(), *object, region) @@ -791,7 +785,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) } // Now the same for perimeters - see comments above for explanation: - for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections + for (const ExtrusionEntity* ee : layerm->perimeters.entities) { // iterate through all perimeter Collections auto* fill = dynamic_cast(ee); if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy)) set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index c3dcad162..3f2327686 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -27,7 +27,7 @@ bool Layer::empty() const return true; } -LayerRegion* Layer::add_region(PrintRegion* print_region) +LayerRegion* Layer::add_region(const PrintRegion *print_region) { m_regions.emplace_back(new LayerRegion(this, print_region)); return m_regions.back(); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index f2cce4880..102a991ca 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -22,7 +22,6 @@ class LayerRegion public: Layer* layer() { return m_layer; } const Layer* layer() const { return m_layer; } - PrintRegion& region() { return *m_region; } const PrintRegion& region() const { return *m_region; } // collection of surfaces generated by slicing the original geometry @@ -86,12 +85,12 @@ public: protected: friend class Layer; - LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {} + LayerRegion(Layer *layer, const PrintRegion *region) : m_layer(layer), m_region(region) {} ~LayerRegion() {} private: - Layer *m_layer; - PrintRegion *m_region; + Layer *m_layer; + const PrintRegion *m_region; }; @@ -128,7 +127,7 @@ public: size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(int idx) const { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; } - LayerRegion* add_region(PrintRegion* print_region); + LayerRegion* add_region(const PrintRegion *print_region); const LayerRegionPtrs& regions() const { return m_regions; } // Test whether whether there are any slices assigned to this layer. bool empty() const; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d0235c40e..538ee6009 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -413,10 +413,12 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print) // Precondition: Print::validate() requires the Print::apply() to be called its invocation. std::string Print::validate(std::string* warning) const { + std::vector extruders = this->extruders(); + if (m_objects.empty()) return L("All objects are outside of the print volume."); - if (extruders().empty()) + if (extruders.empty()) return L("The supplied settings will cause an empty print."); if (m_config.complete_objects) { @@ -442,9 +444,9 @@ std::string Print::validate(std::string* warning) const if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments - double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front()); - double first_filament_diam = m_config.filament_diameter.get_at(extruders().front()); - for (const auto& extruder_idx : extruders()) { + double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front()); + double first_filament_diam = m_config.filament_diameter.get_at(extruders.front()); + for (const auto& extruder_idx : extruders) { double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); double filament_diam = m_config.filament_diameter.get_at(extruder_idx); if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam @@ -462,7 +464,7 @@ std::string Print::validate(std::string* warning) const return L("Ooze prevention is currently not supported with the wipe tower enabled."); if (m_config.use_volumetric_e) return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); - if (m_config.complete_objects && extruders().size() > 1) + if (m_config.complete_objects && extruders.size() > 1) return L("The Wipe Tower is currently not supported for multimaterial sequential prints."); if (m_objects.size() > 1) { @@ -542,8 +544,6 @@ std::string Print::validate(std::string* warning) const } { - std::vector extruders = this->extruders(); - // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); double max_nozzle_diameter = 0; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e8f2284fb..33e961dbe 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -772,11 +772,11 @@ void PrintObject::detect_surfaces_type() bool interface_shells = ! spiral_vase && m_config.interface_shells.value; size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size(); - for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++ idx_region) { - BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) - layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); + layer->m_regions[region_id]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads. @@ -792,7 +792,7 @@ void PrintObject::detect_surfaces_type() ((num_layers > 1) ? num_layers - 1 : num_layers) : // In non-spiral vase mode, go over all layers. m_layers.size()), - [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { + [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range& range) { // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // the support from the print. SurfaceType surface_type_bottom_other = @@ -800,9 +800,9 @@ void PrintObject::detect_surfaces_type() stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); - // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; + // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z; Layer *layer = m_layers[idx_layer]; - LayerRegion *layerm = layer->m_regions[idx_region]; + LayerRegion *layerm = layer->m_regions[region_id]; // comparison happens against the *full* slices (considering all regions) // unless internal shells are requested Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; @@ -815,7 +815,7 @@ void PrintObject::detect_surfaces_type() Surfaces top; if (upper_layer) { ExPolygons upper_slices = interface_shells ? - diff_ex(layerm->slices.surfaces, upper_layer->m_regions[idx_region]->slices.surfaces, ApplySafetyOffset::Yes) : + diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop); } else { @@ -832,7 +832,7 @@ void PrintObject::detect_surfaces_type() #if 0 //FIXME Why is this branch failing t\multi.t ? Polygons lower_slices = interface_shells ? - to_polygons(lower_layer->get_region(idx_region)->slices.surfaces) : + to_polygons(lower_layer->get_region(region_id)->slices.surfaces) : to_polygons(lower_layer->slices); surfaces_append(bottom, offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset), @@ -855,7 +855,7 @@ void PrintObject::detect_surfaces_type() offset2_ex( diff_ex( intersection(layerm->slices.surfaces, lower_layer->lslices), // supported - lower_layer->m_regions[idx_region]->slices.surfaces, + lower_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes), -offset, offset), stBottom); @@ -888,7 +888,7 @@ void PrintObject::detect_surfaces_type() expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green"))); expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown"))); expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black"))); - SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, idx_region, layer->print_z).c_str(), expolygons_with_attributes); + SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -925,25 +925,25 @@ void PrintObject::detect_surfaces_type() if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) - m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]); + m_layers[idx_layer]->m_regions[region_id]->slices.surfaces = std::move(surfaces_new[idx_layer]); } if (spiral_vase) { if (num_layers > 1) // Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern. - m_layers[num_layers - 1]->m_regions[idx_region]->slices.set_type(stTop); + m_layers[num_layers - 1]->m_regions[region_id]->slices.set_type(stTop); for (size_t i = num_layers; i < m_layers.size(); ++ i) - m_layers[i]->m_regions[idx_region]->slices.set_type(stInternal); + m_layers[i]->m_regions[region_id]->slices.set_type(stInternal); } - BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; + BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, idx_region](const tbb::blocked_range& range) { + [this, region_id](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); - LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region]; + LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); @@ -951,7 +951,7 @@ void PrintObject::detect_surfaces_type() } // for each layer of a region }); m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; + BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end"; } // for each this->print->region_count // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) @@ -1071,8 +1071,8 @@ void PrintObject::discover_vertical_shells() // is calculated over all materials. // Is the "ensure vertical wall thickness" applicable to any region? bool has_extra_layers = false; - for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++idx_region) { - const PrintRegionConfig &config = this->printing_region(idx_region).config(); + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { + const PrintRegionConfig &config = this->printing_region(region_id).config(); if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) { has_extra_layers = true; break; @@ -1100,8 +1100,8 @@ void PrintObject::discover_vertical_shells() static size_t debug_idx = 0; ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) { - LayerRegion &layerm = *layer.m_regions[idx_region]; + for (size_t region_id = 0; region_id < num_regions; ++ region_id) { + LayerRegion &layerm = *layer.m_regions[region_id]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing)); @@ -1149,10 +1149,10 @@ void PrintObject::discover_vertical_shells() BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - for (size_t idx_region = 0; idx_region < this->num_printing_regions(); ++ idx_region) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { PROFILE_BLOCK(discover_vertical_shells_region); - const PrintRegion ®ion = this->printing_region(idx_region); + const PrintRegion ®ion = this->printing_region(region_id); if (! region.config().ensure_vertical_shell_thickness.value) // This region will be handled by discover_horizontal_shells(). continue; @@ -1166,15 +1166,15 @@ void PrintObject::discover_vertical_shells() if (! top_bottom_surfaces_all_regions) { // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // is calculated over a single material. - BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; + BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : cache top / bottom"; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), - [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { + [this, region_id, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); Layer &layer = *m_layers[idx_layer]; - LayerRegion &layerm = *layer.m_regions[idx_region]; + LayerRegion &layerm = *layer.m_regions[region_id]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. auto &cache = cache_top_botom_regions[idx_layer]; @@ -1183,21 +1183,21 @@ void PrintObject::discover_vertical_shells() // Bottom surfaces. cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing); append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing)); - // Holes over all regions. Only collect them once, they are valid for all idx_region iterations. + // Holes over all regions. Only collect them once, they are valid for all region_id iterations. if (cache.holes.empty()) { - for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region) - polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons)); + for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) + polygons_append(cache.holes, to_polygons(layer.regions()[region_id]->fill_expolygons)); } } }); m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; + BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end : cache top / bottom"; } - BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; + BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness"; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), - [this, idx_region, &cache_top_botom_regions] + [this, region_id, &cache_top_botom_regions] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -1209,7 +1209,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Layer *layer = m_layers[idx_layer]; - LayerRegion *layerm = layer->m_regions[idx_region]; + LayerRegion *layerm = layer->m_regions[region_id]; const PrintRegionConfig ®ion_config = layerm->region().config(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1424,11 +1424,11 @@ void PrintObject::discover_vertical_shells() } // for each layer }); m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; + BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) { - LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); + LayerRegion *layerm = m_layers[idx_layer]->get_region(region_id); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); } @@ -1663,6 +1663,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full object_extruders); } sort_remove_duplicates(object_extruders); + //FIXME add painting extruders if (object_max_z <= 0.f) object_max_z = (float)model_object.raw_bounding_box().size().z(); From feefbc575abd32678d0a1b8c48ccbbf94df3b886 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 May 2021 15:48:38 +0200 Subject: [PATCH 42/79] Refactored PrintObject::m_region_volumes for extensibility. WIP for multi-material painting. --- src/libslic3r/Print.hpp | 40 ++++++++++++++++----- src/libslic3r/PrintApply.cpp | 6 ++-- src/libslic3r/PrintObject.cpp | 65 +++++++++++++++++------------------ 3 files changed, 67 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 25fd0dfc3..b4241c91e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -152,6 +152,34 @@ struct PrintInstance typedef std::vector PrintInstances; +// Region and its volumes (printing volumes or modifier volumes) +struct PrintRegionVolumes +{ + // Single volume + Z range assigned to a region. + struct VolumeWithZRange { + // Z range to slice this ModelVolume over. + t_layer_height_range layer_height_range; + // Index of a ModelVolume inside its parent ModelObject. + int volume_idx; + }; + + // Overriding one region with some other extruder, producing another region. + // The region is owned by PrintObject::m_all_regions. + struct ExtruderOverride { + unsigned int extruder; +// const PrintRegion *region; + }; + + // The region is owned by PrintObject::m_all_regions. +// const PrintRegion *region; + // Possible overrides of the default region extruder. + std::vector overrides; + // List of ModelVolume indices and layer ranges of thereof. + std::vector volumes; + // Is this region printing in any layer? +// bool printing { false }; +}; + class PrintObject : public PrintObjectBaseWithState { private: // Prevents erroneous use by other classes. @@ -186,7 +214,7 @@ public: void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { if (region_id >= m_region_volumes.size()) m_region_volumes.resize(region_id + 1); - m_region_volumes[region_id].emplace_back(layer_range, volume_id); + m_region_volumes[region_id].volumes.push_back({ layer_range, volume_id }); } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id @@ -305,9 +333,9 @@ private: // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system. Point m_center_offset; - std::vector> m_all_regions; + std::vector> m_all_regions; // vector of (layer height ranges and vectors of volume ids), indexed by region_id - std::vector>> m_region_volumes; + std::vector m_region_volumes; SlicingParameters m_slicing_params; LayerPtrs m_layers; @@ -513,15 +541,11 @@ public: std::string output_filename(const std::string &filename_base = std::string()) const override; - // Accessed by SupportMaterial size_t num_print_regions() const throw() { return m_print_regions.size(); } const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } - const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing + const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } protected: - // methods for handling regions - PrintRegion& get_print_region(size_t idx) { return *m_print_regions[idx]; } - // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 3679080e6..f0e262fdc 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -694,9 +694,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ PrintRegion ®ion = *print_object->m_all_regions[region_id]; PrintRegionConfig region_config; bool region_config_set = false; - for (const std::pair &volume_and_range : print_object->m_region_volumes[region_id]) { - const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; - const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); + for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : print_object->m_region_volumes[region_id].volumes) { + const ModelVolume &volume = *print_object->model_object()->volumes[volume_w_zrange.volume_idx]; + const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_w_zrange.layer_height_range); PrintRegionConfig this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders); if (region_config_set) { if (this_region_config != region_config) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 33e961dbe..8fefd4beb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1757,14 +1757,13 @@ void PrintObject::_slice(const std::vector &layer_height_profile) size_t num_modifiers = 0; for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) { int last_volume_id = -1; - for (const std::pair &volume_and_range : m_region_volumes[region_id]) { - const int volume_id = volume_and_range.second; - const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; + for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { + const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; if (model_volume->is_model_part()) { - if (last_volume_id == volume_id) { + if (last_volume_id == volume_w_zrange.volume_idx) { has_z_ranges = true; } else { - last_volume_id = volume_id; + last_volume_id = volume_w_zrange.volume_idx; if (all_volumes_single_region == -2) // first model volume met all_volumes_single_region = region_id; @@ -1821,21 +1820,21 @@ void PrintObject::_slice(const std::vector &layer_height_profile) std::vector sliced_volumes; sliced_volumes.reserve(num_volumes); for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; - for (size_t i = 0; i < volumes_and_ranges.size(); ) { - int volume_id = volumes_and_ranges[i].second; + const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; + for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { + int volume_id = volumes_and_ranges.volumes[i].volume_idx; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; if (model_volume->is_model_part()) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id; // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. std::vector ranges; - ranges.emplace_back(volumes_and_ranges[i].first); + ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); size_t j = i + 1; - for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) - if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON) - ranges.back().second = volumes_and_ranges[j].first.second; + for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) + if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON) + ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second; else - ranges.emplace_back(volumes_and_ranges[j].first); + ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); // slicing in parallel sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume)); i = j; @@ -2048,8 +2047,8 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v { std::vector volumes; if (region_id < m_region_volumes.size()) { - for (const std::pair &volume_and_range : m_region_volumes[region_id]) { - const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; + for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { + const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; if (volume->is_model_part()) volumes.emplace_back(volume); } @@ -2057,27 +2056,27 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes); } -// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once. +// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once. std::vector PrintObject::slice_modifiers(size_t region_id, const std::vector &slice_zs) const { std::vector out; if (region_id < m_region_volumes.size()) { std::vector> volume_ranges; - const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; - volume_ranges.reserve(volumes_and_ranges.size()); - for (size_t i = 0; i < volumes_and_ranges.size(); ) { - int volume_id = volumes_and_ranges[i].second; + const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; + volume_ranges.reserve(volumes_and_ranges.volumes.size()); + for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { + int volume_id = volumes_and_ranges.volumes[i].volume_idx; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; if (model_volume->is_modifier()) { std::vector ranges; - ranges.emplace_back(volumes_and_ranges[i].first); + ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); size_t j = i + 1; - for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) { - if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON) - ranges.back().second = volumes_and_ranges[j].first.second; + for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) { + if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON) + ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second; else - ranges.emplace_back(volumes_and_ranges[j].first); + ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); } volume_ranges.emplace_back(std::move(ranges)); i = j; @@ -2099,8 +2098,8 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) { // No modifier in this region was split to layer spans. std::vector volumes; - for (const std::pair &volume_and_range : m_region_volumes[region_id]) { - const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; + for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { + const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; if (volume->is_modifier()) volumes.emplace_back(volume); } @@ -2109,18 +2108,18 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std // Some modifier in this region was split to layer spans. std::vector merge; for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - const std::vector> &volumes_and_ranges = m_region_volumes[region_id]; - for (size_t i = 0; i < volumes_and_ranges.size(); ) { - int volume_id = volumes_and_ranges[i].second; + const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; + for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { + int volume_id = volumes_and_ranges.volumes[i].volume_idx; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; if (model_volume->is_modifier()) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id; // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. std::vector ranges; - ranges.emplace_back(volumes_and_ranges[i].first); + ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); size_t j = i + 1; - for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) - ranges.emplace_back(volumes_and_ranges[j].first); + for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) + ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); // slicing in parallel std::vector this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume); // Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume. From f8a4c3c7ceb04a6d5d0759b7285b6e6bbc039d65 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Thu, 6 May 2021 16:18:12 +0200 Subject: [PATCH 43/79] Updated start/end g-code. https://github.com/prusa3d/PrusaSlicer-settings/pull/131 --- resources/profiles/Anycubic.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini index ff0367291..c1b763879 100644 --- a/resources/profiles/Anycubic.ini +++ b/resources/profiles/Anycubic.ini @@ -1163,7 +1163,7 @@ before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z] default_filament_profile = Generic PLA @MEGA default_print_profile = 0.15mm QUALITY @MEGA deretract_speed = 40 -end_gcode = G1 E-1.0 F2100 ; retract\nG92 E0.0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; move print head up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y105 F3000 ; park print head\nM84 ; disable motors +end_gcode = G1 E-1.0 F2100 ; retract\nG92 E0.0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} E-34.0 F720 ; move print head up & retract filament\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y105 F3000 ; park print head\nM84 ; disable motors extruder_colour = #808080 gcode_flavor = marlin layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] @@ -1178,7 +1178,7 @@ retract_lift = 0.2 retract_lift_below = 204 retract_speed = 70 silent_mode = 0 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y2.0 Z0.2 F1000 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0.0\nG1 X60.0 E9.0 F1000 ; intro line\nG1 X100.0 E12.5 F1000 ; intro line\nG92 E0.0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y1.0 Z0.3 F1000 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0.0\n; initial load\nG1 X205.0 E19 F1000\nG1 Y1.6\nG1 X5.0 E19 F1000\nG92 E0.0\n; intro line\nG1 Y2.0 Z0.2 F1000\nG1 X65.0 E9.0 F1000\nG1 X105.0 E12.5 F1000\nG92 E0.0 thumbnails = 16x16,220x124 use_relative_e_distances = 1 wipe = 1 @@ -1892,4 +1892,4 @@ default_print_profile = 0.24mm 0.8 nozzle DETAILED QUALITY @PREDATOR ######################################### ########## end printer presets ########## -#########################################"do not" cause ' is bad for syntax highlighting +######################################### From 963849e18b4f1fe55406f82867bdb564b4122433 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 6 May 2021 17:37:55 +0200 Subject: [PATCH 44/79] desktop integration functions and dialog --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/ConfigWizard.cpp | 31 +- src/slic3r/GUI/ConfigWizard.hpp | 1 - src/slic3r/GUI/ConfigWizard_private.hpp | 6 +- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 439 ++++++++++++++++++++ src/slic3r/GUI/DesktopIntegrationDialog.hpp | 39 ++ src/slic3r/GUI/GUI_App.cpp | 19 + src/slic3r/GUI/GUI_App.hpp | 2 + src/slic3r/GUI/NotificationManager.hpp | 16 +- 9 files changed, 550 insertions(+), 5 deletions(-) create mode 100644 src/slic3r/GUI/DesktopIntegrationDialog.cpp create mode 100644 src/slic3r/GUI/DesktopIntegrationDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4b3a1c6ca..7f4b87439 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -189,6 +189,8 @@ set(SLIC3R_GUI_SOURCES GUI/UnsavedChangesDialog.hpp GUI/ExtraRenderers.cpp GUI/ExtraRenderers.hpp + GUI/DesktopIntegrationDialog.cpp + GUI/DesktopIntegrationDialog.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index bd6986d3e..1d07b137e 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -26,14 +26,17 @@ #include #include +#include "libslic3r/Platform.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Config.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_Utils.hpp" #include "GUI_ObjectManipulation.hpp" +#include "DesktopIntegrationDialog.hpp" #include "slic3r/Config/Snapshot.hpp" #include "slic3r/Utils/PresetUpdater.hpp" +#include "format.hpp" #if defined(__linux__) && defined(__WXGTK3__) #define wxLinux_gtk3 true @@ -450,7 +453,6 @@ void ConfigWizardPage::append_spacer(int space) content->AddSpacer(space); } - // Wizard pages PageWelcome::PageWelcome(ConfigWizard *parent) @@ -469,9 +471,21 @@ PageWelcome::PageWelcome(ConfigWizard *parent) , cbox_reset(append( new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)")) )) + , cbox_integrate(append( + new wxCheckBox(this, wxID_ANY, _L("Perform desktop integration (This will set shortcuts to PrusaSlicer to this Appimage executable).")) + )) { welcome_text->Hide(); cbox_reset->Hide(); +#ifdef __linux__ + if (!DesktopIntegrationDialog::is_integrated()) + cbox_integrate->Show(true); + else + cbox_integrate->Hide(); +#else + cbox_integrate->Hide(); +#endif + } void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) @@ -479,6 +493,14 @@ void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY; welcome_text->Show(data_empty); cbox_reset->Show(!data_empty); +#ifdef __linux__ + if (!DesktopIntegrationDialog::is_integrated()) + cbox_integrate->Show(true); + else + cbox_integrate->Hide(); +#else + cbox_integrate->Hide(); +#endif } @@ -2373,6 +2395,12 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese } } +#ifdef __linux__ + // Desktop integration on Linux + if (page_welcome->integrate_desktop()) + DesktopIntegrationDialog::perform_desktop_integration(); +#endif + // Decide whether to create snapshot based on run_reason and the reset profile checkbox bool snapshot = true; Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE; @@ -2490,7 +2518,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // Update the selections from the compatibilty. preset_bundle->export_selections(*app_config); } - void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add) { const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla; diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index 942f4b4ce..86245836b 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -45,7 +45,6 @@ public: bool run(RunReason reason, StartPage start_page = SP_WELCOME); static const wxString& name(const bool from_menu = false); - protected: void on_dpi_changed(const wxRect &suggested_rect) override ; diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index eee906ae7..4e3f1538e 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -227,10 +227,12 @@ struct PageWelcome: ConfigWizardPage { wxStaticText *welcome_text; wxCheckBox *cbox_reset; + wxCheckBox *cbox_integrate; PageWelcome(ConfigWizard *parent); bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } + bool integrate_desktop() const { return cbox_integrate != nullptr ? cbox_integrate->GetValue() : false; } virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; }; @@ -615,7 +617,9 @@ struct ConfigWizard::priv void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); // #ys_FIXME_alise void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); - +#ifdef __linux__ + void perform_desktop_integration() const; +#endif bool check_fff_selected(); // Used to decide whether to display Filaments page bool check_sla_selected(); // Used to decide whether to display SLA Materials page diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp new file mode 100644 index 000000000..25a5ab575 --- /dev/null +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -0,0 +1,439 @@ +#ifdef __linux__ +#include "DesktopIntegrationDialog.hpp" +#include "GUI_App.hpp" +#include "format.hpp" +#include "I18N.hpp" +#include "NotificationManager.hpp" +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Platform.hpp" + +namespace Slic3r { +namespace GUI { + +namespace integrate_desktop_internal{ +// Disects path strings stored in system variable divided by ':' and adds into vector +static void resolve_path_from_var(const std::string& var, std::vector& paths) +{ + wxString wxdirs; + if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() ) + return; + std::string dirs = boost::nowide::narrow(wxdirs); + for (size_t i = dirs.find(':'); i != std::string::npos; i = dirs.find(':')) + { + paths.push_back(dirs.substr(0, i)); + if (dirs.size() > i+1) + dirs = dirs.substr(i+1); + } + paths.push_back(dirs); +} +// Return true if directory in path p+dir_name exists +static bool contains_path_dir(const std::string& p, const std::string& dir_name) +{ + if (p.empty() || dir_name.empty()) + return false; + boost::filesystem::path path(p + (p[p.size()-1] == '/' ? "" : "/") + dir_name); + if (boost::filesystem::exists(path) && boost::filesystem::is_directory(path)) { + //BOOST_LOG_TRIVIAL(debug) << path.string() << " " << std::oct << boost::filesystem::status(path).permissions(); + return true; //boost::filesystem::status(path).permissions() & boost::filesystem::owner_write; + } else + BOOST_LOG_TRIVIAL(debug) << path.string() << " doesnt exists"; + return false; +} +// Creates directory in path if not exists yet +static void create_dir(const boost::filesystem::path& path) +{ + if (boost::filesystem::exists(path)) + return; + BOOST_LOG_TRIVIAL(debug)<< "creating " << path.string(); + boost::system::error_code ec; + boost::filesystem::create_directory(path, ec); + if (ec) + BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message(); +} +// Starts at basic_path (excluded) and creates all directories in dir_path +static void create_path(const std::string& basic_path, const std::string& dir_path) +{ + if (basic_path.empty() || dir_path.empty()) + return; + + boost::filesystem::path path(basic_path); + std::string dirs = dir_path; + for (size_t i = dirs.find('/'); i != std::string::npos; i = dirs.find('/')) + { + std::string dir = dirs.substr(0, i); + path = boost::filesystem::path(path.string() +"/"+ dir); + create_dir(path); + dirs = dirs.substr(i+1); + } + path = boost::filesystem::path(path.string() +"/"+ dirs); + create_dir(path); +} +// Calls our internal copy_file function to copy file at icon_path to dest_path +static bool copy_icon(const std::string& icon_path, const std::string& dest_path) +{ + BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path; + BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path; + std::string error_message; + auto cfr = copy_file(icon_path, dest_path, error_message, false); + if (cfr) { + BOOST_LOG_TRIVIAL(debug) << "Copy icon fail(" << cfr << "): " << error_message; + return false; + } + BOOST_LOG_TRIVIAL(debug) << "Copy icon success."; + return true; +} +// Creates new file filled with data. +static bool create_desktop_file(const std::string& path, const std::string& data) +{ + BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path; + std::ofstream output(path); + output << data; + struct stat buffer; + if (stat(path.c_str(), &buffer) == 0) + { + BOOST_LOG_TRIVIAL(debug) << "Desktop file created."; + return true; + } + BOOST_LOG_TRIVIAL(debug) << "Desktop file NOT created."; + return false; +} +} // namespace integratec_desktop_internal + +// methods that actually do / undo desktop integration. Static to be accesible from anywhere. +bool DesktopIntegrationDialog::is_integrated() +{ + const char *appimage_env = std::getenv("APPIMAGE"); + if (!appimage_env) + return false; + + const AppConfig *app_config = wxGetApp().app_config; + std::string path(app_config->get("desktop_integration_app_path")); + BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path; + + if (path.empty()) + return false; + + // confirmation that PrusaSlicer.desktop exists + struct stat buffer; + return (stat (path.c_str(), &buffer) == 0); +} +bool DesktopIntegrationDialog::integration_possible() +{ + + const char *appimage_env = std::getenv("APPIMAGE"); + if (!appimage_env) + return false; + return true; +} +void DesktopIntegrationDialog::perform_desktop_integration() +{ + BOOST_LOG_TRIVIAL(debug) << "performing desktop integration"; + + // Path to appimage + const char *appimage_env = std::getenv("APPIMAGE"); + std::string appimage_path; + if (appimage_env) { + try { + appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); + } catch (std::exception &) { + } + } else { + // not appimage - not performing + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable."; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + return; + } + + // Find directories icons and applications + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. + // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. + // $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. + // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. + // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. + std::vectortarget_candidates; + integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates); + integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates); + + AppConfig *app_config = wxGetApp().app_config; + // suffix string to create different desktop file for alpha, beta. + + std::string version_suffix; + std::string name_suffix; + std::string version(SLIC3R_VERSION); + if (version.find("alpha") != std::string::npos) + { + version_suffix = "-alpha"; + name_suffix = " - alpha"; + }else if (version.find("beta") != std::string::npos) + { + version_suffix = "-beta"; + name_suffix = " - beta"; + } + + // theme path to icon destination + std::string icon_theme_path; + std::string icon_theme_dirs; + + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { + icon_theme_path = "hicolor/96x96/apps/"; + icon_theme_dirs = "/hicolor/96x96/apps"; + } + + + std::string target_dir_icons; + std::string target_dir_desktop; + + // slicer icon + // iterate thru target_candidates to find icons folder + for (size_t i = 0; i < target_candidates.size(); ++i) { + // Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/ + if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) { + target_dir_icons = target_candidates[i]; + std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); + std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); + if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + break; // success + else + target_dir_icons.clear(); // copying failed + // if all failed - try creating default home folder + if (i == target_candidates.size() - 1) { + // create $HOME/.local/share + integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); + // copy icon + target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); + std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); + std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); + if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons") + || !integrate_desktop_internal::copy_icon(icon_path, dest_path)) { + // every attempt failed - icon wont be present + target_dir_icons.clear(); + } + } + } + } + if(target_dir_icons.empty()) { + BOOST_LOG_TRIVIAL(error) << "Copying PrusaSlicer icon to icons directory failed."; + } else + // save path to icon + app_config->set("desktop_integration_icon_slicer_path", GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix)); + + // desktop file + // iterate thru target_candidates to find applications folder + for (size_t i = 0; i < target_candidates.size(); ++i) + { + if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) { + target_dir_desktop = target_candidates[i]; + // Write slicer desktop file + std::string desktop_file = GUI::format( + "[Desktop Entry]\n" + "Name=PrusaSlicer%1%\n" + "GenericName=3D Printing Software\n" + "Icon=PrusaSlicer%2%\n" + "Exec=%3% %%F\n" + "Terminal=false\n" + "Type=Application\n" + "MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n" + "Categories=Graphics;3DGraphics;Engineering;\n" + "Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n" + "StartupNotify=false\n" + "StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path); + + std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); + if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){ + BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success."; + break; + } else { + // write failed - try another path + BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed."; + target_dir_desktop.clear(); + } + // if all failed - try creating default home folder + if (i == target_candidates.size() - 1) { + // create $HOME/.local/share + integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); + // create desktop file + target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); + std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); + if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) { + if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file"; + return; + } + } else { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory"; + return; + } + } + } + } + if(target_dir_desktop.empty()) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory"; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + return; + } + // save path to desktop file + app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix)); + + // Repeat for Gcode viewer - use same paths as for slicer files + // Icon + if (!target_dir_icons.empty()) + { + std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir()); + std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix); + if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + // save path to icon + app_config->set("desktop_integration_icon_viewer_path", dest_path); + else + BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed."; + } + + // Desktop file + std::string desktop_file = GUI::format( + "[Desktop Entry]\n" + "Name=Prusa Gcode Viewer%1%\n" + "GenericName=3D Printing Software\n" + "Icon=PrusaSlicer-gcodeviewer%2%\n" + "Exec=%3% --gcodeviwer %%F\n" + "Terminal=false\n" + "Type=Application\n" + "MimeType=text/x.gcode;\n" + "Categories=Graphics;3DGraphics;\n" + "Keywords=3D;Printing;Slicer;\n" + "StartupNotify=false", name_suffix, version_suffix, appimage_path); + + std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix); + if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file)) + // save path to desktop file + app_config->set("desktop_integration_app_viewer_path", desktop_path); + else { + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file"; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + } + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); +} +void DesktopIntegrationDialog::undo_desktop_intgration() +{ + const char *appimage_env = std::getenv("APPIMAGE"); + if (!appimage_env) { + BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable."; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail); + return; + } + const AppConfig *app_config = wxGetApp().app_config; + // slicer .desktop + std::string path = std::string(app_config->get("desktop_integration_app_path")); + if (!path.empty()) { + BOOST_LOG_TRIVIAL(debug) << "removing " << path; + std::remove(path.c_str()); + } + // slicer icon + path = std::string(app_config->get("desktop_integration_icon_slicer_path")); + if (!path.empty()) { + BOOST_LOG_TRIVIAL(debug) << "removing " << path; + std::remove(path.c_str()); + } + // gcode viwer .desktop + path = std::string(app_config->get("desktop_integration_app_viewer_path")); + if (!path.empty()) { + BOOST_LOG_TRIVIAL(debug) << "removing " << path; + std::remove(path.c_str()); + } + // gcode viewer icon + path = std::string(app_config->get("desktop_integration_icon_viewer_path")); + if (!path.empty()) { + BOOST_LOG_TRIVIAL(debug) << "removing " << path; + std::remove(path.c_str()); + } + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess); +} + +DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) +: wxDialog(parent, wxID_ANY, _(L("Desktop Integration")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +{ + bool can_undo = DesktopIntegrationDialog::is_integrated(); + + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + + + wxString text = _L("Desktop Integration sets this binary to be searchable by the system.\n\nPress \"Perform\" to proceed."); + if (can_undo) + text += "\nPress \"Undo\" to remove previous integration."; + + vbox->Add( + new wxStaticText( this, wxID_ANY, text), + // , wxDefaultPosition, wxSize(100,50), wxTE_MULTILINE), + 1, // make vertically stretchable + wxEXPAND | // make horizontally stretchable + wxALL, // and make border all around + 10 ); // set border width to 10 + + + wxBoxSizer *btn_szr = new wxBoxSizer(wxHORIZONTAL); + wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform")); + btn_szr->Add(btn_perform, 0, wxALL, 10); + + btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); }); + + if (can_undo){ + wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo")); + btn_szr->Add(btn_undo, 0, wxALL, 10); + btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::undo_desktop_intgration(); EndModal(wxID_ANY); }); + } + wxButton *btn_cancel = new wxButton(this, wxID_ANY, _L("Cancel")); + btn_szr->Add(btn_cancel, 0, wxALL, 10); + btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_ANY); }); + + vbox->Add(btn_szr, 0, wxALIGN_CENTER); + + SetSizerAndFit(vbox); + + +/* + //boldfont.SetWeight(wxFONTWEIGHT_BOLD); + + //this->SetFont(wxGetApp().normal_font()); + + auto *topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *rightsizer = new wxBoxSizer(wxVERTICAL); + + auto *headtext = new wxStaticText(this, wxID_ANY, headline); + headtext->SetFont(boldfont); + headtext->Wrap(50*wxGetApp().em_unit()); + rightsizer->Add(headtext); + rightsizer->AddSpacer(VERT_SPACING); + + rightsizer->Add(content_sizer, 1, wxEXPAND); + + if (button_id != wxID_NONE) { + auto *button = new wxButton(this, button_id); + button->SetFocus(); + btn_sizer->Add(button); + } + + rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT); + + if (! bitmap.IsOk()) { + bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192); + } + + logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); + + topsizer->Add(logo, 0, wxALL, BORDER); + topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER); + + SetSizerAndFit(topsizer); + */ +} + +DesktopIntegrationDialog::~DesktopIntegrationDialog() +{ + +} + +} // namespace GUI +} // namespace Slic3r +#endif // __linux__ \ No newline at end of file diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.hpp b/src/slic3r/GUI/DesktopIntegrationDialog.hpp new file mode 100644 index 000000000..74a0a68f9 --- /dev/null +++ b/src/slic3r/GUI/DesktopIntegrationDialog.hpp @@ -0,0 +1,39 @@ +#ifdef __linux__ +#ifndef slic3r_DesktopIntegrationDialog_hpp_ +#define slic3r_DesktopIntegrationDialog_hpp_ + +#include + +namespace Slic3r { +namespace GUI { +class DesktopIntegrationDialog : public wxDialog +{ +public: + DesktopIntegrationDialog(wxWindow *parent); + DesktopIntegrationDialog(DesktopIntegrationDialog &&) = delete; + DesktopIntegrationDialog(const DesktopIntegrationDialog &) = delete; + DesktopIntegrationDialog &operator=(DesktopIntegrationDialog &&) = delete; + DesktopIntegrationDialog &operator=(const DesktopIntegrationDialog &) = delete; + ~DesktopIntegrationDialog(); + + // methods that actually do / undo desktop integration. Static to be accesible from anywhere. + + // returns true if path to PrusaSlicer.desktop is stored in App Config and existence of desktop file. + // Does not check if desktop file leads to this binary or existence of icons and viewer desktop file. + static bool is_integrated(); + // true if appimage + static bool integration_possible(); + // Creates Desktop files and icons for both PrusaSlicer and GcodeViewer. + // Stores paths into App Config. + // Rewrites if files already existed. + static void perform_desktop_integration(); + // Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config. + static void undo_desktop_intgration(); +private: + +}; +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_DesktopIntegrationDialog_hpp_ +#endif // __linux__ \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 62961a867..cb7c2d24e 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -69,6 +69,7 @@ #include "UnsavedChangesDialog.hpp" #include "SavePresetDialog.hpp" #include "PrintHostDialogs.hpp" +#include "DesktopIntegrationDialog.hpp" #include "BitmapCache.hpp" @@ -1634,6 +1635,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots")); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot")); local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates")); +#ifdef __linux__ + if (DesktopIntegrationDialog::integration_possible()) + local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); +#endif local_menu->AppendSeparator(); } local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots + @@ -1674,6 +1679,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu) case ConfigMenuUpdate: check_updates(true); break; +#ifdef __linux__ + case ConfigMenuDesktopIntegration: + show_desktop_integration_dialog(); + break; +#endif case ConfigMenuTakeSnapshot: // Take a configuration snapshot. if (check_unsaved_changes()) { @@ -2066,6 +2076,15 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage return res; } +void GUI_App::show_desktop_integration_dialog() +{ +#ifdef __linux__ + //wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); + DesktopIntegrationDialog dialog(mainframe); + dialog.ShowModal(); +#endif //__linux__ +} + #if ENABLE_THUMBNAIL_GENERATOR_DEBUG void GUI_App::gcode_thumbnails_debug() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index f1ee0746a..bc030a1bf 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -76,6 +76,7 @@ enum ConfigMenuIDs { ConfigMenuSnapshots, ConfigMenuTakeSnapshot, ConfigMenuUpdate, + ConfigMenuDesktopIntegration, ConfigMenuPreferences, ConfigMenuModeSimple, ConfigMenuModeAdvanced, @@ -268,6 +269,7 @@ public: void open_web_page_localized(const std::string &http_address); bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME); + void show_desktop_integration_dialog(); #if ENABLE_THUMBNAIL_GENERATOR_DEBUG // temporary and debug only -> extract thumbnails from selected gcode and save them as png files diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 17657948e..1bcb93de0 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -82,7 +82,13 @@ enum class NotificationType // Notification emitted by Print::validate PrintValidateWarning, // Notification telling user to quit SLA supports manual editing - QuitSLAManualMode + QuitSLAManualMode, + // Desktop integration basic info + DesktopIntegrationSuccess, + DesktopIntegrationFail, + UndoDesktopIntegrationSuccess, + UndoDesktopIntegrationFail + }; class NotificationManager @@ -514,6 +520,14 @@ private: "To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") }, {NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10, _u8L("This model doesn't allow to automatically add the color changes") }, + {NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10, + _u8L("Desktop integration was successful.") }, + {NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10, + _u8L("Desktop integration failed.") }, + {NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10, + _u8L("Undo desktop integration was successful.") }, + {NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10, + _u8L("Undo desktop integration failed.") }, //{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") }, //{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") }, //{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification From 9cfcba78f792a7262a0951ec1e56ea129373805c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 7 May 2021 09:01:47 +0200 Subject: [PATCH 45/79] text fix --- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 37 --------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 1d07b137e..f4497b997 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -472,7 +472,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent) new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)")) )) , cbox_integrate(append( - new wxCheckBox(this, wxID_ANY, _L("Perform desktop integration (This will set shortcuts to PrusaSlicer to this Appimage executable).")) + new wxCheckBox(this, wxID_ANY, _L("Perform desktop integration (Sets this binary to be searchable by the system).")) )) { welcome_text->Hide(); diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index 25a5ab575..8ac134f5f 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -390,43 +390,6 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) vbox->Add(btn_szr, 0, wxALIGN_CENTER); SetSizerAndFit(vbox); - - -/* - //boldfont.SetWeight(wxFONTWEIGHT_BOLD); - - //this->SetFont(wxGetApp().normal_font()); - - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *rightsizer = new wxBoxSizer(wxVERTICAL); - - auto *headtext = new wxStaticText(this, wxID_ANY, headline); - headtext->SetFont(boldfont); - headtext->Wrap(50*wxGetApp().em_unit()); - rightsizer->Add(headtext); - rightsizer->AddSpacer(VERT_SPACING); - - rightsizer->Add(content_sizer, 1, wxEXPAND); - - if (button_id != wxID_NONE) { - auto *button = new wxButton(this, button_id); - button->SetFocus(); - btn_sizer->Add(button); - } - - rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT); - - if (! bitmap.IsOk()) { - bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192); - } - - logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); - - topsizer->Add(logo, 0, wxALL, BORDER); - topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER); - - SetSizerAndFit(topsizer); - */ } DesktopIntegrationDialog::~DesktopIntegrationDialog() From 68fabfea626d80ba1627d694c9752feab814734a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 7 May 2021 09:45:27 +0200 Subject: [PATCH 46/79] Fix of Polygon::area(). --- src/libslic3r/Polygon.cpp | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index e744272ac..66aa224ed 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -33,32 +33,16 @@ Polyline Polygon::split_at_index(int index) const return polyline; } -/* -int64_t Polygon::area2x() const -{ - size_t n = poly.size(); - if (n < 3) - return 0; - - int64_t a = 0; - for (size_t i = 0, j = n - 1; i < n; ++i) - a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1)); - j = i; - } - return -a * 0.5; -} -*/ - double Polygon::area(const Points &points) { - size_t n = points.size(); - if (n < 3) - return 0.; - double a = 0.; - for (size_t i = 0, j = n - 1; i < n; ++i) { - a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1)); - j = i; + if (points.size() >= 3) { + Vec2d p1 = points.back().cast(); + for (const Point &p : points) { + Vec2d p2 = p.cast(); + a += cross2(p1, p2); + p1 = p2; + } } return 0.5 * a; } From 52b3c655ffd43ba4ad2819729ece4485aa4346e5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 7 May 2021 11:42:01 +0200 Subject: [PATCH 47/79] Fixed Polygon::centroid() Ported Polygon unit tests from Perl to C++. --- src/libslic3r/Polygon.cpp | 22 ++++--- src/libslic3r/Polyline.hpp | 3 + tests/libslic3r/test_polygon.cpp | 106 +++++++++++++++++++++++++++++++ xs/t/06_polygon.t | 78 +---------------------- 4 files changed, 122 insertions(+), 87 deletions(-) diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 66aa224ed..9bfd359c2 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -155,17 +155,19 @@ void Polygon::triangulate_convex(Polygons* polygons) const // center of mass Point Polygon::centroid() const { - double area_temp = this->area(); - double x_temp = 0; - double y_temp = 0; - - Polyline polyline = this->split_at_first_point(); - for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) { - x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); - y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); + double area_sum = 0.; + Vec2d c(0., 0.); + if (points.size() >= 3) { + Vec2d p1 = points.back().cast(); + for (const Point &p : points) { + Vec2d p2 = p.cast(); + double a = cross2(p1, p2); + area_sum += a; + c += (p1 + p2) * a; + p1 = p2; + } } - - return Point(x_temp/(6*area_temp), y_temp/(6*area_temp)); + return Point(Vec2d(c / (3. * area_sum))); } // find all concave vertices (i.e. having an internal angle greater than the supplied angle) diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 88f910590..51dcf9d36 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -78,6 +78,9 @@ public: bool is_closed() const { return this->points.front() == this->points.back(); } }; +inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; } +inline bool operator!=(const Polyline &lhs, const Polyline &rhs) { return lhs.points != rhs.points; } + // Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! #ifdef PERL_UCHAR_MIN class PolylineCollection diff --git a/tests/libslic3r/test_polygon.cpp b/tests/libslic3r/test_polygon.cpp index d45e37fb1..f2c78cace 100644 --- a/tests/libslic3r/test_polygon.cpp +++ b/tests/libslic3r/test_polygon.cpp @@ -5,6 +5,112 @@ using namespace Slic3r; +SCENARIO("Converted Perl tests", "[Polygon]") { + GIVEN("ccw_square") { + Polygon ccw_square{ { 100, 100 }, { 200, 100 }, { 200, 200 }, { 100, 200 } }; + Polygon cw_square(ccw_square); + cw_square.reverse(); + + THEN("ccw_square is valid") { + REQUIRE(ccw_square.is_valid()); + } + THEN("cw_square is valid") { + REQUIRE(cw_square.is_valid()); + } + THEN("ccw_square.area") { + REQUIRE(ccw_square.area() == 100 * 100); + } + THEN("cw_square.area") { + REQUIRE(cw_square.area() == - 100 * 100); + } + THEN("ccw_square.centroid") { + REQUIRE(ccw_square.centroid() == Point { 150, 150 }); + } + THEN("cw_square.centroid") { + REQUIRE(cw_square.centroid() == Point { 150, 150 }); + } + THEN("ccw_square.contains_point(150, 150)") { + REQUIRE(ccw_square.contains({ 150, 150 })); + } + THEN("cw_square.contains_point(150, 150)") { + REQUIRE(cw_square.contains({ 150, 150 })); + } + THEN("conversion to lines") { + REQUIRE(ccw_square.lines() == Lines{ + { { 100, 100 }, { 200, 100 } }, + { { 200, 100 }, { 200, 200 } }, + { { 200, 200 }, { 100, 200 } }, + { { 100, 200 }, { 100, 100 } } }); + } + THEN("split_at_first_point") { + REQUIRE(ccw_square.split_at_first_point() == Polyline { ccw_square[0], ccw_square[1], ccw_square[2], ccw_square[3], ccw_square[0] }); + } + THEN("split_at_index(2)") { + REQUIRE(ccw_square.split_at_index(2) == Polyline { ccw_square[2], ccw_square[3], ccw_square[0], ccw_square[1], ccw_square[2] }); + } + THEN("split_at_vertex(ccw_square[2])") { + REQUIRE(ccw_square.split_at_vertex(ccw_square[2]) == Polyline { ccw_square[2], ccw_square[3], ccw_square[0], ccw_square[1], ccw_square[2] }); + } + THEN("is_counter_clockwise") { + REQUIRE(ccw_square.is_counter_clockwise()); + } + THEN("! is_counter_clockwise") { + REQUIRE(! cw_square.is_counter_clockwise()); + } + THEN("make_counter_clockwise") { + cw_square.make_counter_clockwise(); + REQUIRE(cw_square.is_counter_clockwise()); + } + THEN("make_counter_clockwise^2") { + cw_square.make_counter_clockwise(); + cw_square.make_counter_clockwise(); + REQUIRE(cw_square.is_counter_clockwise()); + } + THEN("first_point") { + REQUIRE(&ccw_square.first_point() == &ccw_square.points.front()); + } + } + GIVEN("Triangulating hexagon") { + Polygon hexagon{ { 100, 0 } }; + for (size_t i = 1; i < 6; ++ i) { + Point p = hexagon.points.front(); + p.rotate(PI / 3 * i); + hexagon.points.emplace_back(p); + } + Polygons triangles; + hexagon.triangulate_convex(&triangles); + THEN("right number of triangles") { + REQUIRE(triangles.size() == 4); + } + THEN("all triangles are ccw") { + auto it = std::find_if(triangles.begin(), triangles.end(), [](const Polygon &tri) { return tri.is_clockwise(); }); + REQUIRE(it == triangles.end()); + } + } + GIVEN("General triangle") { + Polygon polygon { { 50000000, 100000000 }, { 300000000, 102000000 }, { 50000000, 104000000 } }; + Line line { { 175992032, 102000000 }, { 47983964, 102000000 } }; + Point intersection; + bool has_intersection = polygon.intersection(line, &intersection); + THEN("Intersection with line") { + REQUIRE(has_intersection); + REQUIRE(intersection == Point { 50000000, 102000000 }); + } + } +} + +TEST_CASE("Centroid of Trapezoid must be inside", "[Polygon][Utils]") +{ + Slic3r::Polygon trapezoid { + { 4702134, 1124765853 }, + { -4702134, 1124765853 }, + { -9404268, 1049531706 }, + { 9404268, 1049531706 }, + }; + Point centroid = trapezoid.centroid(); + CHECK(trapezoid.contains(centroid)); +} + // This test currently only covers remove_collinear_points. // All remaining tests are to be ported from xs/t/06_polygon.t diff --git a/xs/t/06_polygon.t b/xs/t/06_polygon.t index 779c7deec..7bbcd5356 100644 --- a/xs/t/06_polygon.t +++ b/xs/t/06_polygon.t @@ -3,11 +3,8 @@ use strict; use warnings; -use List::Util qw(first); use Slic3r::XS; -use Test::More tests => 21; - -use constant PI => 4 * atan2(1, 1); +use Test::More tests => 3; my $square = [ # ccw [100, 100], @@ -17,81 +14,8 @@ my $square = [ # ccw ]; my $polygon = Slic3r::Polygon->new(@$square); -my $cw_polygon = $polygon->clone; -$cw_polygon->reverse; - -ok $polygon->is_valid, 'is_valid'; -is_deeply $polygon->pp, $square, 'polygon roundtrip'; - is ref($polygon->arrayref), 'ARRAY', 'polygon arrayref is unblessed'; isa_ok $polygon->[0], 'Slic3r::Point::Ref', 'polygon point is blessed'; - -my $lines = $polygon->lines; -is_deeply [ map $_->pp, @$lines ], [ - [ [100, 100], [200, 100] ], - [ [200, 100], [200, 200] ], - [ [200, 200], [100, 200] ], - [ [100, 200], [100, 100] ], -], 'polygon lines'; - -is_deeply $polygon->split_at_first_point->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point'; -is_deeply $polygon->split_at_index(2)->pp, [ @$square[2,3,0,1,2] ], 'split_at_index'; -is_deeply $polygon->split_at_vertex(Slic3r::Point->new(@{$square->[2]}))->pp, [ @$square[2,3,0,1,2] ], 'split_at'; -is $polygon->area, 100*100, 'area'; - -ok $polygon->is_counter_clockwise, 'is_counter_clockwise'; -ok !$cw_polygon->is_counter_clockwise, 'is_counter_clockwise'; -{ - my $clone = $polygon->clone; - $clone->reverse; - ok !$clone->is_counter_clockwise, 'is_counter_clockwise'; - $clone->make_counter_clockwise; - ok $clone->is_counter_clockwise, 'make_counter_clockwise'; - $clone->make_counter_clockwise; - ok $clone->is_counter_clockwise, 'make_counter_clockwise'; -} - ok ref($polygon->first_point) eq 'Slic3r::Point', 'first_point'; -ok $polygon->contains_point(Slic3r::Point->new(150,150)), 'ccw contains_point'; -ok $cw_polygon->contains_point(Slic3r::Point->new(150,150)), 'cw contains_point'; - -{ - my @points = (Slic3r::Point->new(100,0)); - foreach my $i (1..5) { - my $point = $points[0]->clone; - $point->rotate(PI/3*$i, [0,0]); - push @points, $point; - } - my $hexagon = Slic3r::Polygon->new(@points); - my $triangles = $hexagon->triangulate_convex; - is scalar(@$triangles), 4, 'right number of triangles'; - ok !(defined first { $_->is_clockwise } @$triangles), 'all triangles are ccw'; -} - -{ - is_deeply $polygon->centroid->pp, [150,150], 'centroid'; -} - -{ - my $polygon = Slic3r::Polygon->new( - [50000000, 100000000], - [300000000, 102000000], - [50000000, 104000000], - ); - my $line = Slic3r::Line->new([175992032,102000000], [47983964,102000000]); - my $intersection = $polygon->intersection($line); - is_deeply $intersection->pp, [50000000, 102000000], 'polygon-line intersection'; -} - -# this is not a test: this just demonstrates bad usage, where $polygon->clone gets -# DESTROY'ed before the derived object ($point), causing bad memory access -if (0) { - my $point; - { - $point = $polygon->clone->[0]; - } - $point->scale(2); -} - __END__ From 5c35fa4539fa7430fb133c874c8da9c007fca13d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 7 May 2021 11:54:25 +0200 Subject: [PATCH 48/79] Commenting source of Polygon::centroid() algorithm --- src/libslic3r/Polygon.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 9bfd359c2..5b180afca 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -153,6 +153,7 @@ void Polygon::triangulate_convex(Polygons* polygons) const } // center of mass +// source: https://en.wikipedia.org/wiki/Centroid Point Polygon::centroid() const { double area_sum = 0.; From 96447de1d4391a89049394b0cfd8fa56d9d0faa7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 May 2021 12:17:17 +0200 Subject: [PATCH 49/79] ConfigWizard:: Use wxTextCtrl instead of wxDoubleSpinCtrl for nozzle and filament diameters --- src/slic3r/GUI/ConfigWizard.cpp | 49 +++++++++++++++++++------ src/slic3r/GUI/ConfigWizard_private.hpp | 4 +- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index f4497b997..52df39fd5 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -33,6 +33,7 @@ #include "GUI_App.hpp" #include "GUI_Utils.hpp" #include "GUI_ObjectManipulation.hpp" +#include "Field.hpp" #include "DesktopIntegrationDialog.hpp" #include "slic3r/Config/Snapshot.hpp" #include "slic3r/Utils/PresetUpdater.hpp" @@ -1383,20 +1384,39 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config) config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model)); } +static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value) +{ + e.Skip(); + wxString str = ctrl->GetValue(); + // Replace the first occurence of comma in decimal number. + bool was_replace = str.Replace(",", ".", false) > 0; + double val = 0.0; + if (!str.ToCDouble(&val)) { + if (val == 0.0) + val = def_value; + ctrl->SetValue(double_to_string(val)); + show_error(nullptr, _L("Invalid numeric input.")); + ctrl->SetFocus(); + } + else if (was_replace) + ctrl->SetValue(double_to_string(val)); +} + PageDiameters::PageDiameters(ConfigWizard *parent) : ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1) - , spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)) - , spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) + , diam_nozzle(new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord))) + , diam_filam (new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord))) { - spin_nozzle->SetDigits(2); - spin_nozzle->SetIncrement(0.1); auto *default_nozzle = print_config_def.get("nozzle_diameter")->get_default_value(); - spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); + wxString value = double_to_string(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); + diam_nozzle->SetValue(value); - spin_filam->SetDigits(2); - spin_filam->SetIncrement(0.25); auto *default_filam = print_config_def.get("filament_diameter")->get_default_value(); - spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); + value = double_to_string(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); + diam_filam->SetValue(value); + + diam_nozzle->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { focus_event(e, diam_nozzle, 0.5); }, diam_nozzle->GetId()); + diam_filam ->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { focus_event(e, diam_filam , 3.0); }, diam_filam->GetId()); append_text(_L("Enter the diameter of your printer's hot end nozzle.")); @@ -1405,7 +1425,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent) auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm")); sizer_nozzle->AddGrowableCol(0, 1); sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - sizer_nozzle->Add(spin_nozzle); + sizer_nozzle->Add(diam_nozzle); sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); append(sizer_nozzle); @@ -1419,16 +1439,21 @@ PageDiameters::PageDiameters(ConfigWizard *parent) auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm")); sizer_filam->AddGrowableCol(0, 1); sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); - sizer_filam->Add(spin_filam); + sizer_filam->Add(diam_filam); sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); append(sizer_filam); } void PageDiameters::apply_custom_config(DynamicPrintConfig &config) { - auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); + double val = 0.0; + diam_nozzle->GetValue().ToCDouble(&val); + auto *opt_nozzle = new ConfigOptionFloats(1, val); config.set_key_value("nozzle_diameter", opt_nozzle); - auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); + + val = 0.0; + diam_filam->GetValue().ToCDouble(&val); + auto * opt_filam = new ConfigOptionFloats(1, val); config.set_key_value("filament_diameter", opt_filam); auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) { diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 4e3f1538e..ea39e04ab 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -451,8 +451,8 @@ struct PageBedShape: ConfigWizardPage struct PageDiameters: ConfigWizardPage { - wxSpinCtrlDouble *spin_nozzle; - wxSpinCtrlDouble *spin_filam; + wxTextCtrl *diam_nozzle; + wxTextCtrl *diam_filam; PageDiameters(ConfigWizard *parent); virtual void apply_custom_config(DynamicPrintConfig &config); From ddf59a4a8cb88f635a8c321e36b125bb0c43815c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Apr 2021 11:39:52 +0200 Subject: [PATCH 50/79] Tech ENABLE_SCROLLABLE_LEGEND -> 1st installment of scrollable legend --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 310 +++++++++++++++++++-------------- src/slic3r/GUI/GLCanvas3D.cpp | 17 +- src/slic3r/GUI/GLCanvas3D.hpp | 4 + 4 files changed, 196 insertions(+), 137 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a28d23bcc..edb6e6bcb 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,8 @@ #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) // Enable a modified version of automatic downscale on load of objects too big #define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) +// Enable scrollable legend in preview +#define ENABLE_SCROLLABLE_LEGEND (1 && ENABLE_2_4_0_ALPHA0) // Enable visualization of start gcode as regular toolpaths #define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) // Enable visualization of seams in preview diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ca1617bc2..bb186622f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4051,14 +4051,23 @@ void GCodeViewer::render_legend() const if (!m_legend_enabled) return; +#if ENABLE_SCROLLABLE_LEGEND + Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); +#endif // ENABLE_SCROLLABLE_LEGEND + ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(0.0f, 0.0f, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); +#if ENABLE_SCROLLABLE_LEGEND + float child_height = 0.2222f * static_cast(cnv_size.get_height()); + imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); +#else imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImDrawList* draw_list = ImGui::GetWindowDrawList(); +#endif // ENABLE_SCROLLABLE_LEGEND enum class EItemType : unsigned char { @@ -4069,94 +4078,106 @@ void GCodeViewer::render_legend() const }; const PrintEstimatedTimeStatistics::Mode& time_mode = m_time_statistics.modes[static_cast(m_time_estimate_mode)]; +#if ENABLE_SCROLLABLE_LEGEND + bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || + (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); +#endif // ENABLE_SCROLLABLE_LEGEND float icon_size = ImGui::GetTextLineHeight(); float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); +#if ENABLE_SCROLLABLE_LEGEND + auto append_item = [this, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, +#else auto append_item = [this, draw_list, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, +#endif // ENABLE_SCROLLABLE_LEGEND bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array& offsets = { 0.0f, 0.0f }, std::function callback = nullptr) { - if (!visible) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - ImVec2 pos = ImGui::GetCursorScreenPos(); - switch (type) { - default: - case EItemType::Rect: { - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); - break; - } - case EItemType::Circle: { - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { - draw_list->AddCircleFilled(center, 0.5f * icon_size, - ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = 0.5f * icon_size; - draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - radius = 0.5f * icon_size * 0.01f * 33.0f; - draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - } - else - draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + if (!visible) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - break; - } - case EItemType::Hexagon: { - ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); - break; - } - case EItemType::Line: { - draw_list->AddLine({ pos.x + 1, pos.y + icon_size - 1 }, { pos.x + icon_size - 1, pos.y + 1 }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); - break; - } - } - - // draw text - ImGui::Dummy({ icon_size, icon_size }); - ImGui::SameLine(); - if (callback != nullptr) { - if (ImGui::MenuItem(label.c_str())) - callback(); - else { - // show tooltip - if (ImGui::IsItemHovered()) { - if (!visible) - ImGui::PopStyleVar(); - ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); - ImGui::BeginTooltip(); - imgui.text(visible ? _u8L("Click to hide") : _u8L("Click to show")); - ImGui::EndTooltip(); - ImGui::PopStyleColor(); - if (!visible) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - - // to avoid the tooltip to change size when moving the mouse - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); - } - } - - if (!time.empty()) { - ImGui::SameLine(offsets[0]); - imgui.text(time); - ImGui::SameLine(offsets[1]); - pos = ImGui::GetCursorScreenPos(); - float width = std::max(1.0f, percent_bar_size * percent / max_percent); - draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, - ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); - ImGui::Dummy({ percent_bar_size, icon_size }); - ImGui::SameLine(); - char buf[64]; - ::sprintf(buf, "%.1f%%", 100.0f * percent); - ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); - } +#if ENABLE_SCROLLABLE_LEGEND + ImDrawList* draw_list = ImGui::GetWindowDrawList(); +#endif // ENABLE_SCROLLABLE_LEGEND + ImVec2 pos = ImGui::GetCursorScreenPos(); + switch (type) { + default: + case EItemType::Rect: { + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + break; + } + case EItemType::Circle: { + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { + draw_list->AddCircleFilled(center, 0.5f * icon_size, + ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); + float radius = 0.5f * icon_size; + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + radius = 0.5f * icon_size * 0.01f * 33.0f; + draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); } else - imgui.text(label); + draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); - if (!visible) - ImGui::PopStyleVar(); + break; + } + case EItemType::Hexagon: { + ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); + break; + } + case EItemType::Line: { + draw_list->AddLine({ pos.x + 1, pos.y + icon_size - 1 }, { pos.x + icon_size - 1, pos.y + 1 }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); + break; + } + } + + // draw text + ImGui::Dummy({ icon_size, icon_size }); + ImGui::SameLine(); + if (callback != nullptr) { + if (ImGui::MenuItem(label.c_str())) + callback(); + else { + // show tooltip + if (ImGui::IsItemHovered()) { + if (!visible) + ImGui::PopStyleVar(); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(visible ? _u8L("Click to hide") : _u8L("Click to show")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + if (!visible) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + + // to avoid the tooltip to change size when moving the mouse + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + } + + if (!time.empty()) { + ImGui::SameLine(offsets[0]); + imgui.text(time); + ImGui::SameLine(offsets[1]); + pos = ImGui::GetCursorScreenPos(); + float width = std::max(1.0f, percent_bar_size * percent / max_percent); + draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, + ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); + ImGui::Dummy({ percent_bar_size, icon_size }); + ImGui::SameLine(); + char buf[64]; + ::sprintf(buf, "%.1f%%", 100.0f * percent); + ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); + } + } + else + imgui.text(label); + + if (!visible) + ImGui::PopStyleVar(); }; auto append_range = [append_item](const Extrusions::Range& range, unsigned int decimals) { @@ -4254,7 +4275,7 @@ void GCodeViewer::render_legend() const return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); }; - auto role_time_and_percent = [ time_mode](ExtrusionRole role) { + auto role_time_and_percent = [time_mode](ExtrusionRole role) { auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f); }; @@ -4341,57 +4362,65 @@ void GCodeViewer::render_legend() const } case EViewType::ColorPrint: { - const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; - if (m_extruders_count == 1) { // single extruder use case - std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); - const int items_cnt = static_cast(cp_values.size()); - if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); - } - else { - for (int i = items_cnt; i >= 0; --i) { - // create label for color change item - if (i == 0) { - append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first)); - break; - } - else if (i == items_cnt) { - append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); - continue; - } - append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); - } - } - } - else { // multi extruder use case - // shows only extruders actually used - for (unsigned char i : m_extruder_ids) { - std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); +#if ENABLE_SCROLLABLE_LEGEND + // add scrollable region + if (ImGui::BeginChild("color_prints", { -1.0f, child_height }, false)) { +#endif // ENABLE_SCROLLABLE_LEGEND + const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + if (m_extruders_count == 1) { // single extruder use case + std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); + append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); } else { - for (int j = items_cnt; j >= 0; --j) { + for (int i = items_cnt; i >= 0; --i) { // create label for color change item - std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); - if (j == 0) { - label += " " + upto_label(cp_values.front().second.first); - append_item(EItemType::Rect, m_tool_colors[i], label); + if (i == 0) { + append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first)); break; } - else if (j == items_cnt) { - label += " " + above_label(cp_values[j - 1].second.second); - append_item(EItemType::Rect, cp_values[j - 1].first, label); + else if (i == items_cnt) { + append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); continue; } - - label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); - append_item(EItemType::Rect, cp_values[j - 1].first, label); + append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); } } } + else { // multi extruder use case + // shows only extruders actually used + for (unsigned char i : m_extruder_ids) { + std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); + const int items_cnt = static_cast(cp_values.size()); + if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); + } + else { + for (int j = items_cnt; j >= 0; --j) { + // create label for color change item + std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); + if (j == 0) { + label += " " + upto_label(cp_values.front().second.first); + append_item(EItemType::Rect, m_tool_colors[i], label); + break; + } + else if (j == items_cnt) { + label += " " + above_label(cp_values[j - 1].second.second); + append_item(EItemType::Rect, cp_values[j - 1].first, label); + continue; + } + + label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); + append_item(EItemType::Rect, cp_values[j - 1].first, label); + } + } + } + } +#if ENABLE_SCROLLABLE_LEGEND } + ImGui::EndChild(); +#endif // ENABLE_SCROLLABLE_LEGEND break; } @@ -4517,26 +4546,35 @@ void GCodeViewer::render_legend() const offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time") }, 2.0f * icon_size); ImGui::Spacing(); - append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); - for (const PartialTime& item : partial_times) { - switch (item.type) - { - case PartialTime::EType::Print: { - append_print(item.color1, offsets, item.times); - break; - } - case PartialTime::EType::Pause: { - imgui.text(_u8L("Pause")); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); - break; - } - case PartialTime::EType::ColorChange: { - append_color_change(item.color1, item.color2, offsets, item.times); - break; - } + append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); +#if ENABLE_SCROLLABLE_LEGEND + // add scrollable region + if (ImGui::BeginChild("events", { -1.0f, child_height }, false)) { +#endif // ENABLE_SCROLLABLE_LEGEND + for (const PartialTime& item : partial_times) { + switch (item.type) + { + case PartialTime::EType::Print: { + append_print(item.color1, offsets, item.times); + break; + } + case PartialTime::EType::Pause: { + imgui.text(_u8L("Pause")); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); + break; + } + case PartialTime::EType::ColorChange: { + append_color_change(item.color1, item.color2, offsets, item.times); + break; + } + } } +#if ENABLE_SCROLLABLE_LEGEND } + ImGui::EndChild(); +#endif // ENABLE_SCROLLABLE_LEGEND + } } @@ -4680,10 +4718,14 @@ void GCodeViewer::render_legend() const } // total estimated printing time section +#if ENABLE_SCROLLABLE_LEGEND + if (show_estimated_time) { +#else if (time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()))) { ImGui::Spacing(); +#endif // ENABLE_SCROLLABLE_LEGEND ImGui::Spacing(); ImGui::PushStyleColor(ImGuiCol_Separator, { 1.0f, 1.0f, 1.0f, 1.0f }); ImGui::Separator(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 26c4a314c..d4f037998 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4780,8 +4780,16 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) if (m_canvas == nullptr && m_context == nullptr) return; +#if ENABLE_SCROLLABLE_LEGEND + const std::array new_size = { w, h }; + if (m_old_size == new_size) + return; + + m_old_size = new_size; +#endif // ENABLE_SCROLLABLE_LEGEND + auto *imgui = wxGetApp().imgui(); - imgui->set_display_size((float)w, (float)h); + imgui->set_display_size(static_cast(w), static_cast(h)); const float font_size = 1.5f * wxGetApp().em_unit(); #if ENABLE_RETINA_GL imgui->set_scaling(font_size, 1.0f, m_retina_helper->get_scale_factor()); @@ -4789,6 +4797,10 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) imgui->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f); #endif +#if ENABLE_SCROLLABLE_LEGEND + this->request_extra_frame(); +#endif // ENABLE_SCROLLABLE_LEGEND + // ensures that this canvas is current _set_current(); } @@ -4829,8 +4841,7 @@ void GLCanvas3D::_update_camera_zoom(double zoom) void GLCanvas3D::_refresh_if_shown_on_screen() { - if (_is_shown_on_screen()) - { + if (_is_shown_on_screen()) { const Size& cnv_size = get_canvas_size(); _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9e9a2501e..0e1bb7229 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -474,6 +474,10 @@ private: Model* m_model; BackgroundSlicingProcess *m_process; +#if ENABLE_SCROLLABLE_LEGEND + std::array m_old_size{ 0, 0 }; +#endif // ENABLE_SCROLLABLE_LEGEND + // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; bool m_initialized; From 49503db65eea7336072d8603584e1d6bf996ef8c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Sat, 1 May 2021 12:24:24 +0200 Subject: [PATCH 51/79] Tech ENABLE_SCROLLABLE_LEGEND -> Set legend max height --- src/slic3r/GUI/GCodeViewer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bb186622f..dead0c795 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4061,7 +4061,9 @@ void GCodeViewer::render_legend() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); #if ENABLE_SCROLLABLE_LEGEND - float child_height = 0.2222f * static_cast(cnv_size.get_height()); + float max_height = 0.75f * static_cast(cnv_size.get_height()); + float child_height = 0.3333f * max_height; + ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height }); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); #else imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); From c602e655e05d8b7157af3319d4d7550695db6821 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 May 2021 14:40:37 +0200 Subject: [PATCH 52/79] Tech ENABLE_SCROLLABLE_LEGEND -> Fixed layout of scrollable sub panels --- src/slic3r/GUI/GCodeViewer.cpp | 164 ++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 76 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index dead0c795..ed1ae7af2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4052,7 +4052,7 @@ void GCodeViewer::render_legend() const return; #if ENABLE_SCROLLABLE_LEGEND - Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); #endif // ENABLE_SCROLLABLE_LEGEND ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -4061,8 +4061,8 @@ void GCodeViewer::render_legend() const ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); #if ENABLE_SCROLLABLE_LEGEND - float max_height = 0.75f * static_cast(cnv_size.get_height()); - float child_height = 0.3333f * max_height; + const float max_height = 0.75f * static_cast(cnv_size.get_height()); + const float child_height = 0.3333f * max_height; ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height }); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); #else @@ -4085,8 +4085,8 @@ void GCodeViewer::render_legend() const (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); #endif // ENABLE_SCROLLABLE_LEGEND - float icon_size = ImGui::GetTextLineHeight(); - float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); + const float icon_size = ImGui::GetTextLineHeight(); + const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); #if ENABLE_SCROLLABLE_LEGEND auto append_item = [this, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, @@ -4114,7 +4114,7 @@ void GCodeViewer::render_legend() const if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - float radius = 0.5f * icon_size; + const float radius = 0.5f * icon_size; draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); radius = 0.5f * icon_size * 0.01f * 33.0f; draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); @@ -4165,7 +4165,7 @@ void GCodeViewer::render_legend() const imgui.text(time); ImGui::SameLine(offsets[1]); pos = ImGui::GetCursorScreenPos(); - float width = std::max(1.0f, percent_bar_size * percent / max_percent); + const float width = std::max(1.0f, percent_bar_size * percent / max_percent); draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); ImGui::Dummy({ percent_bar_size, icon_size }); @@ -4197,7 +4197,7 @@ void GCodeViewer::render_legend() const append_range_item(0, range.min, decimals); } else { - float step_size = range.step_size(); + const float step_size = range.step_size(); for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { append_range_item(i, range.min + static_cast(i) * step_size, decimals); } @@ -4246,8 +4246,8 @@ void GCodeViewer::render_legend() const if (lower_b == zs.end()) continue; - double current_z = *lower_b; - double previous_z = (lower_b == zs.begin()) ? 0.0 : *(--lower_b); + const double current_z = *lower_b; + const double previous_z = (lower_b == zs.begin()) ? 0.0 : *(--lower_b); // to avoid duplicate values, check adding values if (ret.empty() || !(ret.back().second.first == previous_z && ret.back().second.second == current_z)) @@ -4333,7 +4333,7 @@ void GCodeViewer::render_legend() const ExtrusionRole role = m_roles[i]; if (role >= erCount) continue; - bool visible = is_visible(role); + const bool visible = is_visible(role); append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], visible, times[i], percents[i], max_percent, offsets, [this, role, visible]() { Extrusions* extrusions = const_cast(&m_extrusions); @@ -4365,63 +4365,73 @@ void GCodeViewer::render_legend() const case EViewType::ColorPrint: { #if ENABLE_SCROLLABLE_LEGEND - // add scrollable region - if (ImGui::BeginChild("color_prints", { -1.0f, child_height }, false)) { + const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + size_t total_items = 1; + for (unsigned char i : m_extruder_ids) { + total_items += color_print_ranges(i, custom_gcode_per_print_z).size(); + } + + const bool need_scrollable = static_cast(total_items) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + + // add scrollable region, if needed + if (need_scrollable) + ImGui::BeginChild("color_prints", { -1.0f, child_height }, false); +#else + const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; #endif // ENABLE_SCROLLABLE_LEGEND - const std::vector& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; - if (m_extruders_count == 1) { // single extruder use case - std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); + if (m_extruders_count == 1) { // single extruder use case + const std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); + const int items_cnt = static_cast(cp_values.size()); + if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode + append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); + } + else { + for (int i = items_cnt; i >= 0; --i) { + // create label for color change item + if (i == 0) { + append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first)); + break; + } + else if (i == items_cnt) { + append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); + continue; + } + append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); + } + } + } + else { // multi extruder use case + // shows only extruders actually used + for (unsigned char i : m_extruder_ids) { + const std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Rect, m_tool_colors.front(), _u8L("Default color")); + append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); } else { - for (int i = items_cnt; i >= 0; --i) { + for (int j = items_cnt; j >= 0; --j) { // create label for color change item - if (i == 0) { - append_item(EItemType::Rect, m_tool_colors[0], upto_label(cp_values.front().second.first)); + std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); + if (j == 0) { + label += " " + upto_label(cp_values.front().second.first); + append_item(EItemType::Rect, m_tool_colors[i], label); break; } - else if (i == items_cnt) { - append_item(EItemType::Rect, cp_values[i - 1].first, above_label(cp_values[i - 1].second.second)); + else if (j == items_cnt) { + label += " " + above_label(cp_values[j - 1].second.second); + append_item(EItemType::Rect, cp_values[j - 1].first, label); continue; } - append_item(EItemType::Rect, cp_values[i - 1].first, fromto_label(cp_values[i - 1].second.second, cp_values[i].second.first)); - } - } - } - else { // multi extruder use case - // shows only extruders actually used - for (unsigned char i : m_extruder_ids) { - std::vector>> cp_values = color_print_ranges(i, custom_gcode_per_print_z); - const int items_cnt = static_cast(cp_values.size()); - if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode - append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1) + " " + _u8L("default color")); - } - else { - for (int j = items_cnt; j >= 0; --j) { - // create label for color change item - std::string label = _u8L("Extruder") + " " + std::to_string(i + 1); - if (j == 0) { - label += " " + upto_label(cp_values.front().second.first); - append_item(EItemType::Rect, m_tool_colors[i], label); - break; - } - else if (j == items_cnt) { - label += " " + above_label(cp_values[j - 1].second.second); - append_item(EItemType::Rect, cp_values[j - 1].first, label); - continue; - } - label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); - append_item(EItemType::Rect, cp_values[j - 1].first, label); - } + label += " " + fromto_label(cp_values[j - 1].second.second, cp_values[j].second.first); + append_item(EItemType::Rect, cp_values[j - 1].first, label); } } } -#if ENABLE_SCROLLABLE_LEGEND } - ImGui::EndChild(); +#if ENABLE_SCROLLABLE_LEGEND + if (need_scrollable) + ImGui::EndChild(); #endif // ENABLE_SCROLLABLE_LEGEND break; @@ -4548,35 +4558,37 @@ void GCodeViewer::render_legend() const offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time") }, 2.0f * icon_size); ImGui::Spacing(); - append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); + append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); #if ENABLE_SCROLLABLE_LEGEND + const bool need_scrollable = static_cast(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + if (need_scrollable) // add scrollable region - if (ImGui::BeginChild("events", { -1.0f, child_height }, false)) { + ImGui::BeginChild("events", { -1.0f, child_height }, false); #endif // ENABLE_SCROLLABLE_LEGEND - for (const PartialTime& item : partial_times) { - switch (item.type) - { - case PartialTime::EType::Print: { - append_print(item.color1, offsets, item.times); - break; - } - case PartialTime::EType::Pause: { - imgui.text(_u8L("Pause")); - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); - break; - } - case PartialTime::EType::ColorChange: { - append_color_change(item.color1, item.color2, offsets, item.times); - break; - } - } + for (const PartialTime& item : partial_times) { + switch (item.type) + { + case PartialTime::EType::Print: { + append_print(item.color1, offsets, item.times); + break; + } + case PartialTime::EType::Pause: { + imgui.text(_u8L("Pause")); + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); + break; + } + case PartialTime::EType::ColorChange: { + append_color_change(item.color1, item.color2, offsets, item.times); + break; + } } -#if ENABLE_SCROLLABLE_LEGEND } - ImGui::EndChild(); -#endif // ENABLE_SCROLLABLE_LEGEND +#if ENABLE_SCROLLABLE_LEGEND + if (need_scrollable) + ImGui::EndChild(); +#endif // ENABLE_SCROLLABLE_LEGEND } } From 56aa45fa1fabe7fbc3389f9881b109b34c39279f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 May 2021 14:50:53 +0200 Subject: [PATCH 53/79] Fixed typo --- src/slic3r/GUI/GCodeViewer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed1ae7af2..c7cb3a5d9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4114,7 +4114,7 @@ void GCodeViewer::render_legend() const if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); - const float radius = 0.5f * icon_size; + float radius = 0.5f * icon_size; draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); radius = 0.5f * icon_size * 0.01f * 33.0f; draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); @@ -4565,6 +4565,7 @@ void GCodeViewer::render_legend() const // add scrollable region ImGui::BeginChild("events", { -1.0f, child_height }, false); #endif // ENABLE_SCROLLABLE_LEGEND + for (const PartialTime& item : partial_times) { switch (item.type) { From b9910669e82d79dfe5a4312c296b34ac5a107f4a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 21 Apr 2021 14:57:43 +0200 Subject: [PATCH 54/79] Fix of #2825 - Add the length of each filament used --- src/libslic3r/GCode.cpp | 43 +++- src/libslic3r/GCode/GCodeProcessor.cpp | 264 +++++++++++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 76 +++++-- src/libslic3r/Print.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 221 ++++++++++++++++----- src/slic3r/GUI/GCodeViewer.hpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 13 +- xs/xsp/Print.xsp | 2 +- 9 files changed, 463 insertions(+), 164 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 01740ca7b..4368783a6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -614,9 +614,44 @@ namespace DoExport { static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) { const GCodeProcessor::Result& result = processor.get_result(); - print_statistics.estimated_normal_print_time = get_time_dhms(result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time); + print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? - get_time_dhms(result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A"; + get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A"; + } + + static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector& extruders, PrintStatistics& print_statistics) + { + const GCodeProcessor::Result& result = processor.get_result(); + print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); + print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? + get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A"; + + // update filament statictics + double total_extruded_volume = 0.0; + double total_used_filament = 0.0; + double total_weight = 0.0; + double total_cost = 0.0; + for (auto volume : result.print_statistics.volumes_per_extruder) { + total_extruded_volume += volume.second; + + size_t extruder_id = volume.first; + auto extruder = std::find_if(extruders.begin(), extruders.end(), [extruder_id](const Extruder& extr) { return extr.id() == extruder_id; }); + if (extruder == extruders.end()) + continue; + + double s = PI * sqr(0.5* extruder->filament_diameter()); + double weight = volume.second * extruder->filament_density() * 0.001; + total_used_filament += volume.second/s; + total_weight += weight; + total_cost += weight * extruder->filament_cost() * 0.001; + } + + print_statistics.total_extruded_volume = total_extruded_volume; + print_statistics.total_used_filament = total_used_filament; + print_statistics.total_weight = total_weight; + print_statistics.total_cost = total_cost; + + print_statistics.filament_stats = result.print_statistics.volumes_per_extruder; } #if ENABLE_VALIDATE_CUSTOM_GCODE @@ -754,7 +789,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); }); - DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); +// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); + DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics); #if ENABLE_GCODE_WINDOW if (result != nullptr) { *result = std::move(m_processor.extract_result()); @@ -957,7 +993,6 @@ namespace DoExport { dst.first += buf; ++ dst.second; }; - print_statistics.filament_stats.insert(std::pair{extruder.id(), (float)used_filament}); append(out_filament_used_mm, "%.2lf", used_filament); append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); if (filament_weight > 0.) { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 232a51239..917f84f40 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -186,6 +186,72 @@ void GCodeProcessor::TimeMachine::CustomGCodeTime::reset() times = std::vector>(); } +void GCodeProcessor::UsedFilaments::reset() +{ + color_change_cache = 0.0f; + volumes_per_color_change = std::vector(); + + tool_change_cache = 0.0f; + volumes_per_extruder.clear(); + + role_cache = 0.0f; + filaments_per_role.clear(); +} + +void GCodeProcessor::UsedFilaments::increase_caches(double extruded_volume) +{ + color_change_cache += extruded_volume; + tool_change_cache += extruded_volume; + role_cache += extruded_volume; +} + +void GCodeProcessor::UsedFilaments::process_color_change_cache() +{ + if (color_change_cache != 0.0f) { + volumes_per_color_change.push_back(color_change_cache); + color_change_cache = 0.0f; + } +} + +void GCodeProcessor::UsedFilaments::process_extruder_cache(GCodeProcessor* processor) +{ + size_t active_extruder_id = processor->m_extruder_id; + if (tool_change_cache != 0.0f) { + if (volumes_per_extruder.find(active_extruder_id) != volumes_per_extruder.end()) + volumes_per_extruder[active_extruder_id] += tool_change_cache; + else + volumes_per_extruder[active_extruder_id] = tool_change_cache; + tool_change_cache = 0.0f; + } +} + +void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor) +{ + if (role_cache != 0.0f) { + std::pair filament = { 0.0f, 0.0f }; + + double s = PI * sqr(0.5 * processor->m_filament_diameters[processor->m_extruder_id]); + filament.first = role_cache/s * 0.001; + filament.second = role_cache * processor->m_filament_densities[processor->m_extruder_id] * 0.001; + + ExtrusionRole active_role = processor->m_extrusion_role; + if (filaments_per_role.find(active_role) != filaments_per_role.end()) { + filaments_per_role[active_role].first += filament.first; + filaments_per_role[active_role].second += filament.second; + } + else + filaments_per_role[active_role] = filament; + role_cache = 0.0f; + } +} + +void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor) +{ + process_color_change_cache(); + process_extruder_cache(processor); + process_role_cache(processor); +} + void GCodeProcessor::TimeMachine::reset() { enabled = false; @@ -348,10 +414,10 @@ void GCodeProcessor::TimeProcessor::reset() machine_limits = MachineEnvelopeConfig(); filament_load_times = std::vector(); filament_unload_times = std::vector(); - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { machines[i].reset(); } - machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].enabled = true; + machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true; } #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER @@ -416,19 +482,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) size_t g1_lines_counter = 0; // keeps track of last exported pair #if ENABLE_EXTENDED_M73_LINES - std::array, static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_main; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main; + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { last_exported_main[i] = { 0, time_in_minutes(machines[i].time) }; } // keeps track of last exported remaining time to next printer stop - std::array(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_stop; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + std::array(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop; + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { last_exported_stop[i] = time_in_minutes(machines[i].time); } #else - std::array, static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> last_exported; + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { last_exported[i] = { 0, time_in_minutes(machines[i].time) }; } #endif // ENABLE_EXTENDED_M73_LINES @@ -451,7 +517,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) line = line.substr(1); if (export_remaining_time_enabled && (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { #if ENABLE_EXTENDED_M73_LINES @@ -486,7 +552,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) { #else if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { ret += format_line_M73(machine.line_m73_mask.c_str(), @@ -497,13 +563,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) } else if (line == Estimated_Printing_Time_Placeholder_Tag) { #endif // ENABLE_VALIDATE_CUSTOM_GCODE - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; - PrintEstimatedTimeStatistics::ETimeMode mode = static_cast(i); - if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) { + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { char buf[128]; sprintf(buf, "; estimated printing time (%s mode) = %s\n", - (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent", + (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", get_time_dhms(machine.time).c_str()); ret += buf; } @@ -545,7 +611,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) unsigned int exported_lines_count = 0; #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER if (export_remaining_time_enabled) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = machines[i]; if (machine.enabled) { // export pair @@ -789,13 +855,13 @@ GCodeProcessor::GCodeProcessor() { reset(); #if ENABLE_EXTENDED_M73_LINES - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n"; - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n"; - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n"; - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n"; #else - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; #endif // ENABLE_EXTENDED_M73_LINES } @@ -826,6 +892,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_filament_diameters[i] = static_cast(config.filament_diameter.values[i]); } + m_filament_densities.resize(config.filament_density.values.size()); + for (size_t i = 0; i < config.filament_density.values.size(); ++i) { + m_filament_densities[i] = static_cast(config.filament_density.values[i]); + } + if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) { m_time_processor.machine_limits = reinterpret_cast(config); if (m_flavor == gcfMarlinLegacy) { @@ -846,7 +917,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_time_processor.filament_unload_times[i] = static_cast(config.filament_unload_time.values[i]); } - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; @@ -896,6 +967,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } + const ConfigOptionFloats* filament_densities = config.option("filament_density"); + if (filament_densities != nullptr) { + for (double dens : filament_densities->values) { + m_filament_densities.push_back(static_cast(dens)); + } + } + m_result.extruders_count = config.option("nozzle_diameter")->values.size(); const ConfigOptionPoints* extruder_offset = config.option("extruder_offset"); @@ -1026,7 +1104,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values; } - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; @@ -1051,7 +1129,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::enable_stealth_time_estimator(bool enabled) { - m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled = enabled; + m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled = enabled; } void GCodeProcessor::reset() @@ -1096,6 +1174,7 @@ void GCodeProcessor::reset() } m_filament_diameters = std::vector(Min_Extruder_Count, 1.75f); + m_filament_densities = std::vector(Min_Extruder_Count, 1.245f); m_extruded_last_z = 0.0f; #if ENABLE_START_GCODE_VISUALIZATION m_first_layer_height = 0.0f; @@ -1109,6 +1188,7 @@ void GCodeProcessor::reset() m_producers_enabled = false; m_time_processor.reset(); + m_used_filaments.reset(); m_result.reset(); m_result.id = ++s_result_id; @@ -1186,7 +1266,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr } // process the time blocks - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; machine.calculate_time(); @@ -1194,6 +1274,8 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache }); } + m_used_filaments.process_caches(this); + update_estimated_times_stats(); // post-process to add M73 lines into the gcode @@ -1216,20 +1298,20 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr #endif // ENABLE_GCODE_VIEWER_STATISTICS } -float GCodeProcessor::get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const +float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const { - return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; + return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].time : 0.0f; } -std::string GCodeProcessor::get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const +std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const { - return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); + return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); } -std::vector>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const +std::vector>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const { std::vector>> ret; - if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { + if (mode < PrintEstimatedStatistics::ETimeMode::Count) { const TimeMachine& machine = m_time_processor.machines[static_cast(mode)]; float total_time = 0.0f; for (const auto& [type, time] : machine.gcode_time.times) { @@ -1241,10 +1323,10 @@ std::vector>> GCodeProcesso return ret; } -std::vector> GCodeProcessor::get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const +std::vector> GCodeProcessor::get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const { std::vector> ret; - if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { + if (mode < PrintEstimatedStatistics::ETimeMode::Count) { for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].moves_time.size(); ++i) { float time = m_time_processor.machines[static_cast(mode)].moves_time[i]; if (time > 0.0f) @@ -1254,10 +1336,10 @@ std::vector> GCodeProcessor::get_moves_time(PrintEst return ret; } -std::vector> GCodeProcessor::get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const +std::vector> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const { std::vector> ret; - if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { + if (mode < PrintEstimatedStatistics::ETimeMode::Count) { for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].roles_time.size(); ++i) { float time = m_time_processor.machines[static_cast(mode)].roles_time[i]; if (time > 0.0f) @@ -1267,9 +1349,9 @@ std::vector> GCodeProcessor::get_roles_time(Prin return ret; } -std::vector GCodeProcessor::get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const +std::vector GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const { - return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? + return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].layers_time : std::vector(); } @@ -1461,6 +1543,7 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_VALIDATE_CUSTOM_GCODE // extrusion role tag if (boost::starts_with(comment, reserved_tag(ETags::Role))) { + m_used_filaments.process_role_cache(this); m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())); #if ENABLE_SEAMS_VISUALIZATION if (m_extrusion_role == erExternalPerimeter) @@ -1546,7 +1629,8 @@ void GCodeProcessor::process_tags(const std::string_view comment) extruder_id = static_cast(eid); } - m_extruder_colors[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview + if (extruder_id < m_extruder_colors.size()) + m_extruder_colors[extruder_id] = static_cast(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview ++m_cp_color.counter; if (m_cp_color.counter == UCHAR_MAX) m_cp_color.counter = 0; @@ -1557,6 +1641,7 @@ void GCodeProcessor::process_tags(const std::string_view comment) } process_custom_gcode_time(CustomGCode::ColorChange); + process_filaments(CustomGCode::ColorChange); return; } @@ -2194,6 +2279,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; float area_toolpath_cross_section = volume_extruded_filament / delta_xyz; + // save extruded volume to the cache + m_used_filaments.increase_caches(volume_extruded_filament); + // volume extruded filament / tool displacement = area toolpath cross section m_mm3_per_mm = area_toolpath_cross_section; #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -2254,7 +2342,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) assert(distance != 0.0f); float inv_distance = 1.0f / distance; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; if (!machine.enabled) continue; @@ -2264,8 +2352,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) std::vector& blocks = machine.blocks; curr.feedrate = (delta_pos[E] == 0.0f) ? - minimum_travel_feedrate(static_cast(i), m_feedrate) : - minimum_feedrate(static_cast(i), m_feedrate); + minimum_travel_feedrate(static_cast(i), m_feedrate) : + minimum_feedrate(static_cast(i), m_feedrate); TimeBlock block; block.move_type = type; @@ -2283,7 +2371,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); if (curr.abs_axis_feedrate[a] != 0.0f) { - float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); + float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); } @@ -2300,13 +2388,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // calculates block acceleration float acceleration = - (type == EMoveType::Travel) ? get_travel_acceleration(static_cast(i)) : + (type == EMoveType::Travel) ? get_travel_acceleration(static_cast(i)) : (is_extrusion_only_move(delta_pos) ? - get_retract_acceleration(static_cast(i)) : - get_acceleration(static_cast(i))); + get_retract_acceleration(static_cast(i)) : + get_acceleration(static_cast(i))); for (unsigned char a = X; a <= E; ++a) { - float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); + float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) acceleration = axis_max_acceleration; } @@ -2317,7 +2405,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) curr.safe_feedrate = block.feedrate_profile.cruise; for (unsigned char a = X; a <= E; ++a) { - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (curr.abs_axis_feedrate[a] > axis_max_jerk) curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk); } @@ -2365,7 +2453,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // axis reversal std::max(-v_exit, v_entry)); - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); + float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; limited = true; @@ -2650,8 +2738,8 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); @@ -2678,8 +2766,8 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) // http://smoothieware.org/supported-g-codes float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor); @@ -2699,27 +2787,27 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) { float value; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { if (line.has_value('S', value)) { // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware // It is also generated by PrusaSlicer to control acceleration per extrusion type // (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used. - set_acceleration(static_cast(i), value); - set_travel_acceleration(static_cast(i), value); + set_acceleration(static_cast(i), value); + set_travel_acceleration(static_cast(i), value); if (line.has_value('T', value)) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); } else { // New acceleration format, compatible with the upstream Marlin. if (line.has_value('P', value)) - set_acceleration(static_cast(i), value); + set_acceleration(static_cast(i), value); if (line.has_value('R', value)) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); if (line.has_value('T', value)) // Interpret the T value as the travel acceleration in the new Marlin format. - set_travel_acceleration(static_cast(i), value); + set_travel_acceleration(static_cast(i), value); } } } @@ -2727,8 +2815,8 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { if (line.has_x()) { float max_jerk = line.x(); @@ -2761,7 +2849,7 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line) float value_t; if (line.has_value('S', value_s) && !line.has_value('T', value_t)) { value_s *= 0.01f; - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { m_time_processor.machines[i].extrude_factor_override_percentage = value_s; } } @@ -2812,7 +2900,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC); @@ -2863,6 +2951,7 @@ void GCodeProcessor::process_T(const std::string_view command) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; else { unsigned char old_extruder_id = m_extruder_id; + process_filaments(CustomGCode::ToolChange); m_extruder_id = id; m_cp_color.current = m_extruder_colors[id]; // Specific to the MK3 MMU2: @@ -2920,7 +3009,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) #if ENABLE_EXTENDED_M73_LINES // stores stop time placeholders for later use if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; if (!machine.enabled) continue; @@ -2931,7 +3020,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) #endif // ENABLE_EXTENDED_M73_LINES } -float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const +float GCodeProcessor::minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const { if (m_time_processor.machine_limits.machine_min_extruding_rate.empty()) return feedrate; @@ -2939,7 +3028,7 @@ float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode m return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast(mode))); } -float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const +float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const { if (m_time_processor.machine_limits.machine_min_travel_rate.empty()) return feedrate; @@ -2947,7 +3036,7 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETim return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast(mode))); } -float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -2959,7 +3048,7 @@ float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeM } } -float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -2971,7 +3060,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ET } } -float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const +float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { switch (axis) { @@ -2983,18 +3072,18 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode } } -float GCodeProcessor::get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const +float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast(mode)); } -float GCodeProcessor::get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const +float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const { size_t id = static_cast(mode); return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION; } -void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value) +void GCodeProcessor::set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value) { size_t id = static_cast(mode); if (id < m_time_processor.machines.size()) { @@ -3004,13 +3093,13 @@ void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mo } } -float GCodeProcessor::get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const +float GCodeProcessor::get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const { size_t id = static_cast(mode); return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; } -void GCodeProcessor::set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value) +void GCodeProcessor::set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value) { size_t id = static_cast(mode); if (id < m_time_processor.machines.size()) { @@ -3038,7 +3127,7 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id) void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { TimeMachine& machine = m_time_processor.machines[i]; if (!machine.enabled) continue; @@ -3055,17 +3144,26 @@ void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) } } +void GCodeProcessor::process_filaments(CustomGCode::Type code) +{ + if (code == CustomGCode::ColorChange) + m_used_filaments.process_color_change_cache(); + + if (code == CustomGCode::ToolChange) + m_used_filaments.process_extruder_cache(this); +} + void GCodeProcessor::simulate_st_synchronize(float additional_time) { - for (size_t i = 0; i < static_cast(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { m_time_processor.machines[i].simulate_st_synchronize(additional_time); } } void GCodeProcessor::update_estimated_times_stats() { - auto update_mode = [this](PrintEstimatedTimeStatistics::ETimeMode mode) { - PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast(mode)]; + auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { + PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast(mode)]; data.time = get_time(mode); data.custom_gcode_times = get_custom_gcode_times(mode, true); data.moves_times = get_moves_time(mode); @@ -3073,11 +3171,15 @@ void GCodeProcessor::update_estimated_times_stats() data.layers_times = get_layers_time(mode); }; - update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal); - if (m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled) - update_mode(PrintEstimatedTimeStatistics::ETimeMode::Stealth); + update_mode(PrintEstimatedStatistics::ETimeMode::Normal); + if (m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled) + update_mode(PrintEstimatedStatistics::ETimeMode::Stealth); else - m_result.time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].reset(); + m_result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].reset(); + + m_result.print_statistics.volumes_per_color_change = m_used_filaments.volumes_per_color_change; + m_result.print_statistics.volumes_per_extruder = m_used_filaments.volumes_per_extruder; + m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role; } } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 75ec1546b..8975255ec 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -36,7 +36,7 @@ namespace Slic3r { Count }; - struct PrintEstimatedTimeStatistics + struct PrintEstimatedStatistics { enum class ETimeMode : unsigned char { @@ -62,14 +62,21 @@ namespace Slic3r { } }; + std::vector volumes_per_color_change; + std::map volumes_per_extruder; + std::map> used_filaments_per_role; + std::array(ETimeMode::Count)> modes; - PrintEstimatedTimeStatistics() { reset(); } + PrintEstimatedStatistics() { reset(); } void reset() { for (auto m : modes) { m.reset(); } + volumes_per_color_change.clear(); + volumes_per_extruder.clear(); + used_filaments_per_role.clear(); } }; @@ -314,7 +321,7 @@ namespace Slic3r { // Additional load / unload times for a filament exchange sequence. std::vector filament_load_times; std::vector filament_unload_times; - std::array(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines; + std::array(PrintEstimatedStatistics::ETimeMode::Count)> machines; void reset(); @@ -327,6 +334,30 @@ namespace Slic3r { #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER }; + struct UsedFilaments // filaments per ColorChange + { + double color_change_cache; + std::vector volumes_per_color_change; + + double tool_change_cache; + std::map volumes_per_extruder; + + double role_cache; + // ExtrusionRole : + std::map> filaments_per_role; + + void reset(); + + void increase_caches(double extruded_volume); + + void process_color_change_cache(); + void process_extruder_cache(GCodeProcessor* processor); + void process_role_cache(GCodeProcessor* processor); + void process_caches(GCodeProcessor* processor); + + friend class GCodeProcessor; + }; + public: #if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER struct MoveVertex @@ -372,7 +403,7 @@ namespace Slic3r { SettingsIds settings_ids; size_t extruders_count; std::vector extruder_colors; - PrintEstimatedTimeStatistics time_statistics; + PrintEstimatedStatistics print_statistics; #if ENABLE_GCODE_VIEWER_STATISTICS int64_t time{ 0 }; @@ -519,6 +550,7 @@ namespace Slic3r { ExtruderColors m_extruder_colors; ExtruderTemps m_extruder_temps; std::vector m_filament_diameters; + std::vector m_filament_densities; float m_extruded_last_z; #if ENABLE_START_GCODE_VISUALIZATION float m_first_layer_height; // mm @@ -550,6 +582,7 @@ namespace Slic3r { bool m_producers_enabled; TimeProcessor m_time_processor; + UsedFilaments m_used_filaments; Result m_result; static unsigned int s_result_id; @@ -566,7 +599,7 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { - return m_time_processor.machines[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled; + return m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled; } void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; } void enable_producers(bool enabled) { m_producers_enabled = enabled; } @@ -579,13 +612,13 @@ namespace Slic3r { // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). void process_file(const std::string& filename, bool apply_postprocess, std::function cancel_callback = nullptr); - float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; - std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const; - std::vector>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const; + float get_time(PrintEstimatedStatistics::ETimeMode mode) const; + std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; + std::vector>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const; - std::vector> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; - std::vector> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; - std::vector get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; + std::vector> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const; + std::vector> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const; + std::vector get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const; private: void apply_config(const DynamicPrintConfig& config); @@ -701,20 +734,21 @@ namespace Slic3r { void store_move_vertex(EMoveType type); - float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; - float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; - float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; - float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; - float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; - float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; - float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; - void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value); - float get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; - void set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value); + float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; + float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; + float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; + float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; + float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; + float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; + float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; + void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); + float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; + void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); float get_filament_load_time(size_t extruder_id); float get_filament_unload_time(size_t extruder_id); void process_custom_gcode_time(CustomGCode::Type code); + void process_filaments(CustomGCode::Type code); // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b4241c91e..3fdc49db8 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -407,7 +407,7 @@ struct PrintStatistics double total_weight; double total_wipe_tower_cost; double total_wipe_tower_filament; - std::map filament_stats; + std::map filament_stats; // Config with the filled in print statistics. DynamicConfig config() const; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c7cb3a5d9..233bdf1cd 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -17,6 +17,7 @@ #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" +#include "GUI_ObjectManipulation.hpp" #include #include @@ -687,13 +688,13 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty()); } - m_time_statistics = gcode_result.time_statistics; + m_print_statistics = gcode_result.print_statistics; - if (m_time_estimate_mode != PrintEstimatedTimeStatistics::ETimeMode::Normal) { - float time = m_time_statistics.modes[static_cast(m_time_estimate_mode)].time; + if (m_time_estimate_mode != PrintEstimatedStatistics::ETimeMode::Normal) { + float time = m_print_statistics.modes[static_cast(m_time_estimate_mode)].time; if (time == 0.0f || - short_time(get_time_dhms(time)) == short_time(get_time_dhms(m_time_statistics.modes[static_cast(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time))) - m_time_estimate_mode = PrintEstimatedTimeStatistics::ETimeMode::Normal; + short_time(get_time_dhms(time)) == short_time(get_time_dhms(m_print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time))) + m_time_estimate_mode = PrintEstimatedStatistics::ETimeMode::Normal; } } @@ -788,7 +789,7 @@ void GCodeViewer::reset() m_layers.reset(); m_layers_z_range = { 0, 0 }; m_roles = std::vector(); - m_time_statistics.reset(); + m_print_statistics.reset(); #if ENABLE_GCODE_WINDOW m_sequential_view.gcode_window.reset(); #endif // ENABLE_GCODE_WINDOW @@ -4079,7 +4080,7 @@ void GCodeViewer::render_legend() const Line }; - const PrintEstimatedTimeStatistics::Mode& time_mode = m_time_statistics.modes[static_cast(m_time_estimate_mode)]; + const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast(m_time_estimate_mode)]; #if ENABLE_SCROLLABLE_LEGEND bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); @@ -4088,12 +4089,15 @@ void GCodeViewer::render_legend() const const float icon_size = ImGui::GetTextLineHeight(); const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); + bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + #if ENABLE_SCROLLABLE_LEGEND - auto append_item = [this, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, + auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label, #else - auto append_item = [this, draw_list, icon_size, percent_bar_size, &imgui](EItemType type, const Color& color, const std::string& label, + auto append_item = [this, draw_list, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label, #endif // ENABLE_SCROLLABLE_LEGEND - bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array& offsets = { 0.0f, 0.0f }, + bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array& offsets = { 0.0f, 0.0f, 0.0f, 0.0f }, + double used_filament_m = 0.0, double used_filament_g = 0.0, std::function callback = nullptr) { if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); @@ -4173,10 +4177,26 @@ void GCodeViewer::render_legend() const char buf[64]; ::sprintf(buf, "%.1f%%", 100.0f * percent); ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); + ImGui::SameLine(offsets[2]); + ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m); + imgui.text(buf); + ImGui::SameLine(offsets[3]); + ::sprintf(buf, "%.2f g", used_filament_g); + imgui.text(buf); } } - else + else { imgui.text(label); + if (used_filament_m > 0.0) { + char buf[64]; + ImGui::SameLine(offsets[0]); + ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m); + imgui.text(buf); + ImGui::SameLine(offsets[1]); + ::sprintf(buf, "%.2f g", used_filament_g); + imgui.text(buf); + } + } if (!visible) ImGui::PopStyleVar(); @@ -4204,12 +4224,13 @@ void GCodeViewer::render_legend() const } }; - auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { - imgui.text(texts[0]); - ImGui::SameLine(offsets[0]); - imgui.text(texts[1]); - ImGui::SameLine(offsets[1]); - imgui.text(texts[2]); + auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { + size_t i = 0; + for (; i < offsets.size(); i++) { + imgui.text(texts[i]); + ImGui::SameLine(offsets[i]); + } + imgui.text(texts[i]); ImGui::Separator(); }; @@ -4222,11 +4243,12 @@ void GCodeViewer::render_legend() const }; auto calculate_offsets = [max_width](const std::vector& labels, const std::vector& times, - const std::array& titles, float extra_size = 0.0f) { + const std::array& titles, float extra_size = 0.0f) { const ImGuiStyle& style = ImGui::GetStyle(); - std::array ret = { 0.0f, 0.0f }; + std::array ret = { 0.0f, 0.0f, 0.0f, 0.0f }; ret[0] = max_width(labels, titles[0], extra_size) + 3.0f * style.ItemSpacing.x; - ret[1] = ret[0] + max_width(times, titles[1]) + style.ItemSpacing.x; + for (size_t i = 1; i < titles.size(); i++) + ret[i] = ret[i-1] + max_width(times, titles[i]) + style.ItemSpacing.x; return ret; }; @@ -4282,11 +4304,22 @@ void GCodeViewer::render_legend() const return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f); }; + auto used_filament_per_role = [this, imperial_units](ExtrusionRole role) { + auto it = m_print_statistics.used_filaments_per_role.find(role); + if (it == m_print_statistics.used_filaments_per_role.end()) + return std::make_pair(0.0, 0.0); + + double koef = imperial_units ? 1000.0 / ObjectManipulation::in_to_mm : 1.0; + return std::make_pair(it->second.first * koef, it->second.second); + }; + // data used to properly align items in columns when showing time - std::array offsets = { 0.0f, 0.0f }; + std::array offsets = { 0.0f, 0.0f, 0.0f, 0.0f }; std::vector labels; std::vector times; std::vector percents; + std::vector used_filaments_m; + std::vector used_filaments_g; float max_percent = 0.0f; if (m_view_type == EViewType::FeatureType) { @@ -4299,10 +4332,73 @@ void GCodeViewer::render_legend() const times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); percents.push_back(percent); max_percent = std::max(max_percent, percent); + auto [used_filament_m, used_filament_g] = used_filament_per_role(role); + used_filaments_m.push_back(used_filament_m); + used_filaments_g.push_back(used_filament_g); } } - offsets = calculate_offsets(labels, times, { _u8L("Feature type"), _u8L("Time") }, icon_size); + std::string longest_percentage_string; + for (double item : percents) { + char buffer[64]; + ::sprintf(buffer, "%.2f %%", item); + if (::strlen(buffer) > longest_percentage_string.length()) + longest_percentage_string = buffer; + } + longest_percentage_string += " "; + if (_u8L("Percentage").length() > longest_percentage_string.length()) + longest_percentage_string = _u8L("Percentage"); + + std::string longest_used_filament_string; + for (double item : used_filaments_m) { + char buffer[64]; + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item); + if (::strlen(buffer) > longest_used_filament_string.length()) + longest_used_filament_string = buffer; + } + + offsets = calculate_offsets(labels, times, { _u8L("Feature type"), _u8L("Time"), longest_percentage_string, longest_used_filament_string }, icon_size); + } + + // get used filament (meters and grams) from used volume in respect to the active extruder + auto get_used_filament_from_volume = [imperial_units](double volume, int extruder_id) { + const std::vector& filament_presets = wxGetApp().preset_bundle->filament_presets; + const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; + + double koef = imperial_units ? 1.0/ObjectManipulation::in_to_mm : 0.001; + + std::pair ret = { 0.0, 0.0 }; + if (const Preset* filament_preset = filaments.find_preset(filament_presets[extruder_id], false)) { + double filament_radius = 0.5 * filament_preset->config.opt_float("filament_diameter", 0); + double s = PI * sqr(filament_radius); + ret.first = volume / s * koef; + double filament_density = filament_preset->config.opt_float("filament_density", 0); + ret.second = volume * filament_density * 0.001; + } + return ret; + }; + + if (m_view_type == EViewType::Tool) { + // calculate used filaments data + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.volumes_per_extruder.find(extruder_id) == m_print_statistics.volumes_per_extruder.end()) + continue; + double volume = m_print_statistics.volumes_per_extruder.at(extruder_id); + + auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, extruder_id); + used_filaments_m.push_back(used_filament_m); + used_filaments_g.push_back(used_filament_g); + } + + std::string longest_used_filament_string; + for (double item : used_filaments_m) { + char buffer[64]; + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item); + if (::strlen(buffer) > longest_used_filament_string.length()) + longest_used_filament_string = buffer; + } + + offsets = calculate_offsets(labels, times, { "Extruder NNN", longest_used_filament_string }, icon_size); } // extrusion paths section -> title @@ -4310,7 +4406,7 @@ void GCodeViewer::render_legend() const { case EViewType::FeatureType: { - append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage") }, offsets); + append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage"), _u8L("Used filament") }, offsets); break; } case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } @@ -4319,7 +4415,11 @@ void GCodeViewer::render_legend() const case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } - case EViewType::Tool: { imgui.title(_u8L("Tool")); break; } + case EViewType::Tool: + { + append_headers({ _u8L("Tool"), _u8L("Used filament") }, offsets); + break; + } case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } default: { break; } } @@ -4335,7 +4435,7 @@ void GCodeViewer::render_legend() const continue; const bool visible = is_visible(role); append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], - visible, times[i], percents[i], max_percent, offsets, [this, role, visible]() { + visible, times[i], percents[i], max_percent, offsets, used_filaments_m[i], used_filaments_g[i], [this, role, visible]() { Extrusions* extrusions = const_cast(&m_extrusions); extrusions->role_visibility_flags = visible ? extrusions->role_visibility_flags & ~(1 << role) : extrusions->role_visibility_flags | (1 << role); // update buffers' render paths @@ -4357,8 +4457,11 @@ void GCodeViewer::render_legend() const case EViewType::Tool: { // shows only extruders actually used - for (unsigned char i : m_extruder_ids) { - append_item(EItemType::Rect, m_tool_colors[i], _u8L("Extruder") + " " + std::to_string(i + 1)); + size_t i = 0; + for (unsigned char extruder_id : m_extruder_ids) { + append_item(EItemType::Rect, m_tool_colors[extruder_id], _u8L("Extruder") + " " + std::to_string(extruder_id + 1), + true, "", 0.0f, 0.0f, offsets, used_filaments_m[i], used_filaments_g[i]); + i++; } break; } @@ -4458,10 +4561,11 @@ void GCodeViewer::render_legend() const Color color1; Color color2; Times times; + std::pair used_filament {0.0f, 0.0f}; }; using PartialTimes = std::vector; - auto generate_partial_times = [this](const TimesList& times) { + auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector& used_filaments) { PartialTimes items; std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; @@ -4471,6 +4575,7 @@ void GCodeViewer::render_legend() const last_color[i] = m_tool_colors[i]; } int last_extruder_id = 1; + int color_change_idx = 0; for (const auto& time_rec : times) { switch (time_rec.first) { @@ -4486,14 +4591,14 @@ void GCodeViewer::render_legend() const case CustomGCode::ColorChange: { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder-1) }); items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); last_color[it->extruder - 1] = decode_color(it->color); last_extruder_id = it->extruder; custom_gcode_per_print_z.erase(it); } else - items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id -1) }); break; } @@ -4504,7 +4609,7 @@ void GCodeViewer::render_legend() const return items; }; - auto append_color_change = [&imgui](const Color& color1, const Color& color2, const std::array& offsets, const Times& times) { + auto append_color_change = [&imgui](const Color& color1, const Color& color2, const std::array& offsets, const Times& times) { imgui.text(_u8L("Color change")); ImGui::SameLine(); @@ -4523,7 +4628,7 @@ void GCodeViewer::render_legend() const imgui.text(short_time(get_time_dhms(times.second - times.first))); }; - auto append_print = [&imgui](const Color& color, const std::array& offsets, const Times& times) { + auto append_print = [&imgui, imperial_units](const Color& color, const std::array& offsets, const Times& times, std::pair used_filament) { imgui.text(_u8L("Print")); ImGui::SameLine(); @@ -4539,9 +4644,19 @@ void GCodeViewer::render_legend() const imgui.text(short_time(get_time_dhms(times.second))); ImGui::SameLine(offsets[1]); imgui.text(short_time(get_time_dhms(times.first))); + if (used_filament.first > 0.0f) { + char buffer[64]; + ImGui::SameLine(offsets[2]); + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", used_filament.first); + imgui.text(buffer); + + ImGui::SameLine(offsets[3]); + ::sprintf(buffer, "%.2f g", used_filament.second); + imgui.text(buffer); + } }; - PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times); + PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times, m_print_statistics.volumes_per_color_change); if (!partial_times.empty()) { labels.clear(); times.clear(); @@ -4555,10 +4670,22 @@ void GCodeViewer::render_legend() const } times.push_back(short_time(get_time_dhms(item.times.second))); } - offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time") }, 2.0f * icon_size); + + + std::string longest_used_filament_string; + for (const PartialTime& item : partial_times) { + if (item.used_filament.first > 0.0f) { + char buffer[64]; + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item.used_filament.first); + if (::strlen(buffer) > longest_used_filament_string.length()) + longest_used_filament_string = buffer; + } + } + + offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), longest_used_filament_string }, 2.0f * icon_size); ImGui::Spacing(); - append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration") }, offsets); + append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets); #if ENABLE_SCROLLABLE_LEGEND const bool need_scrollable = static_cast(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; if (need_scrollable) @@ -4570,7 +4697,7 @@ void GCodeViewer::render_legend() const switch (item.type) { case PartialTime::EType::Print: { - append_print(item.color1, offsets, item.times); + append_print(item.color1, offsets, item.times, item.used_filament); break; } case PartialTime::EType::Pause: { @@ -4750,12 +4877,12 @@ void GCodeViewer::render_legend() const ImGui::AlignTextToFramePadding(); switch (m_time_estimate_mode) { - case PrintEstimatedTimeStatistics::ETimeMode::Normal: + case PrintEstimatedStatistics::ETimeMode::Normal: { imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Normal mode") + "]:"); break; } - case PrintEstimatedTimeStatistics::ETimeMode::Stealth: + case PrintEstimatedStatistics::ETimeMode::Stealth: { imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Stealth mode") + "]:"); break; @@ -4765,18 +4892,18 @@ void GCodeViewer::render_legend() const ImGui::SameLine(); imgui.text(short_time(get_time_dhms(time_mode.time))); - auto show_mode_button = [this, &imgui](const wxString& label, PrintEstimatedTimeStatistics::ETimeMode mode) { + auto show_mode_button = [this, &imgui](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { bool show = false; - for (size_t i = 0; i < m_time_statistics.modes.size(); ++i) { + for (size_t i = 0; i < m_print_statistics.modes.size(); ++i) { if (i != static_cast(mode) && - short_time(get_time_dhms(m_time_statistics.modes[static_cast(mode)].time)) != short_time(get_time_dhms(m_time_statistics.modes[i].time))) { + short_time(get_time_dhms(m_print_statistics.modes[static_cast(mode)].time)) != short_time(get_time_dhms(m_print_statistics.modes[i].time))) { show = true; break; } } - if (show && m_time_statistics.modes[static_cast(mode)].roles_times.size() > 0) { + if (show && m_print_statistics.modes[static_cast(mode)].roles_times.size() > 0) { if (imgui.button(label)) { - *const_cast(&m_time_estimate_mode) = mode; + *const_cast(&m_time_estimate_mode) = mode; wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); } @@ -4784,12 +4911,12 @@ void GCodeViewer::render_legend() const }; switch (m_time_estimate_mode) { - case PrintEstimatedTimeStatistics::ETimeMode::Normal: { - show_mode_button(_L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth); + case PrintEstimatedStatistics::ETimeMode::Normal: { + show_mode_button(_L("Show stealth mode"), PrintEstimatedStatistics::ETimeMode::Stealth); break; } - case PrintEstimatedTimeStatistics::ETimeMode::Stealth: { - show_mode_button(_L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal); + case PrintEstimatedStatistics::ETimeMode::Stealth: { + show_mode_button(_L("Show normal mode"), PrintEstimatedStatistics::ETimeMode::Normal); break; } default : { assert(false); break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 2ccda6f5d..112c681d4 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -696,8 +696,8 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; - PrintEstimatedTimeStatistics m_time_statistics; - PrintEstimatedTimeStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedTimeStatistics::ETimeMode::Normal }; + PrintEstimatedStatistics m_print_statistics; + PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; #if ENABLE_GCODE_VIEWER_STATISTICS Statistics m_statistics; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index ea289bb14..52223b3d4 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -643,7 +643,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee if (sla_print_technology) m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times); else { - auto print_mode_stat = m_gcode_result->time_statistics.modes.front(); + auto print_mode_stat = m_gcode_result->print_statistics.modes.front(); m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aebec14ee..13601396f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1172,10 +1172,10 @@ void Sidebar::update_sliced_info_sizer() new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower")); wxString info_text = is_wipe_tower ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / /*1000*/koef, - (ps.total_used_filament - ps.total_wipe_tower_filament) / /*1000*/koef, - ps.total_wipe_tower_filament / /*1000*/koef) : - wxString::Format("%.2f", ps.total_used_filament / /*1000*/koef); + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / koef, + (ps.total_used_filament - ps.total_wipe_tower_filament) / koef, + ps.total_wipe_tower_filament / koef) : + wxString::Format("%.2f", ps.total_used_filament / koef); p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f; @@ -1203,7 +1203,7 @@ void Sidebar::update_sliced_info_sizer() filament_weight = ps.total_weight; else { double filament_density = filament_preset->config.opt_float("filament_density", 0); - filament_weight = filament.second * filament_density * 2.4052f * 0.001; // assumes 1.75mm filament diameter; + filament_weight = filament.second * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter; new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1); info_text += wxString::Format("\n%.2f", filament_weight); @@ -1357,7 +1357,8 @@ void Sidebar::update_ui_from_settings() update_sliced_info_sizer(); // update Cut gizmo, if it's open p->plater->canvas3D()->update_gizmos_on_off_state(); - p->plater->canvas3D()->request_extra_frame(); + p->plater->set_current_canvas_as_dirty(); + p->plater->get_current_canvas3D()->request_extra_frame(); } std::vector& Sidebar::combos_filament() diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 44d58266b..8aef9e7a3 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -105,7 +105,7 @@ _constant() SV* filament_stats() %code%{ HV* hv = newHV(); - for (std::map::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) { + for (std::map::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) { // stringify extruder_id std::ostringstream ss; ss << it->first; From 4d2c2070f8c6f321094498f4540e2aef5ef3ad9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 7 May 2021 12:51:10 +0200 Subject: [PATCH 55/79] Added missing includes (GCC 9.3) --- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index 8ac134f5f..a2f7c8933 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -8,6 +8,12 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Platform.hpp" +#include +#include + +#include +#include + namespace Slic3r { namespace GUI { From 389955966cce168b46dc985f32121658bb8cf853 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 May 2021 13:42:53 +0200 Subject: [PATCH 56/79] Disabled tech ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW --- src/libslic3r/Technologies.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4cc5fbfec..99684e93e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,7 +69,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_ From f1cb529a7b190cc7a9ca9d84b721fa5e91612308 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 May 2021 14:17:17 +0200 Subject: [PATCH 57/79] Fixed warnings into ProjectDirtyStateManager --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 30d41f808..d538b0cf7 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -133,13 +133,13 @@ void ProjectDirtyStateManager::DirtyState::Gizmos::add_used(const UndoRedo::Snap void ProjectDirtyStateManager::DirtyState::Gizmos::remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack) { const std::vector& snapshots = main_stack.snapshots(); - for (auto& [name, gizmo] : used) { - auto it = gizmo.modified_timestamps.begin(); - while (it != gizmo.modified_timestamps.end()) { + for (auto& item : used) { + auto it = item.second.modified_timestamps.begin(); + while (it != item.second.modified_timestamps.end()) { size_t timestamp = *it; auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [timestamp](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == timestamp; }); if (snapshot_it == snapshots.end()) - it = gizmo.modified_timestamps.erase(it); + it = item.second.modified_timestamps.erase(it); else ++it; } @@ -160,8 +160,8 @@ bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const // returns true if the given snapshot is contained in any of the gizmos caches bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const { - for (auto& [name, gizmo] : used) { - for (size_t i : gizmo.modified_timestamps) { + for (const auto& item : used) { + for (size_t i : item.second.modified_timestamps) { if (i == snapshot.timestamp) return true; } @@ -366,7 +366,6 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; - size_t search_timestamp = 0; if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { if (type == UpdateType::UndoRedoTo) { std::string topmost_redo; @@ -382,14 +381,12 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, } m_state.gizmos.current = false; m_last_save.gizmo = 0; - search_timestamp = m_last_save.main; } else if (boost::starts_with(active_snapshot->name, _utf8("Leaving"))) { if (m_state.gizmos.current) m_state.gizmos.add_used(*active_snapshot); m_state.gizmos.current = false; m_last_save.gizmo = 0; - search_timestamp = m_last_save.main; } const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos, m_last_save.main); From 62ad1904e2534a61a55bb69afb3f26a33470899f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 May 2021 14:46:10 +0200 Subject: [PATCH 58/79] Fixed warnings into DoExport --- src/libslic3r/GCode.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4368783a6..d47d185a0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -611,13 +611,13 @@ std::vector>> GCode::collec // free functions called by GCode::do_export() namespace DoExport { - static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) - { - const GCodeProcessor::Result& result = processor.get_result(); - print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); - print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? - get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A"; - } +// static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) +// { +// const GCodeProcessor::Result& result = processor.get_result(); +// print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time); +// print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? +// get_time_dhms(result.print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A"; +// } static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector& extruders, PrintStatistics& print_statistics) { From 89da02734e981b174dd76c07c8c79d925ce91a2d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 May 2021 15:08:07 +0200 Subject: [PATCH 59/79] ENABLE_ALLOW_NEGATIVE_Z -> Ensure objects on bed when switching to SLA printer --- src/slic3r/GUI/GLCanvas3D.cpp | 12 ++++++------ src/slic3r/GUI/Plater.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c8e402a5d..f99947293 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1945,7 +1945,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_reload_delayed = !m_canvas->IsShown() && !refresh_immediately && !force_full_scene_refresh; - PrinterTechnology printer_technology = m_process->current_printer_technology(); + PrinterTechnology printer_technology = current_printer_technology(); int volume_idx_wipe_tower_old = -1; // Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed). @@ -3572,7 +3572,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) #if ENABLE_ALLOW_NEGATIVE_Z double shift_z = m->get_instance_min_z(i.second); #if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA - if (m_process->current_printer_technology() == ptSLA || shift_z > 0.0) { + if (current_printer_technology() == ptSLA || shift_z > 0.0) { #else if (shift_z > 0.0) { #endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA @@ -3919,7 +3919,7 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() { std::string new_tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip); @@ -4073,7 +4073,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) ArrangeSettings &settings_out = get_arrange_settings(); auto &appcfg = wxGetApp().app_config; - PrinterTechnology ptech = m_process->current_printer_technology(); + PrinterTechnology ptech = current_printer_technology(); bool settings_changed = false; float dist_min = 0.f; @@ -4640,7 +4640,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "settings"; item.icon_filename = "settings.svg"; item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + - "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; item.sprite_id = 10; item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; @@ -4682,7 +4682,7 @@ bool GLCanvas3D::_init_main_toolbar() item.sprite_id = 12; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { - bool res = m_process->current_printer_technology() == ptFFF; + bool res = current_printer_technology() == ptFFF; // turns off if changing printer technology if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ab1d657e4..9f23de421 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5796,6 +5796,14 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology) //FIXME for SLA synchronize //p->background_process.apply(Model)! +#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + if (printer_technology == ptSLA) { + for (ModelObject* model_object : p->model.objects) { + model_object->ensure_on_bed(); + } + } +#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA + p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export"); p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer"); From f11b9a5b6a9758309e2c656b9475c475873ae108 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 May 2021 16:45:37 +0200 Subject: [PATCH 60/79] DiffPresetDialog: Fixed update of the related presets after changing selection of the Printer preset --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 0a0a3dc60..dc2c56246 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1694,6 +1694,9 @@ void DiffPresetDialog::update_compatibility(const std::string& preset_name, Pres technology_changed = old_printer_technology != new_printer_technology; } + // select preset + presets->select_preset_by_name(preset_name, false); + // Mark the print & filament enabled if they are compatible with the currently selected preset. // The following method should not discard changes of current print or filament presets on change of a printer profile, // if they are compatible with the current printer. From 5828decfc7f3776b000fc16e6401dc649884707c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 10 May 2021 09:32:24 +0200 Subject: [PATCH 61/79] Fixing multi-material printing after recent refactoring (d21b9aa to 1c6333e) --- src/libslic3r/PrintObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8fefd4beb..2987b7a23 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1745,7 +1745,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } // Make sure all layers contain layer region objects for all regions. for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) - layer->add_region(&this->print()->get_print_region(region_id)); + layer->add_region(&this->printing_region(region_id)); prev = layer; } } @@ -1793,7 +1793,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) if (spiral_vase) { // Slice the bottom layers with SlicingMode::Regular. // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! - const PrintRegionConfig &config = this->print()->get_print_region(region_id).config(); + const PrintRegionConfig &config = this->printing_region(region_id).config(); slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; ++ slicing_mode_normal_below_layer); From a49d34c6f30df5d0d21a1ec6b3e932028c777334 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 May 2021 12:09:44 +0200 Subject: [PATCH 62/79] Fix of #5437 - Make it clear on the G-code slider that color change is not supported for sequential print --- src/slic3r/GUI/DoubleSlider.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 84a499d6f..655ef496b 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -575,7 +575,10 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ else is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - dc.DrawBitmap(*icon, x_draw, y_draw); + if (m_draw_mode == dmSequentialFffPrint) + dc.DrawBitmap(create_scaled_bitmap("colorchange_add", nullptr, 16, true), x_draw, y_draw); + else + dc.DrawBitmap(*icon, x_draw, y_draw); //update rect of the tick action icon m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); @@ -591,7 +594,7 @@ void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const Selec dc.DrawLine(pt_beg, pt_end); //draw action icon - if (m_draw_mode == dmRegular) + if (m_draw_mode == dmRegular || m_draw_mode == dmSequentialFffPrint) draw_action_icon(dc, pt_beg, pt_end); } } @@ -1377,6 +1380,10 @@ wxString Control::get_tooltip(int tick/*=-1*/) if (tick_code_it == m_ticks.ticks.end() && m_focus == fiActionIcon) // tick doesn't exist { + if (m_draw_mode == dmSequentialFffPrint) + return _L("The sequential print is on.\n" + "It's impossible to apply any custom G-code for objects printing sequentually.\n"); + // Show mode as a first string of tooltop tooltip = " " + _L("Print mode") + ": "; tooltip += (m_mode == SingleExtruder ? SingleExtruderMode : From 137dbbd19f387f08b22a94672957015b27359062 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 May 2021 13:06:13 +0200 Subject: [PATCH 63/79] Fixed crash into ProjectDirtyStateManager::update_from_undo_redo_stack() when switching language --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 480ca0ee5..386728efb 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -49,7 +49,7 @@ #define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0) // Removes obsolete warning texture code #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_4_0_ALPHA0) -// Enable showing gcode line numbers in previeww horizontal slider +// Enable showing gcode line numbers in preview horizontal slider #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0) // Enable validation of custom gcode against gcode processor reserved keywords #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_4_0_ALPHA0) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index d538b0cf7..9ff4821c2 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -180,6 +180,9 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type) return; const Plater* plater = wxGetApp().plater(); + if (plater == nullptr) + return; + const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); From ca8a42c8b12ab4d212497223778ea79228ff91da Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 May 2021 14:45:17 +0200 Subject: [PATCH 64/79] Tech ENABLE_SPLITTED_VERTEX_BUFFER set as default --- src/libslic3r/Technologies.hpp | 4 +- src/slic3r/GUI/GCodeViewer.cpp | 1364 +------------------------------- src/slic3r/GUI/GCodeViewer.hpp | 56 -- src/slic3r/GUI/GLCanvas3D.cpp | 4 - 4 files changed, 2 insertions(+), 1426 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 386728efb..f51241acc 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -41,10 +41,8 @@ //==================== #define ENABLE_2_4_0_ALPHA0 1 -// Enable splitting of vertex buffers used to render toolpaths -#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_4_0_ALPHA0) // Enable rendering only starting and final caps for toolpaths -#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER) +#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_2_4_0_ALPHA0) // Enable reload from disk command for 3mf files #define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0) // Removes obsolete warning texture code diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 233bdf1cd..7ccf10795 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -84,7 +84,6 @@ static float round_to_nearest(float value, unsigned int decimals) return res; } -#if ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::VBuffer::reset() { // release gpu memory @@ -95,38 +94,16 @@ void GCodeViewer::VBuffer::reset() sizes.clear(); count = 0; } -#else -void GCodeViewer::VBuffer::reset() -{ - // release gpu memory - if (id > 0) { - glsafe(::glDeleteBuffers(1, &id)); - id = 0; - } - - count = 0; -} -#endif // ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::IBuffer::reset() { -#if ENABLE_SPLITTED_VERTEX_BUFFER // release gpu memory if (ibo > 0) { glsafe(::glDeleteBuffers(1, &ibo)); ibo = 0; } -#else - // release gpu memory - if (id > 0) { - glsafe(::glDeleteBuffers(1, &id)); - id = 0; - } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER -#if ENABLE_SPLITTED_VERTEX_BUFFER vbo = 0; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER count = 0; } @@ -149,17 +126,10 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const #endif // ENABLE_SEAMS_VISUALIZATION case EMoveType::Extrude: { // use rounding to reduce the number of generated paths -#if ENABLE_SPLITTED_VERTEX_BUFFER return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && move.position[2] <= sub_paths.front().first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed && height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) && matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f); -#else - return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && - move.position[2] <= first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed && - height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) && - matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f); -#endif // ENABLE_SPLITTED_VERTEX_BUFFER } case EMoveType::Travel: { return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; @@ -186,17 +156,10 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi { Path::Endpoint endpoint = { b_id, i_id, s_id, move.position }; // use rounding to reduce the number of generated paths -#if ENABLE_SPLITTED_VERTEX_BUFFER paths.push_back({ move.type, move.extrusion_role, move.delta_extruder, round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed, move.temperature, move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } }); -#else - paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, - round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), - move.feedrate, move.fan_speed, move.temperature, - move.volumetric_rate(), move.extruder_id, move.cp_color_id }); -#endif // ENABLE_SPLITTED_VERTEX_BUFFER } GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -881,12 +844,10 @@ void GCodeViewer::render() const #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#if ENABLE_SPLITTED_VERTEX_BUFFER bool GCodeViewer::can_export_toolpaths() const { return has_data() && m_buffers[buffer_id(EMoveType::Extrude)].render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle; } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned int last) { @@ -894,12 +855,8 @@ void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned in for (const TBuffer& buffer : m_buffers) { if (buffer.visible) { for (const Path& path : buffer.paths) { -#if ENABLE_SPLITTED_VERTEX_BUFFER if (path.sub_paths.front().first.s_id <= id && id <= path.sub_paths.back().last.s_id) -#else - if (path.first.s_id <= id && id <= path.last.s_id) -#endif // ENABLE_SPLITTED_VERTEX_BUFFER - return true; + return true; } } } @@ -1021,20 +978,16 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const if (!t_buffer.has_data()) return; -#if ENABLE_SPLITTED_VERTEX_BUFFER if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Triangle) return; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER // collect color information to generate materials std::vector colors; for (const RenderPath& path : t_buffer.render_paths) { colors.push_back(path.color); } -#if ENABLE_SPLITTED_VERTEX_BUFFER std::sort(colors.begin(), colors.end()); colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); -#endif // ENABLE_SPLITTED_VERTEX_BUFFER // save materials file boost::filesystem::path mat_filename(filename); @@ -1069,7 +1022,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SLIC3R_VERSION); fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str()); -#if ENABLE_SPLITTED_VERTEX_BUFFER const size_t floats_per_vertex = t_buffer.vertices.vertex_size_floats(); std::vector out_vertices; @@ -1155,253 +1107,10 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const } ++i; } -#else - // get vertices data from vertex buffer on gpu - size_t floats_per_vertex = t_buffer.vertices.vertex_size_floats(); - VertexBuffer vertices = VertexBuffer(t_buffer.vertices.count * floats_per_vertex); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, t_buffer.vertices.id)); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, t_buffer.vertices.data_size_bytes(), vertices.data())); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - - // get indices data from index buffer on gpu - MultiIndexBuffer indices; - for (size_t i = 0; i < t_buffer.indices.size(); ++i) { - indices.push_back(IndexBuffer(t_buffer.indices[i].count)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, t_buffer.indices[i].id)); - glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast(indices.back().size() * sizeof(unsigned int)), indices.back().data())); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - - auto get_vertex = [&vertices, floats_per_vertex](unsigned int id) { - // extract vertex from vector of floats - unsigned int base_id = id * floats_per_vertex; - return Vec3f(vertices[base_id + 0], vertices[base_id + 1], vertices[base_id + 2]); - }; - - struct Segment - { - Vec3f v1; - Vec3f v2; - Vec3f dir; - Vec3f right; - Vec3f up; - Vec3f rl_displacement; - Vec3f tb_displacement; - float length; - }; - - auto generate_segment = [get_vertex](unsigned int start_id, unsigned int end_id, float half_width, float half_height) { - auto local_basis = [](const Vec3f& dir) { - // calculate local basis (dir, right, up) on given segment - std::array ret; - ret[0] = dir.normalized(); - if (std::abs(ret[0][2]) < EPSILON) { - // segment parallel to XY plane - ret[1] = { ret[0][1], -ret[0][0], 0.0f }; - ret[2] = Vec3f::UnitZ(); - } - else if (std::abs(std::abs(ret[0].dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) { - // segment parallel to Z axis - ret[1] = Vec3f::UnitX(); - ret[2] = Vec3f::UnitY(); - } - else { - ret[0] = dir.normalized(); - ret[1] = ret[0].cross(Vec3f::UnitZ()).normalized(); - ret[2] = ret[1].cross(ret[0]); - } - return ret; - }; - - Vec3f v1 = get_vertex(start_id) - half_height * Vec3f::UnitZ(); - Vec3f v2 = get_vertex(end_id) - half_height * Vec3f::UnitZ(); - float length = (v2 - v1).norm(); - const auto&& [dir, right, up] = local_basis(v2 - v1); - return Segment({ v1, v2, dir, right, up, half_width * right, half_height * up, length }); - }; - - size_t out_vertices_count = 0; - unsigned int indices_per_segment = t_buffer.indices_per_segment(); - unsigned int start_vertex_offset = t_buffer.start_segment_vertex_offset(); - unsigned int end_vertex_offset = t_buffer.end_segment_vertex_offset(); - - size_t i = 0; - for (const RenderPath& render_path : t_buffer.render_paths) { - // get paths segments from buffer paths - const IndexBuffer& ibuffer = indices[render_path.ibuffer_id]; - const Path& path = t_buffer.paths[render_path.path_id]; - - float half_width = 0.5f * path.width; - // clamp height to avoid artifacts due to z-fighting when importing the obj file into blender and similar - float half_height = std::max(0.5f * path.height, 0.005f); - - // generates vertices/normals/triangles - std::vector out_vertices; - std::vector out_normals; - using Triangle = std::array; - std::vector out_triangles; - for (size_t j = 0; j < render_path.offsets.size(); ++j) { - unsigned int start = static_cast(render_path.offsets[j] / sizeof(unsigned int)); - unsigned int end = start + render_path.sizes[j]; - - for (size_t k = start; k < end; k += static_cast(indices_per_segment)) { - Segment curr = generate_segment(ibuffer[k + start_vertex_offset], ibuffer[k + end_vertex_offset], half_width, half_height); - if (k == start) { - // starting endpoint vertices/normals - out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right - out_vertices.push_back(curr.v1 + curr.tb_displacement); out_normals.push_back(curr.up); // top - out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left - out_vertices.push_back(curr.v1 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom - out_vertices_count += 4; - - // starting cap triangles - size_t base_id = out_vertices_count - 4 + 1; - out_triangles.push_back({ base_id + 0, base_id + 1, base_id + 2 }); - out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 3 }); - } - else { - // for the endpoint shared by the current and the previous segments - // we keep the top and bottom vertices of the previous vertices - // and add new left/right vertices for the current segment - out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right - out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left - out_vertices_count += 2; - - size_t first_vertex_id = k - static_cast(indices_per_segment); - Segment prev = generate_segment(ibuffer[first_vertex_id + start_vertex_offset], ibuffer[first_vertex_id + end_vertex_offset], half_width, half_height); - float disp = 0.0f; - float cos_dir = prev.dir.dot(curr.dir); - if (cos_dir > -0.9998477f) { - // if the angle between adjacent segments is smaller than 179 degrees - Vec3f med_dir = (prev.dir + curr.dir).normalized(); - disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f))); - } - - Vec3f disp_vec = disp * prev.dir; - - bool is_right_turn = prev.up.dot(prev.dir.cross(curr.dir)) <= 0.0f; - if (cos_dir < 0.7071068f) { - // if the angle between two consecutive segments is greater than 45 degrees - // we add a cap in the outside corner - // and displace the vertices in the inside corner to the same position, if possible - if (is_right_turn) { - // corner cap triangles (left) - size_t base_id = out_vertices_count - 6 + 1; - out_triangles.push_back({ base_id + 5, base_id + 2, base_id + 1 }); - out_triangles.push_back({ base_id + 5, base_id + 3, base_id + 2 }); - - // update right vertices - if (disp > 0.0f && disp < prev.length && disp < curr.length) { - base_id = out_vertices.size() - 6; - out_vertices[base_id + 0] -= disp_vec; - out_vertices[base_id + 4] = out_vertices[base_id + 0]; - } - } - else { - // corner cap triangles (right) - size_t base_id = out_vertices_count - 6 + 1; - out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 1 }); - out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 4 }); - - // update left vertices - if (disp > 0.0f && disp < prev.length && disp < curr.length) { - base_id = out_vertices.size() - 6; - out_vertices[base_id + 2] -= disp_vec; - out_vertices[base_id + 5] = out_vertices[base_id + 2]; - } - } - } - else { - // if the angle between two consecutive segments is lesser than 45 degrees - // displace the vertices to the same position - if (is_right_turn) { - size_t base_id = out_vertices.size() - 6; - // right - out_vertices[base_id + 0] -= disp_vec; - out_vertices[base_id + 4] = out_vertices[base_id + 0]; - // left - out_vertices[base_id + 2] += disp_vec; - out_vertices[base_id + 5] = out_vertices[base_id + 2]; - } - else { - size_t base_id = out_vertices.size() - 6; - // right - out_vertices[base_id + 0] += disp_vec; - out_vertices[base_id + 4] = out_vertices[base_id + 0]; - // left - out_vertices[base_id + 2] -= disp_vec; - out_vertices[base_id + 5] = out_vertices[base_id + 2]; - } - } - } - - // current second endpoint vertices/normals - out_vertices.push_back(curr.v2 + curr.rl_displacement); out_normals.push_back(curr.right); // right - out_vertices.push_back(curr.v2 + curr.tb_displacement); out_normals.push_back(curr.up); // top - out_vertices.push_back(curr.v2 - curr.rl_displacement); out_normals.push_back(-curr.right); // left - out_vertices.push_back(curr.v2 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom - out_vertices_count += 4; - - // sides triangles - if (k == start) { - size_t base_id = out_vertices_count - 8 + 1; - out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 5 }); - out_triangles.push_back({ base_id + 0, base_id + 5, base_id + 1 }); - out_triangles.push_back({ base_id + 1, base_id + 5, base_id + 6 }); - out_triangles.push_back({ base_id + 1, base_id + 6, base_id + 2 }); - out_triangles.push_back({ base_id + 2, base_id + 6, base_id + 7 }); - out_triangles.push_back({ base_id + 2, base_id + 7, base_id + 3 }); - out_triangles.push_back({ base_id + 3, base_id + 7, base_id + 4 }); - out_triangles.push_back({ base_id + 3, base_id + 4, base_id + 0 }); - } - else { - size_t base_id = out_vertices_count - 10 + 1; - out_triangles.push_back({ base_id + 4, base_id + 6, base_id + 7 }); - out_triangles.push_back({ base_id + 4, base_id + 7, base_id + 1 }); - out_triangles.push_back({ base_id + 1, base_id + 7, base_id + 8 }); - out_triangles.push_back({ base_id + 1, base_id + 8, base_id + 5 }); - out_triangles.push_back({ base_id + 5, base_id + 8, base_id + 9 }); - out_triangles.push_back({ base_id + 5, base_id + 9, base_id + 3 }); - out_triangles.push_back({ base_id + 3, base_id + 9, base_id + 6 }); - out_triangles.push_back({ base_id + 3, base_id + 6, base_id + 4 }); - } - - if (k + 2 == end) { - // ending cap triangles - size_t base_id = out_vertices_count - 4 + 1; - out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 1 }); - out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 2 }); - } - } - } - - // save to file - fprintf(fp, "\n# vertices path %zu\n", i + 1); - for (const Vec3f& v : out_vertices) { - fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]); - } - - fprintf(fp, "\n# normals path %zu\n", i + 1); - for (const Vec3f& n : out_normals) { - fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]); - } - - fprintf(fp, "\n# material path %zu\n", i + 1); - fprintf(fp, "usemtl material_%zu\n", i + 1); - - fprintf(fp, "\n# triangles path %zu\n", i + 1); - for (const Triangle& t : out_triangles) { - fprintf(fp, "f %zu//%zu %zu//%zu %zu//%zu\n", t[0], t[0], t[1], t[1], t[2], t[2]); - } - - ++ i; - } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER fclose(fp); } -#if ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { // max index buffer size, in bytes @@ -2265,667 +1974,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (progress_dialog != nullptr) progress_dialog->Destroy(); } -#else -void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) -{ -#if ENABLE_GCODE_VIEWER_STATISTICS - auto start_time = std::chrono::high_resolution_clock::now(); - m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex); - m_statistics.results_time = gcode_result.time; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - // vertices data - m_moves_count = gcode_result.moves.size(); - if (m_moves_count == 0) - return; - - unsigned int progress_count = 0; - static const unsigned int progress_threshold = 1000; - wxProgressDialog* progress_dialog = wxGetApp().is_gcode_viewer() ? - new wxProgressDialog(_L("Generating toolpaths"), "...", - 100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; - - m_extruders_count = gcode_result.extruders_count; - - for (size_t i = 0; i < m_moves_count; ++i) { - const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - if (wxGetApp().is_gcode_viewer()) - // for the gcode viewer we need all moves to correctly size the printbed - m_paths_bounding_box.merge(move.position.cast()); - else { - if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) - m_paths_bounding_box.merge(move.position.cast()); - } - } - - // max bounding box (account for tool marker) - m_max_bounding_box = m_paths_bounding_box; - m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); - - auto log_memory_usage = [this](const std::string& label, const std::vector& vertices, const std::vector& indices) { - int64_t vertices_size = 0; - for (size_t i = 0; i < vertices.size(); ++i) { - vertices_size += SLIC3R_STDVEC_MEMSIZE(vertices[i], float); - } - int64_t indices_size = 0; - for (size_t i = 0; i < indices.size(); ++i) { - for (size_t j = 0; j < indices[i].size(); ++j) { - indices_size += SLIC3R_STDVEC_MEMSIZE(indices[i][j], unsigned int); - } - } - log_memory_used(label, vertices_size + indices_size); - }; - - // format data into the buffers to be rendered as points - auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) { - vertices.push_back(curr.position[0]); - vertices.push_back(curr.position[1]); - vertices.push_back(curr.position[2]); - }; - auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, - unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { - buffer.add_path(curr, ibuffer_id, indices.size(), move_id); - indices.push_back(static_cast(indices.size())); - }; - - // format data into the buffers to be rendered as lines - auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, - VertexBuffer& vertices) { - // x component of the normal to the current segment (the normal is parallel to the XY plane) - float normal_x = (curr.position - prev.position).normalized()[1]; - - auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) { - // add position - vertices.push_back(vertex.position[0]); - vertices.push_back(vertex.position[1]); - vertices.push_back(vertex.position[2]); - // add normal x component - vertices.push_back(normal_x); - }; - - // add previous vertex - add_vertex(prev); - // add current vertex - add_vertex(curr); - }; - auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, - unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - // add starting index - indices.push_back(static_cast(indices.size())); - buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1); - buffer.paths.back().first.position = prev.position; - } - - Path& last_path = buffer.paths.back(); - if (last_path.first.i_id != last_path.last.i_id) { - // add previous index - indices.push_back(static_cast(indices.size())); - } - - // add current index - indices.push_back(static_cast(indices.size())); - last_path.last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; - }; - - // format data into the buffers to be rendered as solid - auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, - VertexBuffer& vertices, size_t move_id) { - static Vec3f prev_dir; - static Vec3f prev_up; - static float prev_length; - auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) { - // append position - vertices.push_back(position[0]); - vertices.push_back(position[1]); - vertices.push_back(position[2]); - // append normal - vertices.push_back(normal[0]); - vertices.push_back(normal[1]); - vertices.push_back(normal[2]); - }; - auto extract_position_at = [](const VertexBuffer& vertices, size_t id) { - return Vec3f(vertices[id + 0], vertices[id + 1], vertices[id + 2]); - }; - auto update_position_at = [](VertexBuffer& vertices, size_t id, const Vec3f& position) { - vertices[id + 0] = position[0]; - vertices[id + 1] = position[1]; - vertices[id + 2] = position[2]; - }; - - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, 0, 0, move_id - 1); - buffer.paths.back().first.position = prev.position; - } - - unsigned int starting_vertices_size = static_cast(vertices.size() / buffer.vertices.vertex_size_floats()); - - Vec3f dir = (curr.position - prev.position).normalized(); - Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); - Vec3f left = -right; - Vec3f up = right.cross(dir); - Vec3f down = -up; - - Path& last_path = buffer.paths.back(); - - float half_width = 0.5f * last_path.width; - float half_height = 0.5f * last_path.height; - - Vec3f prev_pos = prev.position - half_height * up; - Vec3f curr_pos = curr.position - half_height * up; - - float length = (curr_pos - prev_pos).norm(); - if (last_path.vertices_count() == 1) { - // 1st segment - - // vertices 1st endpoint - store_vertex(vertices, prev_pos + half_height * up, up); - store_vertex(vertices, prev_pos + half_width * right, right); - store_vertex(vertices, prev_pos + half_height * down, down); - store_vertex(vertices, prev_pos + half_width * left, left); - - // vertices 2nd endpoint - store_vertex(vertices, curr_pos + half_height * up, up); - store_vertex(vertices, curr_pos + half_width * right, right); - store_vertex(vertices, curr_pos + half_height * down, down); - store_vertex(vertices, curr_pos + half_width * left, left); - } - else { - // any other segment - float displacement = 0.0f; - float cos_dir = prev_dir.dot(dir); - if (cos_dir > -0.9998477f) { - // if the angle between adjacent segments is smaller than 179 degrees - Vec3f med_dir = (prev_dir + dir).normalized(); - displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); - } - - Vec3f displacement_vec = displacement * prev_dir; - bool can_displace = displacement > 0.0f && displacement < prev_length&& displacement < length; - - size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats(); - size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats(); - Vec3f prev_right_pos = extract_position_at(vertices, prev_right_id); - Vec3f prev_left_pos = extract_position_at(vertices, prev_left_id); - - bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; - // whether the angle between adjacent segments is greater than 45 degrees - bool is_sharp = cos_dir < 0.7071068f; - - bool right_displaced = false; - bool left_displaced = false; - - // displace the vertex (inner with respect to the corner) of the previous segment 2nd enpoint, if possible - if (can_displace) { - if (is_right_turn) { - prev_right_pos -= displacement_vec; - update_position_at(vertices, prev_right_id, prev_right_pos); - right_displaced = true; - } - else { - prev_left_pos -= displacement_vec; - update_position_at(vertices, prev_left_id, prev_left_pos); - left_displaced = true; - } - } - - if (!is_sharp) { - // displace the vertex (outer with respect to the corner) of the previous segment 2nd enpoint, if possible - if (can_displace) { - if (is_right_turn) { - prev_left_pos += displacement_vec; - update_position_at(vertices, prev_left_id, prev_left_pos); - left_displaced = true; - } - else { - prev_right_pos += displacement_vec; - update_position_at(vertices, prev_right_id, prev_right_pos); - right_displaced = true; - } - } - - // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint) - // vertices position matches that of the previous segment 2nd endpoint, if displaced - store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right); - store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left); - } - else { - // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint) - // the inner corner vertex position matches that of the previous segment 2nd endpoint, if displaced - if (is_right_turn) { - store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right); - store_vertex(vertices, prev_pos + half_width * left, left); - } - else { - store_vertex(vertices, prev_pos + half_width * right, right); - store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left); - } - } - - // vertices 2nd endpoint - store_vertex(vertices, curr_pos + half_height * up, up); - store_vertex(vertices, curr_pos + half_width * right, right); - store_vertex(vertices, curr_pos + half_height * down, down); - store_vertex(vertices, curr_pos + half_width * left, left); - } - - last_path.last = { 0, 0, move_id, curr.position }; - prev_dir = dir; - prev_up = up; - prev_length = length; - }; - auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, - size_t& buffer_vertices_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { - static Vec3f prev_dir; - static Vec3f prev_up; - static float prev_length; - auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) { - indices.push_back(i1); - indices.push_back(i2); - indices.push_back(i3); - }; - auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) { - store_triangle(indices, id, id, id); - store_triangle(indices, id, id, id); - }; - - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1); - buffer.paths.back().first.position = prev.position; - } - - Vec3f dir = (curr.position - prev.position).normalized(); - Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized(); - Vec3f up = right.cross(dir); - - Path& last_path = buffer.paths.back(); - - float half_width = 0.5f * last_path.width; - float half_height = 0.5f * last_path.height; - - Vec3f prev_pos = prev.position - half_height * up; - Vec3f curr_pos = curr.position - half_height * up; - - float length = (curr_pos - prev_pos).norm(); - if (last_path.vertices_count() == 1) { - // 1st segment - - // triangles starting cap - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 2, buffer_vertices_size + 1); - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 3, buffer_vertices_size + 2); - - // dummy triangles outer corner cap - append_dummy_cap(indices, buffer_vertices_size); - - // triangles sides - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 1, buffer_vertices_size + 4); - store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 5, buffer_vertices_size + 4); - store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 2, buffer_vertices_size + 5); - store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 6, buffer_vertices_size + 5); - store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 3, buffer_vertices_size + 6); - store_triangle(indices, buffer_vertices_size + 3, buffer_vertices_size + 7, buffer_vertices_size + 6); - store_triangle(indices, buffer_vertices_size + 3, buffer_vertices_size + 0, buffer_vertices_size + 7); - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 4, buffer_vertices_size + 7); - - // triangles ending cap - store_triangle(indices, buffer_vertices_size + 4, buffer_vertices_size + 6, buffer_vertices_size + 7); - store_triangle(indices, buffer_vertices_size + 4, buffer_vertices_size + 5, buffer_vertices_size + 6); - - buffer_vertices_size += 8; - } - else { - // any other segment - float displacement = 0.0f; - float cos_dir = prev_dir.dot(dir); - if (cos_dir > -0.9998477f) { - // if the angle between adjacent segments is smaller than 179 degrees - Vec3f med_dir = (prev_dir + dir).normalized(); - displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); - } - - Vec3f displacement_vec = displacement * prev_dir; - bool can_displace = displacement > 0.0f && displacement < prev_length && displacement < length; - - bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; - // whether the angle between adjacent segments is greater than 45 degrees - bool is_sharp = cos_dir < 0.7071068f; - - bool right_displaced = false; - bool left_displaced = false; - - if (!is_sharp) { - if (can_displace) { - if (is_right_turn) - left_displaced = true; - else - right_displaced = true; - } - } - - // triangles starting cap - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size - 2, buffer_vertices_size + 0); - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 1, buffer_vertices_size - 2); - - // triangles outer corner cap - if (is_right_turn) { - if (left_displaced) - // dummy triangles - append_dummy_cap(indices, buffer_vertices_size); - else { - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 1, buffer_vertices_size - 1); - store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size - 2, buffer_vertices_size - 1); - } - } - else { - if (right_displaced) - // dummy triangles - append_dummy_cap(indices, buffer_vertices_size); - else { - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size - 3, buffer_vertices_size + 0); - store_triangle(indices, buffer_vertices_size - 3, buffer_vertices_size - 2, buffer_vertices_size + 0); - } - } - - // triangles sides - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 0, buffer_vertices_size + 2); - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 3, buffer_vertices_size + 2); - store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size - 2, buffer_vertices_size + 3); - store_triangle(indices, buffer_vertices_size - 2, buffer_vertices_size + 4, buffer_vertices_size + 3); - store_triangle(indices, buffer_vertices_size - 2, buffer_vertices_size + 1, buffer_vertices_size + 4); - store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 5, buffer_vertices_size + 4); - store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size - 4, buffer_vertices_size + 5); - store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 2, buffer_vertices_size + 5); - - // triangles ending cap - store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 4, buffer_vertices_size + 5); - store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 3, buffer_vertices_size + 4); - - buffer_vertices_size += 6; - } - - last_path.last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; - prev_dir = dir; - prev_up = up; - prev_length = length; - }; - - wxBusyCursor busy; - - // to reduce the peak in memory usage, we split the generation of the vertex and index buffers in two steps. - // the data are deleted as soon as they are sent to the gpu. - std::vector vertices(m_buffers.size()); - std::vector indices(m_buffers.size()); - std::vector options_zs; - - // toolpaths data -> extract vertices from result - for (size_t i = 0; i < m_moves_count; ++i) { - // skip first vertex - if (i == 0) - continue; - - ++progress_count; - if (progress_dialog != nullptr && progress_count % progress_threshold == 0) { - progress_dialog->Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))), - _L("Generating vertex buffer") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%"); - progress_dialog->Fit(); - progress_count = 0; - } - - const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; - const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - - unsigned char id = buffer_id(curr.type); - TBuffer& buffer = m_buffers[id]; - VertexBuffer& buffer_vertices = vertices[id]; - - switch (buffer.render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: { - add_vertices_as_point(curr, buffer_vertices); - break; - } - case TBuffer::ERenderPrimitiveType::Line: { - add_vertices_as_line(prev, curr, buffer_vertices); - break; - } - case TBuffer::ERenderPrimitiveType::Triangle: { - add_vertices_as_solid(prev, curr, buffer, buffer_vertices, i); - break; - } - } - - if (curr.type == EMoveType::Pause_Print || curr.type == EMoveType::Custom_GCode) { - const float* const last_z = options_zs.empty() ? nullptr : &options_zs.back(); - if (last_z == nullptr || curr.position[2] < *last_z - EPSILON || *last_z + EPSILON < curr.position[2]) - options_zs.emplace_back(curr.position[2]); - } - } - - // move the wipe toolpaths half height up to render them on proper position - VertexBuffer& wipe_vertices = vertices[buffer_id(EMoveType::Wipe)]; - for (size_t i = 2; i < wipe_vertices.size(); i += 3) { - wipe_vertices[i] += 0.5f * GCodeProcessor::Wipe_Height; - } - - log_memory_usage("Loaded G-code generated vertex buffers, ", vertices, indices); - - // toolpaths data -> send vertices data to gpu - for (size_t i = 0; i < m_buffers.size(); ++i) { - TBuffer& buffer = m_buffers[i]; - - const VertexBuffer& buffer_vertices = vertices[i]; - buffer.vertices.count = buffer_vertices.size() / buffer.vertices.vertex_size_floats(); -#if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.total_vertices_gpu_size += buffer_vertices.size() * sizeof(float); - m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast(buffer_vertices.size() * sizeof(float))); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - if (buffer.vertices.count > 0) { -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.vbuffers_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - glsafe(::glGenBuffers(1, &buffer.vertices.id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer_vertices.size() * sizeof(float), buffer_vertices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - } - - // dismiss vertices data, no more needed - std::vector().swap(vertices); - - // toolpaths data -> extract indices from result - // paths may have been filled while extracting vertices, - // so reset them, they will be filled again while extracting indices - for (TBuffer& buffer : m_buffers) { - buffer.paths.clear(); - } - - // max index buffer size - const size_t IBUFFER_THRESHOLD = 1024 * 1024 * 32; - - // variable used to keep track of the current size (in vertices) of the vertex buffer - std::vector curr_buffer_vertices_size(m_buffers.size(), 0); - for (size_t i = 0; i < m_moves_count; ++i) { - // skip first vertex - if (i == 0) - continue; - - ++progress_count; - if (progress_dialog != nullptr && progress_count % progress_threshold == 0) { - progress_dialog->Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))), - _L("Generating index buffers") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%"); - progress_dialog->Fit(); - progress_count = 0; - } - - const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; - const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - - unsigned char id = buffer_id(curr.type); - TBuffer& buffer = m_buffers[id]; - MultiIndexBuffer& buffer_indices = indices[id]; - if (buffer_indices.empty()) - buffer_indices.push_back(IndexBuffer()); - - // if adding the indices for the current segment exceeds the threshold size of the current index buffer - // create another index buffer, and move the current path indices into it - if (buffer_indices.back().size() >= IBUFFER_THRESHOLD - static_cast(buffer.indices_per_segment())) { - buffer_indices.push_back(IndexBuffer()); - if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) { - if (!(prev.type != curr.type || !buffer.paths.back().matches(curr))) { - Path& last_path = buffer.paths.back(); - size_t delta_id = last_path.last.i_id - last_path.first.i_id; - - // move indices of the last path from the previous into the new index buffer - IndexBuffer& src_buffer = buffer_indices[buffer_indices.size() - 2]; - IndexBuffer& dst_buffer = buffer_indices[buffer_indices.size() - 1]; - std::move(src_buffer.begin() + last_path.first.i_id, src_buffer.end(), std::back_inserter(dst_buffer)); - src_buffer.erase(src_buffer.begin() + last_path.first.i_id, src_buffer.end()); - - // updates path indices - last_path.first.b_id = buffer_indices.size() - 1; - last_path.first.i_id = 0; - last_path.last.b_id = buffer_indices.size() - 1; - last_path.last.i_id = delta_id; - } - } - } - - switch (buffer.render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: { - add_indices_as_point(curr, buffer, static_cast(buffer_indices.size()) - 1, buffer_indices.back(), i); - break; - } - case TBuffer::ERenderPrimitiveType::Line: { - add_indices_as_line(prev, curr, buffer, static_cast(buffer_indices.size()) - 1, buffer_indices.back(), i); - break; - } - case TBuffer::ERenderPrimitiveType::Triangle: { - add_indices_as_solid(prev, curr, buffer, curr_buffer_vertices_size[id], static_cast(buffer_indices.size()) - 1, buffer_indices.back(), i); - break; - } - } - } - - if (progress_dialog != nullptr) { - progress_dialog->Update(100, ""); - progress_dialog->Fit(); - } - - log_memory_usage("Loaded G-code generated indices buffers, ", vertices, indices); - - // toolpaths data -> send indices data to gpu - for (size_t i = 0; i < m_buffers.size(); ++i) { - TBuffer& buffer = m_buffers[i]; - - for (size_t j = 0; j < indices[i].size(); ++j) { - const IndexBuffer& buffer_indices = indices[i][j]; - buffer.indices.push_back(IBuffer()); - IBuffer& ibuffer = buffer.indices.back(); - ibuffer.count = buffer_indices.size(); -#if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.total_indices_gpu_size += ibuffer.count * sizeof(unsigned int); - m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast(ibuffer.count * sizeof(unsigned int))); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - if (ibuffer.count > 0) { -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.ibuffers_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - glsafe(::glGenBuffers(1, &ibuffer.id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer.id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer_indices.size() * sizeof(unsigned int), buffer_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - } - } - -#if ENABLE_GCODE_VIEWER_STATISTICS - for (const TBuffer& buffer : m_buffers) { - m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path); - } - unsigned int travel_buffer_id = buffer_id(EMoveType::Travel); - const MultiIndexBuffer& travel_buffer_indices = indices[travel_buffer_id]; - for (size_t i = 0; i < travel_buffer_indices.size(); ++i) { - m_statistics.travel_segments_count += travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment(); - } - unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe); - const MultiIndexBuffer& wipe_buffer_indices = indices[wipe_buffer_id]; - for (size_t i = 0; i < wipe_buffer_indices.size(); ++i) { - m_statistics.wipe_segments_count += wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment(); - } - unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude); - const MultiIndexBuffer& extrude_buffer_indices = indices[extrude_buffer_id]; - for (size_t i = 0; i < extrude_buffer_indices.size(); ++i) { - m_statistics.extrude_segments_count += extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment(); - } -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - // dismiss indices data, no more needed - std::vector().swap(indices); - - // layers zs / roles / extruder ids / cp color ids -> extract from result - size_t last_travel_s_id = 0; - for (size_t i = 0; i < m_moves_count; ++i) { - const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - if (move.type == EMoveType::Extrude) { - // layers zs - const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back(); - double z = static_cast(move.position[2]); - if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z) - m_layers.append(z, { last_travel_s_id, i }); - else - m_layers.get_endpoints().back().last = i; - // extruder ids - m_extruder_ids.emplace_back(move.extruder_id); - // roles - if (i > 0) - m_roles.emplace_back(move.extrusion_role); - } - else if (move.type == EMoveType::Travel) { - if (i - last_travel_s_id > 1 && !m_layers.empty()) - m_layers.get_endpoints().back().last = i; - - last_travel_s_id = i; - } - } - - // set layers z range - if (!m_layers.empty()) - m_layers_z_range = { 0, static_cast(m_layers.size() - 1) }; - - // change color of paths whose layer contains option points - if (!options_zs.empty()) { - TBuffer& extrude_buffer = m_buffers[buffer_id(EMoveType::Extrude)]; - for (Path& path : extrude_buffer.paths) { - float z = path.first.position[2]; - if (std::find_if(options_zs.begin(), options_zs.end(), [z](float f) { return f - EPSILON <= z && z <= f + EPSILON; }) != options_zs.end()) - path.cp_color_id = 255 - path.cp_color_id; - } - } - - // roles -> remove duplicates - std::sort(m_roles.begin(), m_roles.end()); - m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); - m_roles.shrink_to_fit(); - - // extruder ids -> remove duplicates - std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); - m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); - m_extruder_ids.shrink_to_fit(); - - log_memory_usage("Loaded G-code generated extrusion paths, ", vertices, indices); - -#if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.load_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - if (progress_dialog != nullptr) - progress_dialog->Destroy(); -} -#endif // ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::load_shells(const Print& print, bool initialized) { @@ -2981,7 +2029,6 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -#if ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const { #if ENABLE_GCODE_VIEWER_STATISTICS @@ -3444,261 +2491,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool statistics->refresh_paths_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } -#else -void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const -{ -#if ENABLE_GCODE_VIEWER_STATISTICS - auto start_time = std::chrono::high_resolution_clock::now(); -#endif // ENABLE_GCODE_VIEWER_STATISTICS - auto extrusion_color = [this](const Path& path) { - Color color; - switch (m_view_type) - { - case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } - case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } - case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } - case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } - case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } - case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } - case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } - case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } - case EViewType::ColorPrint: { - if (path.cp_color_id >= static_cast(m_tool_colors.size())) { - color = { 0.5f, 0.5f, 0.5f }; -// // complementary color -// color = m_tool_colors[255 - path.cp_color_id]; -// color = { 1.0f - color[0], 1.0f - color[1], 1.0f - color[2] }; - } - else - color = m_tool_colors[path.cp_color_id]; - - break; - } - default: { color = { 1.0f, 1.0f, 1.0f }; break; } - } - - return color; - }; - - auto travel_color = [this](const Path& path) { - return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ : - ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ : - Travel_Colors[0] /* Move */); - }; - - auto is_in_layers_range = [this](const Path& path, size_t min_id, size_t max_id) { - auto in_layers_range = [this, min_id, max_id](size_t id) { - return m_layers.get_endpoints_at(min_id).first <= id && id <= m_layers.get_endpoints_at(max_id).last; - }; - - return in_layers_range(path.first.s_id) || in_layers_range(path.last.s_id); - }; - - auto is_travel_in_layers_range = [this](size_t path_id, size_t min_id, size_t max_id) { - auto is_in_z_range = [](const Path& path, double min_z, double max_z) { - auto in_z_range = [min_z, max_z](double z) { - return min_z - EPSILON < z&& z < max_z + EPSILON; - }; - - return in_z_range(path.first.position[2]) || in_z_range(path.last.position[2]); - }; - - const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Travel)]; - if (path_id >= buffer.paths.size()) - return false; - - Path path = buffer.paths[path_id]; - size_t first = path_id; - size_t last = path_id; - - // check adjacent paths - while (first > 0 && path.first.position.isApprox(buffer.paths[first - 1].last.position)) { - --first; - path.first = buffer.paths[first].first; - } - while (last < buffer.paths.size() - 1 && path.last.position.isApprox(buffer.paths[last + 1].first.position)) { - ++last; - path.last = buffer.paths[last].last; - } - - size_t min_s_id = m_layers.get_endpoints_at(min_id).first; - size_t max_s_id = m_layers.get_endpoints_at(max_id).last; - - return (min_s_id <= path.first.s_id && path.first.s_id <= max_s_id) || - (min_s_id <= path.last.s_id && path.last.s_id <= max_s_id); - }; - -#if ENABLE_GCODE_VIEWER_STATISTICS - Statistics* statistics = const_cast(&m_statistics); - statistics->render_paths_size = 0; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - - bool top_layer_only = get_app_config()->get("seq_top_layer_only") == "1"; - - SequentialView::Endpoints global_endpoints = { m_moves_count , 0 }; - SequentialView::Endpoints top_layer_endpoints = global_endpoints; - SequentialView* sequential_view = const_cast(&m_sequential_view); - if (top_layer_only || !keep_sequential_current_first) sequential_view->current.first = 0; - if (!keep_sequential_current_last) sequential_view->current.last = m_moves_count; - - // first pass: collect visible paths and update sequential view data - std::vector> paths; - for (size_t b = 0; b < m_buffers.size(); ++b) { - TBuffer& buffer = const_cast(m_buffers[b]); - // reset render paths - buffer.render_paths.clear(); - - if (!buffer.visible) - continue; - - for (size_t i = 0; i < buffer.paths.size(); ++i) { - const Path& path = buffer.paths[i]; - if (path.type == EMoveType::Travel) { - if (!is_travel_in_layers_range(i, m_layers_z_range[0], m_layers_z_range[1])) - continue; - } - else if (!is_in_layers_range(path, m_layers_z_range[0], m_layers_z_range[1])) - continue; - - if (path.type == EMoveType::Extrude && !is_visible(path)) - continue; - - // store valid path - paths.push_back({ &buffer, path.first.b_id, static_cast(i) }); - - global_endpoints.first = std::min(global_endpoints.first, path.first.s_id); - global_endpoints.last = std::max(global_endpoints.last, path.last.s_id); - - if (top_layer_only) { - if (path.type == EMoveType::Travel) { - if (is_travel_in_layers_range(i, m_layers_z_range[1], m_layers_z_range[1])) { - top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.first.s_id); - top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.last.s_id); - } - } - else if (is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) { - top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.first.s_id); - top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.last.s_id); - } - } - } - } - - // update current sequential position - sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, global_endpoints.first, global_endpoints.last) : global_endpoints.first; - sequential_view->current.last = keep_sequential_current_last ? std::clamp(sequential_view->current.last, global_endpoints.first, global_endpoints.last) : global_endpoints.last; - - // get the world position from gpu - bool found = false; - for (const TBuffer& buffer : m_buffers) { - // searches the path containing the current position - for (const Path& path : buffer.paths) { - if (path.contains(m_sequential_view.current.last)) { - unsigned int offset = static_cast(m_sequential_view.current.last - path.first.s_id); - if (offset > 0) { - if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line) - offset = 2 * offset - 1; - else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { - unsigned int indices_count = buffer.indices_per_segment(); - offset = indices_count * (offset - 1) + (indices_count - 6); - } - } - offset += static_cast(path.first.i_id); - - // gets the index from the index buffer on gpu - unsigned int index = 0; - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices[path.first.b_id].id)); - glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast(offset * sizeof(unsigned int)), static_cast(sizeof(unsigned int)), static_cast(&index))); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - // gets the position from the vertices buffer on gpu - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(index * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(sequential_view->current_position.data()))); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - found = true; - break; - } - } - if (found) - break; - } - - // second pass: filter paths by sequential data and collect them by color - RenderPath *render_path = nullptr; - for (const auto& [buffer, ibuffer_id, path_id] : paths) { - const Path& path = buffer->paths[path_id]; - if (m_sequential_view.current.last <= path.first.s_id || path.last.s_id <= m_sequential_view.current.first) - continue; - - Color color; - switch (path.type) - { - case EMoveType::Extrude: { - if (!top_layer_only || - m_sequential_view.current.last == global_endpoints.last || - is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) - color = extrusion_color(path); - else - color = { 0.25f, 0.25f, 0.25f }; - - break; - } - case EMoveType::Travel: { - if (!top_layer_only || m_sequential_view.current.last == global_endpoints.last || is_travel_in_layers_range(path_id, m_layers_z_range[1], m_layers_z_range[1])) - color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); - else - color = { 0.25f, 0.25f, 0.25f }; - - break; - } - case EMoveType::Wipe: { color = Wipe_Color; break; } - default: { color = { 0.0f, 0.0f, 0.0f }; break; } - } - - RenderPath key{ color, static_cast(ibuffer_id), path_id }; - if (render_path == nullptr || ! RenderPathPropertyEqual()(*render_path, key)) - render_path = const_cast(&(*buffer->render_paths.emplace(key).first)); - unsigned int segments_count = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1; - unsigned int size_in_indices = 0; - switch (buffer->render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: { size_in_indices = segments_count; break; } - case TBuffer::ERenderPrimitiveType::Line: - case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (segments_count - 1); break; } - } - render_path->sizes.push_back(size_in_indices); - - unsigned int delta_1st = 0; - if (path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= path.last.s_id) - delta_1st = m_sequential_view.current.first - path.first.s_id; - - if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) - delta_1st *= buffer->indices_per_segment(); - - render_path->offsets.push_back(static_cast((path.first.i_id + delta_1st) * sizeof(unsigned int))); - } - - // set sequential data to their final value - sequential_view->endpoints = top_layer_only ? top_layer_endpoints : global_endpoints; - sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, sequential_view->endpoints.first, sequential_view->endpoints.last) : sequential_view->endpoints.first; - - wxGetApp().plater()->enable_preview_moves_slider(!paths.empty()); - -#if ENABLE_GCODE_VIEWER_STATISTICS - for (const TBuffer& buffer : m_buffers) { - statistics->render_paths_size += SLIC3R_STDUNORDEREDSET_MEMSIZE(buffer.render_paths, RenderPath); - for (const RenderPath& path : buffer.render_paths) { - statistics->render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int); - statistics->render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t); - } - } - statistics->refresh_paths_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); -#endif // ENABLE_GCODE_VIEWER_STATISTICS -} -#endif // ENABLE_SPLITTED_VERTEX_BUFFER - -#if ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::render_toolpaths() const { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS @@ -3875,155 +2668,6 @@ void GCodeViewer::render_toolpaths() const } #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } -#else -void GCodeViewer::render_toolpaths() const -{ -#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - float point_size = 20.0f; -#else - float point_size = 0.8f; -#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - std::array light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f }; - const Camera& camera = wxGetApp().plater()->get_camera(); - double zoom = camera.get_zoom(); - const std::array& viewport = camera.get_viewport(); - float near_plane_height = camera.get_type() == Camera::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : - static_cast(viewport[3]) * 0.0005; - - auto set_uniform_color = [](const std::array& color, GLShaderProgram& shader) { - std::array color4 = { color[0], color[1], color[2], 1.0f }; - shader.set_uniform("uniform_color", color4); - }; - - auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color] - (const TBuffer& buffer, unsigned int ibuffer_id, EOptionsColors color_id, GLShaderProgram& shader) { - set_uniform_color(Options_Colors[static_cast(color_id)], shader); -#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - shader.set_uniform("use_fixed_screen_size", 1); -#else - shader.set_uniform("use_fixed_screen_size", 0); -#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - shader.set_uniform("zoom", zoom); - shader.set_uniform("percent_outline_radius", 0.0f); - shader.set_uniform("percent_center_radius", 0.33f); - shader.set_uniform("point_size", point_size); - shader.set_uniform("near_plane_height", near_plane_height); - - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glEnable(GL_POINT_SPRITE)); - - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - } - - glsafe(::glDisable(GL_POINT_SPRITE)); - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - }; - - auto render_as_lines = [this, light_intensity, set_uniform_color](const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - shader.set_uniform("light_intensity", light_intensity); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - set_uniform_color(path.color, shader); - glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_lines_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - } - }; - - auto render_as_triangles = [this, set_uniform_color](const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - set_uniform_color(path.color, shader); - glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_triangles_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - } - }; - - auto line_width = [](double zoom) { - return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); - }; - - glsafe(::glLineWidth(static_cast(line_width(zoom)))); - - unsigned char begin_id = buffer_id(EMoveType::Retract); - unsigned char end_id = buffer_id(EMoveType::Count); - - for (unsigned char i = begin_id; i < end_id; ++i) { - const TBuffer& buffer = m_buffers[i]; - if (!buffer.visible || !buffer.has_data()) - continue; - - GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); - if (shader != nullptr) { - shader->start_using(); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_size())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - bool has_normals = buffer.vertices.normal_size_floats() > 0; - if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_size())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - } - - for (size_t j = 0; j < buffer.indices.size(); ++j) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices[j].id)); - - switch (buffer.render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: - { - EOptionsColors color = EOptionsColors(0); - switch (buffer_type(i)) - { - case EMoveType::Tool_change: { color = EOptionsColors::ToolChanges; break; } - case EMoveType::Color_change: { color = EOptionsColors::ColorChanges; break; } - case EMoveType::Pause_Print: { color = EOptionsColors::PausePrints; break; } - case EMoveType::Custom_GCode: { color = EOptionsColors::CustomGCodes; break; } - case EMoveType::Retract: { color = EOptionsColors::Retractions; break; } - case EMoveType::Unretract: { color = EOptionsColors::Unretractions; break; } - default: { assert(false); break; } - } - render_as_points(buffer, static_cast(j), color, *shader); - break; - } - case TBuffer::ERenderPrimitiveType::Line: - { - render_as_lines(buffer, static_cast(j), *shader); - break; - } - case TBuffer::ERenderPrimitiveType::Triangle: - { - render_as_triangles(buffer, static_cast(j), *shader); - break; - } - } - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - - shader->stop_using(); - } - } -} -#endif // ENABLE_SPLITTED_VERTEX_BUFFER void GCodeViewer::render_shells() const { @@ -5047,15 +3691,9 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional) } int64_t layers_size = SLIC3R_STDVEC_MEMSIZE(m_layers.get_zs(), double); layers_size += SLIC3R_STDVEC_MEMSIZE(m_layers.get_endpoints(), Layers::Endpoints); -#if ENABLE_SPLITTED_VERTEX_BUFFER BOOST_LOG_TRIVIAL(trace) << label << "(" << format_memsize_MB(additional + paths_size + render_paths_size + layers_size) << ");" << log_memory_info(); -#else - BOOST_LOG_TRIVIAL(trace) << label - << format_memsize_MB(additional + paths_size + render_paths_size + layers_size) - << log_memory_info(); -#endif // ENABLE_SPLITTED_VERTEX_BUFFER } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 112c681d4..3386e314e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -23,17 +23,11 @@ namespace GUI { class GCodeViewer { -#if ENABLE_SPLITTED_VERTEX_BUFFER using IBufferType = unsigned short; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER using Color = std::array; using VertexBuffer = std::vector; -#if ENABLE_SPLITTED_VERTEX_BUFFER using MultiVertexBuffer = std::vector; using IndexBuffer = std::vector; -#else - using IndexBuffer = std::vector; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER using MultiIndexBuffer = std::vector; static const std::vector Extrusion_Role_Colors; @@ -69,24 +63,17 @@ class GCodeViewer }; EFormat format{ EFormat::Position }; -#if ENABLE_SPLITTED_VERTEX_BUFFER // vbos id std::vector vbos; // sizes of the buffers, in bytes, used in export to obj std::vector sizes; -#else - // vbo id - unsigned int id{ 0 }; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER // count of vertices, updated after data are sent to gpu size_t count{ 0 }; size_t data_size_bytes() const { return count * vertex_size_bytes(); } -#if ENABLE_SPLITTED_VERTEX_BUFFER // We set 65536 as max count of vertices inside a vertex buffer to allow // to use unsigned short in place of unsigned int for indices in the index buffer, to save memory size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); } size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); } @@ -116,15 +103,10 @@ class GCodeViewer // ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type struct IBuffer { -#if ENABLE_SPLITTED_VERTEX_BUFFER // id of the associated vertex buffer unsigned int vbo{ 0 }; // ibo id unsigned int ibo{ 0 }; -#else - // ibo id - unsigned int id{ 0 }; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER // count of indices, updated after data are sent to gpu size_t count{ 0 }; @@ -148,7 +130,6 @@ class GCodeViewer Vec3f position{ Vec3f::Zero() }; }; -#if ENABLE_SPLITTED_VERTEX_BUFFER struct Sub_Path { Endpoint first; @@ -158,14 +139,9 @@ class GCodeViewer return first.s_id <= s_id && s_id <= last.s_id; } }; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER EMoveType type{ EMoveType::Noop }; ExtrusionRole role{ erNone }; -#if !ENABLE_SPLITTED_VERTEX_BUFFER - Endpoint first; - Endpoint last; -#endif // !ENABLE_SPLITTED_VERTEX_BUFFER float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -175,12 +151,9 @@ class GCodeViewer float volumetric_rate{ 0.0f }; unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; -#if ENABLE_SPLITTED_VERTEX_BUFFER std::vector sub_paths; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER bool matches(const GCodeProcessor::MoveVertex& move) const; -#if ENABLE_SPLITTED_VERTEX_BUFFER size_t vertices_count() const { return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1; } @@ -202,10 +175,6 @@ class GCodeViewer Endpoint endpoint = { b_id, i_id, s_id, move.position }; sub_paths.push_back({ endpoint , endpoint }); } -#else - size_t vertices_count() const { return last.s_id - first.s_id + 1; } - bool contains(size_t id) const { return first.s_id <= id && id <= last.s_id; } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER }; // Used to batch the indices needed to render the paths @@ -295,7 +264,6 @@ class GCodeViewer // s_id index of first vertex contained in this->vertices void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id); -#if ENABLE_SPLITTED_VERTEX_BUFFER unsigned int max_vertices_per_segment() const { switch (render_primitive_type) { @@ -308,7 +276,6 @@ class GCodeViewer size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast(max_vertices_per_segment()); } size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER unsigned int indices_per_segment() const { switch (render_primitive_type) { @@ -322,9 +289,7 @@ class GCodeViewer default: { return 0; } } } -#if ENABLE_SPLITTED_VERTEX_BUFFER size_t indices_per_segment_size_bytes() const { return static_cast(indices_per_segment() * sizeof(IBufferType)); } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS unsigned int max_indices_per_segment() const { switch (render_primitive_type) @@ -338,24 +303,9 @@ class GCodeViewer size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); } #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS -#if ENABLE_SPLITTED_VERTEX_BUFFER bool has_data() const { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } -#else - unsigned int start_segment_vertex_offset() const { return 0; } - unsigned int end_segment_vertex_offset() const { - switch (render_primitive_type) - { - case ERenderPrimitiveType::Point: { return 0; } - case ERenderPrimitiveType::Line: { return 1; } - case ERenderPrimitiveType::Triangle: { return 36; } // 1st vertex of 13th triangle - default: { return 0; } - } - } - - bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER }; // helper to render shells @@ -434,11 +384,9 @@ class GCodeViewer size_t first{ 0 }; size_t last{ 0 }; -#if ENABLE_SPLITTED_VERTEX_BUFFER bool operator == (const Endpoints& other) const { return first == other.first && last == other.last; } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER }; private: @@ -464,7 +412,6 @@ class GCodeViewer double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; } Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); } -#if ENABLE_SPLITTED_VERTEX_BUFFER bool operator != (const Layers& other) const { if (m_zs != other.m_zs) return true; @@ -473,7 +420,6 @@ class GCodeViewer return false; } -#endif // ENABLE_SPLITTED_VERTEX_BUFFER }; #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS @@ -722,9 +668,7 @@ public: void render() const; bool has_data() const { return !m_roles.empty(); } -#if ENABLE_SPLITTED_VERTEX_BUFFER bool can_export_toolpaths() const; -#endif // ENABLE_SPLITTED_VERTEX_BUFFER const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8873af245..064924c41 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3933,11 +3933,7 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() bool GLCanvas3D::has_toolpaths_to_export() const { -#if ENABLE_SPLITTED_VERTEX_BUFFER return m_gcode_viewer.can_export_toolpaths(); -#else - return m_gcode_viewer.has_data(); -#endif // ENABLE_SPLITTED_VERTEX_BUFFER } void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const From 426d2cd725eacaadbc41ee63d525d2450ccfc6f4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 May 2021 16:05:16 +0200 Subject: [PATCH 65/79] Tech ENABLE_WARNING_TEXTURE_REMOVAL set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 348 --------------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 54 ----- 3 files changed, 404 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f51241acc..a8cdbd77d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -45,8 +45,6 @@ #define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_2_4_0_ALPHA0) // Enable reload from disk command for 3mf files #define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0) -// Removes obsolete warning texture code -#define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_4_0_ALPHA0) // Enable showing gcode line numbers in preview horizontal slider #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0) // Enable validation of custom gcode against gcode processor reserved keywords diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 064924c41..1eb52e92e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -604,278 +604,6 @@ GLCanvas3D::Mouse::Mouse() { } -#if !ENABLE_WARNING_TEXTURE_REMOVAL -const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 120, 120, 120 };//{ 9, 91, 134 }; -const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; - -GLCanvas3D::WarningTexture::WarningTexture() - : GUI::GLTexture() - , m_original_width(0) - , m_original_height(0) -{ -} - -void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas) -{ - // Since we have NotificationsManager.hpp the warning textures are no loger needed. - // However i have left the infrastructure here and only commented the rendering. - // The plater warning / error notifications are added and closed from here. - - std::string text; - bool error = false; - switch (warning) { - case ObjectOutside: text = _u8L("An object outside the print area was detected."); break; - case ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = true; break; - case SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = true; break; - case SomethingNotShown: text = _u8L("Some objects are not visible."); break; - case ObjectClashed: - text = _u8L( "An object outside the print area was detected.\n" - "Resolve the current problem to continue slicing."); - error = true; - break; - } - BOOST_LOG_TRIVIAL(error) << state << " : " << text ; - auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager(); - if (state) { - if(error) - notification_manager.push_plater_error_notification(text); - else - notification_manager.push_plater_warning_notification(text); - } else { - if (error) - notification_manager.close_plater_error_notification(text); - else - notification_manager.close_plater_warning_notification(text); - } - - /* - auto it = std::find(m_warnings.begin(), m_warnings.end(), warning); - - if (state) { - if (it != m_warnings.end()) // this warning is already set to be shown - return; - - m_warnings.push_back(warning); - std::sort(m_warnings.begin(), m_warnings.end()); - } - else { - if (it == m_warnings.end()) // deactivating something that is not active is an easy task - return; - - m_warnings.erase(it); - if (m_warnings.empty()) { // nothing remains to be shown - reset(); - m_msg_text = "";// save information for rescaling - return; - } - } - - // Look at the end of our vector and generate proper texture. - std::string text; - bool red_colored = false; - switch (m_warnings.back()) { - case ObjectOutside : text = L("An object outside the print area was detected"); break; - case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break; - case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break; - case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break; - case ObjectClashed: { - text = L("An object outside the print area was detected\n" - "Resolve the current problem to continue slicing"); - red_colored = true; - break; - } - } - - generate(text, canvas, true, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...) - - // save information for rescaling - m_msg_text = text; - m_is_colored_red = red_colored; - */ -} - - -#ifdef __WXMSW__ -static bool is_font_cleartype(const wxFont &font) -{ - // Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon. - wxString font_desc = font.GetNativeFontInfoDesc(); - // Find the quality field. - wxString sep(";"); - size_t startpos = 0; - for (size_t i = 0; i < 12; ++ i) - startpos = font_desc.find(sep, startpos + 1); - ++ startpos; - size_t endpos = font_desc.find(sep, startpos); - int quality = wxAtoi(font_desc(startpos, endpos - startpos)); - return quality == CLEARTYPE_QUALITY; -} - -// ClearType produces renders, which are difficult to convert into an alpha blended OpenGL texture. -// Therefore it is better to disable it, though Vojtech found out, that the font returned with ClearType -// disabled is signifcantly thicker than the default ClearType font. -// This function modifies the font provided. -static void msw_disable_cleartype(wxFont &font) -{ - // Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon. - wxString font_desc = font.GetNativeFontInfoDesc(); - // Find the quality field. - wxString sep(";"); - size_t startpos_weight = 0; - for (size_t i = 0; i < 5; ++ i) - startpos_weight = font_desc.find(sep, startpos_weight + 1); - ++ startpos_weight; - size_t endpos_weight = font_desc.find(sep, startpos_weight); - // Parse the weight field. - unsigned int weight = wxAtoi(font_desc(startpos_weight, endpos_weight - startpos_weight)); - size_t startpos = endpos_weight; - for (size_t i = 0; i < 6; ++ i) - startpos = font_desc.find(sep, startpos + 1); - ++ startpos; - size_t endpos = font_desc.find(sep, startpos); - int quality = wxAtoi(font_desc(startpos, endpos - startpos)); - if (quality == CLEARTYPE_QUALITY) { - // Replace the weight with a smaller value to compensate the weight of non ClearType font. - wxString sweight = std::to_string(weight * 2 / 4); - size_t len_weight = endpos_weight - startpos_weight; - wxString squality = std::to_string(ANTIALIASED_QUALITY); - font_desc.replace(startpos_weight, len_weight, sweight); - font_desc.replace(startpos + sweight.size() - len_weight, endpos - startpos, squality); - font.SetNativeFontInfo(font_desc); - wxString font_desc2 = font.GetNativeFontInfoDesc(); - } - wxString font_desc2 = font.GetNativeFontInfoDesc(); -} -#endif /* __WXMSW__ */ - -bool GLCanvas3D::WarningTexture::generate(const std::string& msg_utf8, const GLCanvas3D& canvas, bool compress, bool red_colored/* = false*/) -{ - reset(); - - if (msg_utf8.empty()) - return false; - - wxString msg = _(msg_utf8); - - wxMemoryDC memDC; - -#ifdef __WXMSW__ - // set scaled application normal font as default font - wxFont font = wxGetApp().normal_font(); -#else - // select default font - const float scale = canvas.get_canvas_size().get_scale_factor(); -#if ENABLE_RETINA_GL - // For non-visible or non-created window getBackingScaleFactor function return 0.0 value. - // And using of the zero scale causes a crash, when we trying to draw text to the (0,0) rectangle - // https://github.com/prusa3d/PrusaSlicer/issues/3916 - if (scale <= 0.0f) - return false; -#endif - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale); -#endif - - font.MakeLarger(); - font.MakeBold(); - memDC.SetFont(font); - - // calculates texture size - wxCoord w, h; - memDC.GetMultiLineTextExtent(msg, &w, &h); - - m_original_width = (int)w; - m_original_height = (int)h; - m_width = (int)next_highest_power_of_2((uint32_t)w); - m_height = (int)next_highest_power_of_2((uint32_t)h); - - // generates bitmap - wxBitmap bitmap(m_width, m_height); - - memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(*wxBLACK)); - memDC.Clear(); - - // draw message - memDC.SetTextForeground(*wxRED); - memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER); - - memDC.SelectObject(wxNullBitmap); - - // Convert the bitmap into a linear data ready to be loaded into the GPU. - wxImage image = bitmap.ConvertToImage(); - - // prepare buffer - std::vector data(4 * m_width * m_height, 0); - const unsigned char *src = image.GetData(); - for (int h = 0; h < m_height; ++h) { - unsigned char* dst = data.data() + 4 * h * m_width; - for (int w = 0; w < m_width; ++w) { - *dst++ = 255; - if (red_colored) { - *dst++ = 72; // 204 - *dst++ = 65; // 204 - } else { - *dst++ = 255; - *dst++ = 255; - } - *dst++ = (unsigned char)std::min(255, *src); - src += 3; - } - } - - // sends buffer to gpu - glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - glsafe(::glGenTextures(1, &m_id)); - glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id)); - if (compress && GLEW_EXT_texture_compression_s3tc) - glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - else - glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); - glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - - return true; -} - -void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const -{ - if (m_warnings.empty()) - return; - - if (m_id > 0 && m_original_width > 0 && m_original_height > 0 && m_width > 0 && m_height > 0) { - const Size& cnv_size = canvas.get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float left = (-0.5f * (float)m_original_width) * inv_zoom; - float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom; - float right = left + (float)m_original_width * inv_zoom; - float bottom = top - (float)m_original_height * inv_zoom; - - float uv_left = 0.0f; - float uv_top = 0.0f; - float uv_right = (float)m_original_width / (float)m_width; - float uv_bottom = (float)m_original_height / (float)m_height; - - GLTexture::Quad_UVs uvs; - uvs.left_top = { uv_left, uv_top }; - uvs.left_bottom = { uv_left, uv_bottom }; - uvs.right_bottom = { uv_right, uv_bottom }; - uvs.right_top = { uv_right, uv_top }; - - GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); - } -} - -void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas) -{ - if (m_msg_text.empty()) - return; - - generate(m_msg_text, canvas, true, m_is_colored_red); -} -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL - void GLCanvas3D::Labels::render(const std::vector& sorted_instances) const { if (!m_enabled || !is_shown()) @@ -1294,11 +1022,7 @@ void GLCanvas3D::reset_volumes() m_volumes.clear(); m_dirty = true; -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification(EWarning::ObjectOutside, false); -#else - _set_warning_texture(WarningTexture::ObjectOutside, false); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL } int GLCanvas3D::check_volumes_outside_state() const @@ -1352,19 +1076,11 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject if (visible && !mo) toggle_sla_auxiliaries_visibility(true, mo, instance_idx); -#if ENABLE_WARNING_TEXTURE_REMOVAL if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1)) _set_warning_notification(EWarning::SomethingNotShown, true); if (!mo && visible) _set_warning_notification(EWarning::SomethingNotShown, false); -#else - if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1)) - _set_warning_texture(WarningTexture::SomethingNotShown, true); - - if (!mo && visible) - _set_warning_texture(WarningTexture::SomethingNotShown, false); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL } void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx) @@ -2241,31 +1957,18 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re bool fullyOut = false; const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut); -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification(EWarning::ObjectClashed, partlyOut); _set_warning_notification(EWarning::ObjectOutside, fullyOut); if (printer_technology != ptSLA || !contained_min_one) _set_warning_notification(EWarning::SlaSupportsOutside, false); -#else - _set_warning_texture(WarningTexture::ObjectClashed, partlyOut); - _set_warning_texture(WarningTexture::ObjectOutside, fullyOut); - if(printer_technology != ptSLA || !contained_min_one) - _set_warning_texture(WarningTexture::SlaSupportsOutside, false); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, contained_min_one && !m_model->objects.empty() && !partlyOut)); } else { -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification(EWarning::ObjectOutside, false); _set_warning_notification(EWarning::ObjectClashed, false); _set_warning_notification(EWarning::SlaSupportsOutside, false); -#else - _set_warning_texture(WarningTexture::ObjectOutside, false); - _set_warning_texture(WarningTexture::ObjectClashed, false); - _set_warning_texture(WarningTexture::SlaSupportsOutside, false); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } @@ -2308,11 +2011,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) if (wxGetApp().is_editor()) { m_gcode_viewer.update_shells_color_by_extruder(m_config); -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification_if_needed(EWarning::ToolpathOutside); -#else - _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL } } @@ -2339,11 +2038,7 @@ void GLCanvas3D::load_sla_preview() this->reset_volumes(); _load_sla_shells(); _update_sla_shells_outside_state(); -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification_if_needed(EWarning::SlaSupportsOutside); -#else - _show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL } } @@ -2364,11 +2059,7 @@ void GLCanvas3D::load_preview(const std::vector& str_tool_colors, c _load_print_object_toolpaths(*object, str_tool_colors, color_print_values); _update_toolpath_volumes_outside_state(); -#if ENABLE_WARNING_TEXTURE_REMOVAL _set_warning_notification_if_needed(EWarning::ToolpathOutside); -#else - _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL } void GLCanvas3D::bind_event_handlers() @@ -3916,9 +3607,6 @@ void GLCanvas3D::set_cursor(ECursorType type) void GLCanvas3D::msw_rescale() { -#if !ENABLE_WARNING_TEXTURE_REMOVAL - m_warning_texture.msw_rescale(*this); -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL } void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() @@ -5270,9 +4958,6 @@ void GLCanvas3D::_render_overlays() const _check_and_update_toolbar_icon_scale(); _render_gizmos_overlay(); -#if !ENABLE_WARNING_TEXTURE_REMOVAL - _render_warning_texture(); -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL // main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed // to correctly place them @@ -5310,13 +4995,6 @@ void GLCanvas3D::_render_overlays() const glsafe(::glPopMatrix()); } -#if !ENABLE_WARNING_TEXTURE_REMOVAL -void GLCanvas3D::_render_warning_texture() const -{ - m_warning_texture.render(*this); -} -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL - void GLCanvas3D::_render_volumes_for_picking() const { static const GLfloat INV_255 = 1.0f / 255.0f; @@ -6313,7 +5991,6 @@ void GLCanvas3D::_update_sla_shells_outside_state() } } -#if ENABLE_WARNING_TEXTURE_REMOVAL void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) { _set_current(); @@ -6330,24 +6007,6 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) } _set_warning_notification(warning, show); } -#else -void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning) -{ - _set_current(); - bool show = false; - if (!m_volumes.empty()) - show = _is_any_volume_outside(); - else { - if (wxGetApp().is_editor()) { - BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); - const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); - if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) - show = !test_volume.contains(paths_volume); - } - } - _set_warning_texture(warning, show); -} -#endif // ENABLE_WARNING_TEXTURE_REMOVAL std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { @@ -6371,7 +6030,6 @@ std::vector GLCanvas3D::_parse_colors(const std::vector& col return output; } -#if ENABLE_WARNING_TEXTURE_REMOVAL void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) { enum ErrorType{ @@ -6417,12 +6075,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) break; } } -#else -void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state) -{ - m_warning_texture.activate(warning, state, *this); -} -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL bool GLCanvas3D::_is_any_volume_outside() const { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 10457d877..f51f53f62 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -295,7 +295,6 @@ class GLCanvas3D bool matches(double z) const { return this->z == z; } }; -#if ENABLE_WARNING_TEXTURE_REMOVAL enum class EWarning { ObjectOutside, ToolpathOutside, @@ -303,46 +302,6 @@ class GLCanvas3D SomethingNotShown, ObjectClashed }; -#else - class WarningTexture : public GUI::GLTexture - { - public: - WarningTexture(); - - enum Warning { - ObjectOutside, - ToolpathOutside, - SlaSupportsOutside, - SomethingNotShown, - ObjectClashed - }; - - // Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously, - // only the last one is shown (decided by the order in the enum above). - void activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas); - void render(const GLCanvas3D& canvas) const; - - // function used to get an information for rescaling of the warning - void msw_rescale(const GLCanvas3D& canvas); - - private: - static const unsigned char Background_Color[3]; - static const unsigned char Opacity; - - int m_original_width; - int m_original_height; - - // information for rescaling of the warning legend - std::string m_msg_text = ""; - bool m_is_colored_red{false}; - - // Information about which warnings are currently active. - std::vector m_warnings; - - // Generates the texture with given text. - bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false); - }; -#endif // ENABLE_WARNING_TEXTURE_REMOVAL #if ENABLE_RENDER_STATISTICS class RenderStats @@ -440,9 +399,6 @@ private: std::unique_ptr m_retina_helper; #endif bool m_in_render; -#if !ENABLE_WARNING_TEXTURE_REMOVAL - WarningTexture m_warning_texture; -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL wxTimer m_timer; LayersEditing m_layers_editing; Mouse m_mouse; @@ -814,9 +770,6 @@ private: #endif // ENABLE_RENDER_SELECTION_CENTER void _check_and_update_toolbar_icon_scale() const; void _render_overlays() const; -#if !ENABLE_WARNING_TEXTURE_REMOVAL - void _render_warning_texture() const; -#endif // !ENABLE_WARNING_TEXTURE_REMOVAL void _render_volumes_for_picking() const; void _render_current_gizmo() const; void _render_gizmos_overlay() const; @@ -869,17 +822,10 @@ private: void _load_sla_shells(); void _update_toolpath_volumes_outside_state(); void _update_sla_shells_outside_state(); -#if ENABLE_WARNING_TEXTURE_REMOVAL void _set_warning_notification_if_needed(EWarning warning); // generates a warning notification containing the given message void _set_warning_notification(EWarning warning, bool state); -#else - void _show_warning_texture_if_needed(WarningTexture::Warning warning); - - // generates a warning texture containing the given message - void _set_warning_texture(WarningTexture::Warning warning, bool state); -#endif // ENABLE_WARNING_TEXTURE_REMOVAL bool _is_any_volume_outside() const; From b87c03fc0937b27431833b4de0ebb013d9b49fa8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 May 2021 17:57:17 +0200 Subject: [PATCH 66/79] Linux specific: Fixed ObjectDataViewModel::GetColumnType() When "string" type was returned, strange editing TextControl was appeared. + Added check of the selection for ObjectList::toggle_printable_state() function --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 ++ src/slic3r/GUI/ObjectDataViewModel.cpp | 9 +++++++++ src/slic3r/GUI/ObjectDataViewModel.hpp | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 28053971f..c969b95da 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3909,6 +3909,8 @@ void ObjectList::toggle_printable_state() { wxDataViewItemArray sels; GetSelections(sels); + if (sels.IsEmpty()) + return; wxDataViewItem frst_item = sels[0]; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 49c75f9f2..297d8e3c9 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1180,6 +1180,15 @@ int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const return atoi(node->m_extruder.c_str()); } +wxString ObjectDataViewModel::GetColumnType(unsigned int col) const +{ + if (col == colName || col == colExtruder) + return wxT("DataViewBitmapText"); + if (col == colPrint || col == colEditing) + return wxT("DataViewBitmap"); + return wxT("string"); +} + void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const { wxASSERT(item.IsOk()); diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 1dd41bb10..93e092fc2 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -316,7 +316,7 @@ public: // helper methods to change the model unsigned int GetColumnCount() const override { return 3;} - wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } + wxString GetColumnType(unsigned int col) const override; void GetValue( wxVariant &variant, const wxDataViewItem &item, From d701b24bc0f2efaec8e64855531134927dc4aff4 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 28 Apr 2021 15:32:43 +0200 Subject: [PATCH 67/79] Fix of crash when notification text + hypertext wont fit line length --- src/slic3r/GUI/NotificationManager.cpp | 98 +++++++++++++++----------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 7f61ad7f3..489b72293 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -254,7 +254,7 @@ void NotificationManager::PopNotification::count_spaces() void NotificationManager::PopNotification::count_lines() { - std::string text = m_text1 + " " + m_hypertext; + std::string text = m_text1; size_t last_end = 0; m_lines_count = 0; @@ -302,6 +302,14 @@ void NotificationManager::PopNotification::count_lines() } m_lines_count++; } + // hypertext calculation + if (!m_hypertext.empty()) { + int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0; + if (ImGui::CalcTextSize((text.substr(prev_end, last_end - prev_end) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) { + m_endlines.push_back(last_end); + m_lines_count++; + } + } } void NotificationManager::PopNotification::init() @@ -312,7 +320,7 @@ void NotificationManager::PopNotification::init() count_spaces(); count_lines(); - + if (m_lines_count == 3) m_multiline = true; m_notification_start = GLCanvas3D::timestamp_now(); @@ -342,39 +350,46 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons int last_end = 0; float starting_y = m_line_height/2; float shift_y = m_line_height; + std::string line; + for (size_t i = 0; i < m_lines_count; i++) { - std::string line = m_text1.substr(last_end , m_endlines[i] - last_end); - if(i < m_lines_count - 1) - last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(starting_y + i * shift_y); - imgui.text(line.c_str()); + if (m_text1.size() > m_endlines[i]) { + line = m_text1.substr(last_end, m_endlines[i] - last_end); + if (i < m_lines_count - 1) + last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + imgui.text(line.c_str()); + } } //hyperlink text - if (!m_hypertext.empty()) - { - render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(m_endlines[m_lines_count - 2] + 1, m_endlines[m_lines_count - 1] - m_endlines[m_lines_count - 2] - 1).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext); + if (!m_hypertext.empty()) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext); } } else { // line1 - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); - imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + if (m_text1.size() >= m_endlines[0]) { + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + } // line2 - std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); - if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) - { - line = line.substr(0, line.length() - 6); - line += ".."; - }else - line += " "; - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2); - imgui.text(line.c_str()); - // "More" hypertext - render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true); + if (m_text1.size() > m_endlines[0]) { + std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) { + line = line.substr(0, line.length() - 6); + line += ".."; + } else + line += " "; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2); + imgui.text(line.c_str()); + // "More" hypertext + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true); + } + } } else { //text 1 @@ -382,9 +397,11 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons float cursor_x = x_offset; if(m_lines_count > 1) { // line1 - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); - imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + if (m_text1.length() >= m_endlines[0]) { + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + } // line2 if (m_text1.length() > m_endlines[0]) { std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); @@ -401,8 +418,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x; } //hyperlink text - if (!m_hypertext.empty()) - { + if (!m_hypertext.empty()) { render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext); } @@ -712,15 +728,17 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper& float starting_y = m_line_height / 2;//10; float shift_y = m_line_height;// -m_line_height / 20; for (size_t i = 0; i < m_lines_count; i++) { - std::string line = m_text1.substr(last_end, m_endlines[i] - last_end); - if (i < m_lines_count - 1) - last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(starting_y + i * shift_y); - imgui.text(line.c_str()); - //hyperlink text - if ( i == 0 ) { - render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(0, last_end).c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder.")); + if (m_text1.size() >= m_endlines[i]) { + std::string line = m_text1.substr(last_end, m_endlines[i] - last_end); + if (i < m_lines_count - 1) + last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + imgui.text(line.c_str()); + //hyperlink text + if ( i == 0 ) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder.")); + } } } From fabaee10a8fca9676801f2892be382ab4cbce93e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 7 May 2021 16:30:04 +0200 Subject: [PATCH 68/79] Additional controls in NotificationManager --- src/slic3r/GUI/NotificationManager.cpp | 41 ++++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 489b72293..1b29b7f76 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -353,12 +353,14 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons std::string line; for (size_t i = 0; i < m_lines_count; i++) { - if (m_text1.size() > m_endlines[i]) { + line.clear(); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) { line = m_text1.substr(last_end, m_endlines[i] - last_end); - if (i < m_lines_count - 1) - last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(starting_y + i * shift_y); + last_end = m_endlines[i]; + if (m_text1.size() > m_endlines[i]) + last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); imgui.text(line.c_str()); } } @@ -376,20 +378,20 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); } // line2 - if (m_text1.size() > m_endlines[0]) { - std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + std::string line; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2); + if (m_text1.size() >= m_endlines[1]) { + line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) { line = line.substr(0, line.length() - 6); line += ".."; } else line += " "; - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2); imgui.text(line.c_str()); - // "More" hypertext - render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true); } - + // "More" hypertext + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true); } } else { //text 1 @@ -397,17 +399,17 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons float cursor_x = x_offset; if(m_lines_count > 1) { // line1 - if (m_text1.length() >= m_endlines[0]) { + if (m_text1.length() >= m_endlines[0]) { // could be equal than substr takes whole string ImGui::SetCursorPosX(x_offset); ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); } // line2 - if (m_text1.length() > m_endlines[0]) { + ImGui::SetCursorPosX(x_offset); + cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; + ImGui::SetCursorPosY(cursor_y); + if (m_text1.length() > m_endlines[0]) { // must be greater otherwise theres nothing to show and m_text1[m_endlines[0]] is beyond last letter std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); - cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(cursor_y); imgui.text(line.c_str()); cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x; } @@ -730,8 +732,9 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper& for (size_t i = 0; i < m_lines_count; i++) { if (m_text1.size() >= m_endlines[i]) { std::string line = m_text1.substr(last_end, m_endlines[i] - last_end); - if (i < m_lines_count - 1) - last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + last_end = m_endlines[i]; + if (m_text1.size() > m_endlines[i]) + last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); ImGui::SetCursorPosX(x_offset); ImGui::SetCursorPosY(starting_y + i * shift_y); imgui.text(line.c_str()); From 13b0757b8b4131bebaf58171ab21892ceccc80de Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 May 2021 10:28:04 +0200 Subject: [PATCH 69/79] Tech ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GCodeViewer.cpp | 120 --------------------------------- src/slic3r/GUI/GCodeViewer.hpp | 24 ------- 3 files changed, 146 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a8cdbd77d..e00af1851 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -41,8 +41,6 @@ //==================== #define ENABLE_2_4_0_ALPHA0 1 -// Enable rendering only starting and final caps for toolpaths -#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_2_4_0_ALPHA0) // Enable reload from disk command for 3mf files #define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0) // Enable showing gcode line numbers in preview horizontal slider diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 7ccf10795..3287a99e9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -185,7 +185,6 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con return ret; } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS GCodeViewer::SequentialRangeCap::~SequentialRangeCap() { if (ibo > 0) glsafe(::glDeleteBuffers(1, &ibo)); @@ -200,7 +199,6 @@ void GCodeViewer::SequentialRangeCap::reset() { vbo = 0; color = { 0.0f, 0.0f, 0.0f }; } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS void GCodeViewer::SequentialView::Marker::init() { @@ -1241,13 +1239,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position }; }; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS auto add_indices_as_solid = [&](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, const GCodeProcessor::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { -#else - auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, - size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS static Vec3f prev_dir; static Vec3f prev_up; static float sq_prev_length; @@ -1260,7 +1253,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) store_triangle(indices, id, id, id); store_triangle(indices, id, id, id); }; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS auto convert_vertices_offset = [](size_t vbuffer_size, const std::array& v_offsets) { std::array ret = { static_cast(static_cast(vbuffer_size) + v_offsets[0]), @@ -1292,32 +1284,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) store_triangle(indices, v_offsets[4], v_offsets[6], v_offsets[7]); store_triangle(indices, v_offsets[4], v_offsets[5], v_offsets[6]); }; -#else - auto append_stem_triangles = [&](IndexBuffer& indices, size_t vbuffer_size, const std::array& v_offsets) { - std::array v_ids; - for (size_t i = 0; i < v_ids.size(); ++i) { - v_ids[i] = static_cast(static_cast(vbuffer_size) + v_offsets[i]); - } - - // triangles starting cap - store_triangle(indices, v_ids[0], v_ids[2], v_ids[1]); - store_triangle(indices, v_ids[0], v_ids[3], v_ids[2]); - - // triangles sides - store_triangle(indices, v_ids[0], v_ids[1], v_ids[4]); - store_triangle(indices, v_ids[1], v_ids[5], v_ids[4]); - store_triangle(indices, v_ids[1], v_ids[2], v_ids[5]); - store_triangle(indices, v_ids[2], v_ids[6], v_ids[5]); - store_triangle(indices, v_ids[2], v_ids[3], v_ids[6]); - store_triangle(indices, v_ids[3], v_ids[7], v_ids[6]); - store_triangle(indices, v_ids[3], v_ids[0], v_ids[7]); - store_triangle(indices, v_ids[0], v_ids[4], v_ids[7]); - - // triangles ending cap - store_triangle(indices, v_ids[4], v_ids[6], v_ids[7]); - store_triangle(indices, v_ids[4], v_ids[5], v_ids[6]); - }; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1); @@ -1331,30 +1297,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Vec3f up = right.cross(dir); float sq_length = (curr.position - prev.position).squaredNorm(); -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS const std::array first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 }); const std::array non_first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 }); bool is_first_segment = (last_path.vertices_count() == 1); if (is_first_segment || vbuffer_size == 0) { -#else - if (last_path.vertices_count() == 1 || vbuffer_size == 0) { -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // 1st segment or restart into a new vertex buffer // =============================================== -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (is_first_segment) // starting cap triangles append_starting_cap_triangles(indices, first_seg_v_offsets); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // dummy triangles outer corner cap append_dummy_cap(indices, vbuffer_size); // stem triangles -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS append_stem_triangles(indices, first_seg_v_offsets); -#else - append_stem_triangles(indices, vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 }); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS vbuffer_size += 8; } @@ -1408,20 +1364,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } // stem triangles -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS append_stem_triangles(indices, non_first_seg_v_offsets); -#else - append_stem_triangles(indices, vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 }); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS vbuffer_size += 6; } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (next != nullptr && (curr.type != next->type || !last_path.matches(*next))) // ending cap triangles append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; prev_dir = dir; @@ -1761,11 +1711,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) continue; const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS const GCodeProcessor::MoveVertex* next = nullptr; if (i < m_moves_count - 1) next = &gcode_result.moves[i + 1]; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS ++progress_count; if (progress_dialog != nullptr && progress_count % progress_threshold == 0) { @@ -1789,11 +1737,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // if adding the indices for the current segment exceeds the threshold size of the current index buffer // create another index buffer -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.max_indices_per_segment_size_bytes()) { -#else - if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) { -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS i_multibuffer.push_back(IndexBuffer()); vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) { @@ -1832,11 +1776,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) break; } case TBuffer::ERenderPrimitiveType::Triangle: { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, i); -#else - add_indices_as_solid(prev, curr, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, i); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS break; } } @@ -1888,7 +1828,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) auto update_segments_count = [&](EMoveType type, int64_t& count) { unsigned int id = buffer_id(type); const MultiIndexBuffer& buffers = indices[id]; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS int64_t indices_count = 0; for (const IndexBuffer& buffer : buffers) { indices_count += buffer.size(); @@ -1898,11 +1837,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) indices_count -= static_cast(12 * t_buffer.paths.size()); // remove the starting + ending caps = 4 triangles count += indices_count / t_buffer.indices_per_segment(); -#else - for (const IndexBuffer& buffer : buffers) { - count += buffer.size() / m_buffers[id].indices_per_segment(); - } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS }; update_segments_count(EMoveType::Travel, m_statistics.travel_segments_count); @@ -2119,11 +2053,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (!keep_sequential_current_last) sequential_view->current.last = m_moves_count; // first pass: collect visible paths and update sequential view data -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS std::vector> paths; -#else - std::vector> paths; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer& buffer = const_cast(m_buffers[b]); // reset render paths @@ -2146,11 +2076,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool // store valid path for (size_t j = 0; j < path.sub_paths.size(); ++j) { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS paths.push_back({ static_cast(b), path.sub_paths[j].first.b_id, static_cast(i), static_cast(j) }); -#else - paths.push_back({ &buffer, path.sub_paths[j].first.b_id, static_cast(i), static_cast(j) }); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } global_endpoints.first = std::min(global_endpoints.first, path.sub_paths.front().first.s_id); @@ -2190,13 +2116,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool offset = 2 * offset - 1; else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { unsigned int indices_count = buffer.indices_per_segment(); -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS offset = indices_count * (offset - 1) + (indices_count - 2); if (sub_path_id == 0) offset += 6; // add 2 triangles for starting cap -#else - offset = indices_count * (offset - 1) + (indices_count - 6); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } } offset += static_cast(sub_path.first.i_id); @@ -2225,14 +2147,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool // second pass: filter paths by sequential data and collect them by color RenderPath* render_path = nullptr; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS for (const auto& [tbuffer_id, ibuffer_id, path_id, sub_path_id] : paths) { TBuffer& buffer = const_cast(m_buffers[tbuffer_id]); const Path& path = buffer.paths[path_id]; -#else - for (const auto& [buffer, ibuffer_id, path_id, sub_path_id] : paths) { - const Path& path = buffer->paths[path_id]; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id]; if (m_sequential_view.current.last < sub_path.first.s_id || sub_path.last.s_id < m_sequential_view.current.first) continue; @@ -2271,7 +2188,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool default: { color = { 0.0f, 0.0f, 0.0f }; break; } } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS RenderPath key{ tbuffer_id, color, static_cast(ibuffer_id), path_id }; if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) render_path = const_cast(&(*buffer.render_paths.emplace(key).first)); @@ -2279,40 +2195,22 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool unsigned int delta_1st = 0; if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id) delta_1st = static_cast(m_sequential_view.current.first - sub_path.first.s_id); -#else - RenderPath key{ color, static_cast(ibuffer_id), path_id }; - if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) - render_path = const_cast(&(*buffer->render_paths.emplace(key).first)); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS unsigned int size_in_indices = 0; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS switch (buffer.render_primitive_type) -#else - switch (buffer->render_primitive_type) -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS { case TBuffer::ERenderPrimitiveType::Point: { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS size_in_indices = buffer.indices_per_segment(); -#else - size_in_indices = buffer->indices_per_segment(); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS break; } case TBuffer::ERenderPrimitiveType::Line: case TBuffer::ERenderPrimitiveType::Triangle: { unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id); -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS size_in_indices = buffer.indices_per_segment() * segments_count; -#else - size_in_indices = buffer->indices_per_segment() * segments_count; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS break; } } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (size_in_indices == 0) continue; @@ -2324,17 +2222,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (delta_1st > 0) size_in_indices -= 6; // remove 2 triangles for corner cap } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS render_path->sizes.push_back(size_in_indices); -#if !ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS - unsigned int delta_1st = 0; - if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id) - delta_1st = m_sequential_view.current.first - sub_path.first.s_id; -#endif // !ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS - -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { delta_1st *= buffer.indices_per_segment(); if (delta_1st > 0) { @@ -2343,10 +2233,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool delta_1st += 6; // skip 2 triangles for starting cap } } -#else - if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) - delta_1st *= buffer->indices_per_segment(); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS render_path->offsets.push_back(static_cast((sub_path.first.i_id + delta_1st) * sizeof(IBufferType))); @@ -2365,7 +2251,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool sequential_view->endpoints = top_layer_only ? top_layer_endpoints : global_endpoints; sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, sequential_view->endpoints.first, sequential_view->endpoints.last) : sequential_view->endpoints.first; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // updates sequential range caps std::array* sequential_range_caps = const_cast*>(&m_sequential_range_caps); (*sequential_range_caps)[0].reset(); @@ -2476,7 +2361,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool break; } } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS wxGetApp().plater()->enable_preview_moves_slider(!paths.empty()); @@ -2627,7 +2511,6 @@ void GCodeViewer::render_toolpaths() const } } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS auto render_sequential_range_cap = [set_uniform_color](const SequentialRangeCap& cap) { GLShaderProgram* shader = wxGetApp().get_shader(cap.buffer->shader.c_str()); if (shader != nullptr) { @@ -2666,7 +2549,6 @@ void GCodeViewer::render_toolpaths() const if (m_sequential_range_caps[i].is_renderable()) render_sequential_range_cap(m_sequential_range_caps[i]); } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } void GCodeViewer::render_shells() const @@ -3642,9 +3524,7 @@ void GCodeViewer::render_statistics() const add_counter(std::string("Multi GL_POINTS:"), m_statistics.gl_multi_points_calls_count); add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count); add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } if (ImGui::CollapsingHeader("CPU memory")) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3386e314e..931f01b68 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -180,10 +180,8 @@ class GCodeViewer // Used to batch the indices needed to render the paths struct RenderPath { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // Index of the parent tbuffer unsigned char tbuffer_id; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // Render path property Color color; // Index of the buffer in TBuffer::indices @@ -193,7 +191,6 @@ class GCodeViewer unsigned int path_id; std::vector sizes; std::vector offsets; // use size_t because we need an unsigned integer whose size matches pointer's size (used in the call glMultiDrawElements()) -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS bool contains(size_t offset) const { for (size_t i = 0; i < offsets.size(); ++i) { if (offsets[i] <= offset && offset <= offsets[i] + static_cast(sizes[i] * sizeof(IBufferType))) @@ -201,7 +198,6 @@ class GCodeViewer } return false; } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS }; // // for unordered_set implementation of render_paths // struct RenderPathPropertyHash { @@ -213,10 +209,8 @@ class GCodeViewer // }; struct RenderPathPropertyLower { bool operator() (const RenderPath &l, const RenderPath &r) const { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS if (l.tbuffer_id < r.tbuffer_id) return true; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS for (int i = 0; i < 3; ++i) { if (l.color[i] < r.color[i]) return true; @@ -228,11 +222,7 @@ class GCodeViewer }; struct RenderPathPropertyEqual { bool operator() (const RenderPath &l, const RenderPath &r) const { -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS return l.tbuffer_id == r.tbuffer_id && l.ibuffer_id == r.ibuffer_id && l.color == r.color; -#else - return l.color == r.color && l.ibuffer_id == r.ibuffer_id; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } }; @@ -281,16 +271,11 @@ class GCodeViewer { case ERenderPrimitiveType::Point: { return 1; } case ERenderPrimitiveType::Line: { return 2; } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles -#else - case ERenderPrimitiveType::Triangle: { return 42; } // 3 indices x 14 triangles -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS default: { return 0; } } } size_t indices_per_segment_size_bytes() const { return static_cast(indices_per_segment() * sizeof(IBufferType)); } -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS unsigned int max_indices_per_segment() const { switch (render_primitive_type) { @@ -301,7 +286,6 @@ class GCodeViewer } } size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); } -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS bool has_data() const { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; @@ -422,7 +406,6 @@ class GCodeViewer } }; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // used to render the toolpath caps of the current sequential range // (i.e. when sliding on the horizontal slider) struct SequentialRangeCap @@ -437,7 +420,6 @@ class GCodeViewer void reset(); size_t indices_count() const { return 6; } }; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #if ENABLE_GCODE_VIEWER_STATISTICS struct Statistics @@ -454,9 +436,7 @@ class GCodeViewer int64_t gl_multi_points_calls_count{ 0 }; int64_t gl_multi_lines_calls_count{ 0 }; int64_t gl_multi_triangles_calls_count{ 0 }; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS int64_t gl_triangles_calls_count{ 0 }; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS // memory int64_t results_size{ 0 }; int64_t total_vertices_gpu_size{ 0 }; @@ -493,9 +473,7 @@ class GCodeViewer gl_multi_points_calls_count = 0; gl_multi_lines_calls_count = 0; gl_multi_triangles_calls_count = 0; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS gl_triangles_calls_count = 0; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS } void reset_sizes() { @@ -649,9 +627,7 @@ private: #endif // ENABLE_GCODE_VIEWER_STATISTICS std::array m_detected_point_sizes = { 0.0f, 0.0f }; GCodeProcessor::Result::SettingsIds m_settings_ids; -#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS std::array m_sequential_range_caps; -#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS public: GCodeViewer(); From ab886e037bf577db28ec98828b6282dc57431f2d Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko Date: Tue, 11 May 2021 11:02:12 +0200 Subject: [PATCH 70/79] Implementation for #6216 * Implementation for #6216 - Make number keys select extruder when object treeview has focus + deleted unused extruder_selection() + Fixed notification after splitting of the solid object * Follow up https://github.com/prusa3d/PrusaSlicer/commit/85a10268b9eabfa8aac4c7b1361136b40b75f9f0 - OSX implementation + Added shortcuts description to the "Keyboard Shortcuts" dialog * Workaround to use "+/-" and numbers shortcuts on Linux + Fixed build on Linux * OSX specific: fixed a work of keyboard accelerators from numbers on NumPad keyboard * KBShortcutsDialog: fixed shortcuts for "Preferences" and "Show/Hide 3Dconnexion devices settings dialog, if enabled" under osx and "Set Printable/Unprintable" and "Set extruder" under Linux + OSX specific: Added minimize of the application on "Cmd+M" * Hot-fix for https://github.com/prusa3d/PrusaSlicer/commit/6efeb9d6b433f49aaca8ee42ed02d824c452eb8e * Removed Linux specific workaround --- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++++ src/slic3r/GUI/GUI_ObjectList.cpp | 88 +++++++++++++++------------- src/slic3r/GUI/GUI_ObjectList.hpp | 1 - src/slic3r/GUI/KBShortcutsDialog.cpp | 23 ++++++++ src/slic3r/GUI/Plater.cpp | 5 +- src/slic3r/GUI/Search.cpp | 6 ++ src/slic3r/GUI/Search.hpp | 2 +- 7 files changed, 89 insertions(+), 46 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1eb52e92e..e77a6a324 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2205,9 +2205,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #ifdef _WIN32 if (wxGetApp().app_config->get("use_legacy_3DConnexion") == "1") { #endif //_WIN32 +#ifdef __APPLE__ + // On OSX use Cmd+Shift+M to "Show/Hide 3Dconnexion devices settings dialog" + if ((evt.GetModifiers() & shiftMask) != 0) { +#endif // __APPLE__ Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); controller.show_settings_dialog(!controller.is_settings_dialog_shown()); m_dirty = true; +#ifdef __APPLE__ + } + else + // and Cmd+M to minimize application + wxGetApp().mainframe->Iconize(); +#endif // __APPLE__ #ifdef _WIN32 } #endif //_WIN32 diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c969b95da..f27623b09 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -146,18 +146,28 @@ ObjectList::ObjectList(wxWindow* parent) : // Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this); { // Accelerators - wxAcceleratorEntry entries[10]; - entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY); - entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT); - entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE); - entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL); - entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO); - entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO); + wxAcceleratorEntry entries[33]; + entries[0].Set(wxACCEL_CTRL, (int)'C', wxID_COPY); + entries[1].Set(wxACCEL_CTRL, (int)'X', wxID_CUT); + entries[2].Set(wxACCEL_CTRL, (int)'V', wxID_PASTE); + entries[3].Set(wxACCEL_CTRL, (int)'A', wxID_SELECTALL); + entries[4].Set(wxACCEL_CTRL, (int)'Z', wxID_UNDO); + entries[5].Set(wxACCEL_CTRL, (int)'Y', wxID_REDO); entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE); - entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); - entries[8].Set(wxACCEL_NORMAL, int('+'), wxID_ADD); - entries[9].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE); - wxAcceleratorTable accel(10, entries); + entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); + entries[8].Set(wxACCEL_NORMAL, int('+'), wxID_ADD); + entries[9].Set(wxACCEL_NORMAL, WXK_NUMPAD_ADD, wxID_ADD); + entries[10].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE); + entries[11].Set(wxACCEL_NORMAL, WXK_NUMPAD_SUBTRACT, wxID_REMOVE); + entries[12].Set(wxACCEL_NORMAL, int('p'), wxID_PRINT); + + int numbers_cnt = 1; + for (auto char_number : { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }) { + entries[12 + numbers_cnt].Set(wxACCEL_NORMAL, int(char_number), wxID_LAST + numbers_cnt); + entries[22 + numbers_cnt].Set(wxACCEL_NORMAL, WXK_NUMPAD0 + numbers_cnt - 1, wxID_LAST + numbers_cnt); + numbers_cnt++; + } + wxAcceleratorTable accel(33, entries); SetAcceleratorTable(accel); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY); @@ -168,6 +178,13 @@ ObjectList::ObjectList(wxWindow* parent) : this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->increase_instances(); }, wxID_ADD); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->decrease_instances(); }, wxID_REMOVE); + this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->toggle_printable_state(); }, wxID_PRINT); + + for (int i = 0; i < 10; i++) + this->Bind(wxEVT_MENU, [this, i](wxCommandEvent &evt) { + if (extruders_count() > 1 && i <= extruders_count()) + this->set_extruder_for_selected_items(i); + }, wxID_LAST+i+1); } #else //__WXOSX__ Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX @@ -1034,6 +1051,20 @@ void ObjectList::key_event(wxKeyEvent& event) increase_instances(); else if (event.GetUnicodeKey() == '-') decrease_instances(); + else if (event.GetUnicodeKey() == 'p') + toggle_printable_state(); + else if (extruders_count() > 1) { + std::vector numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + wxChar key_char = event.GetUnicodeKey(); + if (std::find(numbers.begin(), numbers.end(), key_char) != numbers.end()) { + long extruder_number; + if (wxNumberFormatter::FromString(wxString(key_char), &extruder_number) && + extruders_count() >= extruder_number) + set_extruder_for_selected_items(int(extruder_number)); + } + else + event.Skip(); + } else event.Skip(); } @@ -3790,33 +3821,6 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) plater->set_current_canvas_as_dirty(); } -void ObjectList::extruder_selection() -{ - wxArrayString choices; - choices.Add(_(L("default"))); - for (int i = 1; i <= extruders_count(); ++i) - choices.Add(wxString::Format("%d", i)); - - const wxString& selected_extruder = wxGetSingleChoice(_(L("Select extruder number:")), - _(L("This extruder will be set for selected items")), - choices, 0, this); - if (selected_extruder.IsEmpty()) - return; - - const int extruder_num = selected_extruder == _(L("default")) ? 0 : atoi(selected_extruder.c_str()); - -// /* Another variant for an extruder selection */ -// extruder_num = wxGetNumberFromUser(_(L("Attention!!! \n" -// "It's a possibile to set an extruder number \n" -// "for whole Object(s) and/or object Part(s), \n" -// "not for an Instance. ")), -// _(L("Enter extruder number:")), -// _(L("This extruder will be set for selected items")), -// 1, 1, 5, this); - - set_extruder_for_selected_items(extruder_num); -} - void ObjectList::set_extruder_for_selected_items(const int extruder) const { wxDataViewItemArray sels; @@ -3923,10 +3927,10 @@ void ObjectList::toggle_printable_state() int inst_idx = type == itObject ? 0 : m_objects_model->GetInstanceIdByItem(frst_item); bool printable = !object(obj_idx)->instances[inst_idx]->printable; - const wxString snapshot_text = sels.Count() > 1 ? (printable ? _L("Set Printable group") : _L("Set Unprintable group")) : - object(obj_idx)->instances.size() == 1 ? from_u8((boost::format("%1% %2%") - % (printable ? _L("Set Printable") : _L("Set Unprintable")) - % object(obj_idx)->name).str()) : + const wxString snapshot_text = sels.Count() > 1 ? + (printable ? _L("Set Printable group") : _L("Set Unprintable group")) : + object(obj_idx)->instances.size() == 1 ? + format_wxstr("%1% %2%", (printable ? _L("Set Printable") : _L("Set Unprintable")), from_u8(object(obj_idx)->name)) : (printable ? _L("Set Printable Instance") : _L("Set Unprintable Instance")); take_snapshot(snapshot_text); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 8bcfec11c..b378b00af 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -390,7 +390,6 @@ private: void OnEditingStarted(wxDataViewEvent &event); #endif /* __WXMSW__ */ void OnEditingDone(wxDataViewEvent &event); - void extruder_selection(); }; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 1c451672c..d766ff5fd 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -109,7 +109,11 @@ void KBShortcutsDialog::fill_shortcuts() { "0-6", L("Camera view") }, { "E", L("Show/Hide object/instance labels") }, // Configuration +#ifdef __APPLE__ + { ctrl + ",", L("Preferences") }, +#else { ctrl + "P", L("Preferences") }, +#endif // Help { "?", L("Show keyboard shortcuts list") } }; @@ -149,8 +153,13 @@ void KBShortcutsDialog::fill_shortcuts() { "Shift+Tab", L("Collapse/Expand the sidebar") }, #ifdef _WIN32 { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog, if enabled") }, +#else +#ifdef __APPLE__ + { ctrl + "Shift+M", L("Show/Hide 3Dconnexion devices settings dialog") }, + { ctrl + "M", L("Minimize application") }, #else { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") }, +#endif // __APPLE__ #endif // _WIN32 #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. @@ -171,6 +180,20 @@ void KBShortcutsDialog::fill_shortcuts() }; m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts }); + + Shortcuts object_list_shortcuts = { +#if defined (__linux__) + { alt + "P", L("Set selected items as Ptrintable/Unprintable") }, + { alt + "0", L("Set default extruder for the selected items") }, + { alt + "1-9", L("Set extruder number for the selected items") }, +#else + { "P", L("Set selected items as Ptrintable/Unprintable") }, + { "0", L("Set default extruder for the selected items") }, + { "1-9", L("Set extruder number for the selected items") }, +#endif + }; + + m_full_shortcuts.push_back({ { _L("Objects List"), "" }, object_list_shortcuts }); } else { Shortcuts commands_shortcuts = { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 24d32b116..f7fbb6395 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2797,10 +2797,11 @@ void Plater::priv::split_object() Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one solid part.")); else { - if (current_model_object->volumes.size() != new_objects.size()) + // If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted + if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size()) notification_manager->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotification, - _u8L("All non-solid parts (modifiers) was deleted")); + _u8L("All non-solid parts (modifiers) were deleted")); Plater::TakeSnapshot snapshot(q, _L("Split to Objects")); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 9ff4e82a1..21373cc89 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -7,6 +7,7 @@ #include #include "wx/dataview.h" +#include "wx/numformatter.h" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" @@ -45,6 +46,11 @@ static char marker_by_type(Preset::Type type, PrinterTechnology pt) } } +std::string Option::opt_key() const +{ + return boost::nowide::narrow(key).substr(2); +} + void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const { *label_ = marked_label.c_str(); diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index a9ced75dd..a942a89f8 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -53,7 +53,7 @@ struct Option { std::wstring category; std::wstring category_local; - std::string opt_key() const { return boost::nowide::narrow(key).substr(2); } + std::string opt_key() const; }; struct FoundOption { From b875fd2755651e5affa0c6ab1b7ed7cfcc884af3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 May 2021 15:01:33 +0200 Subject: [PATCH 71/79] Fixed project dirty state after changing language --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9ff4821c2..67feed4f3 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -64,7 +64,7 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con return true; else if (snapshot.name == _utf8("Reset Project")) return true; - else if (boost::starts_with(snapshot.name, _utf8("Load Project:"))) + else if (boost::starts_with(snapshot.name, _utf8("Load Project"))) return true; else if (boost::starts_with(snapshot.name, _utf8("Selection"))) return true; @@ -366,7 +366,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || - boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) + boost::starts_with(active_snapshot->name, _utf8("Load Project"))) return; if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { From de1d36cc9aa3890208b2e9bfcc30606972539959 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 May 2021 18:02:18 +0200 Subject: [PATCH 72/79] Probably fix for #6270 - Segfault during startup in prusa-slicer-git 2.3.0.r24.gd06aa6069-1 --- src/slic3r/GUI/MainFrame.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8c54e10f8..e7065cb13 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -544,9 +544,12 @@ void MainFrame::init_tabpanel() m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) { #if ENABLE_VALIDATE_CUSTOM_GCODE - Tab* old_tab = dynamic_cast(m_tabpanel->GetPage(e.GetOldSelection())); - if (old_tab) - old_tab->validate_custom_gcodes(); + if (int old_selection = e.GetOldSelection(); + old_selection != wxNOT_FOUND && old_selection < m_tabpanel->GetPageCount()) { + Tab* old_tab = dynamic_cast(m_tabpanel->GetPage(old_selection)); + if (old_tab) + old_tab->validate_custom_gcodes(); + } #endif // ENABLE_VALIDATE_CUSTOM_GCODE wxWindow* panel = m_tabpanel->GetCurrentPage(); From ebe762f17710eeb33b6ca91b271791379ae8f39f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 May 2021 11:21:18 +0200 Subject: [PATCH 73/79] Add estimated printing time for first layer in legend --- src/slic3r/GUI/GCodeViewer.cpp | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3287a99e9..ce4c18a53 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3395,27 +3395,27 @@ void GCodeViewer::render_legend() const ImGui::Spacing(); #endif // ENABLE_SCROLLABLE_LEGEND ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Separator, { 1.0f, 1.0f, 1.0f, 1.0f }); - ImGui::Separator(); - ImGui::PopStyleColor(); - ImGui::Spacing(); - - ImGui::AlignTextToFramePadding(); + std::string time_title = _u8L("Estimated printing times"); switch (m_time_estimate_mode) { - case PrintEstimatedStatistics::ETimeMode::Normal: - { - imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Normal mode") + "]:"); - break; + case PrintEstimatedStatistics::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]:"; break; } + case PrintEstimatedStatistics::ETimeMode::Stealth: { time_title += " [" + _u8L("Stealth mode") + "]:"; break; } + default: { assert(false); break; } } - case PrintEstimatedStatistics::ETimeMode::Stealth: - { - imgui.text(_u8L("Estimated printing time") + " [" + _u8L("Stealth mode") + "]:"); - break; - } - default : { assert(false); break; } - } - ImGui::SameLine(); + + imgui.title(time_title); + + std::string first_str = _u8L("First layer"); + std::string total_str = _u8L("Total"); + + float max_len = 10.0f + ImGui::GetStyle().ItemSpacing.x + std::max(ImGui::CalcTextSize(first_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x); + + imgui.text(first_str + ":"); + ImGui::SameLine(max_len); + imgui.text(short_time(get_time_dhms(time_mode.layers_times.front()))); + + imgui.text(total_str + ":"); + ImGui::SameLine(max_len); imgui.text(short_time(get_time_dhms(time_mode.time))); auto show_mode_button = [this, &imgui](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { From 82da1f8fc122ad1ac9cd87d4bdaaa61a9dd11d8f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 May 2021 11:39:39 +0200 Subject: [PATCH 74/79] Code cleaning: Delete workaround code --- src/slic3r/GUI/KBShortcutsDialog.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index d766ff5fd..e72975629 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -182,15 +182,9 @@ void KBShortcutsDialog::fill_shortcuts() m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts }); Shortcuts object_list_shortcuts = { -#if defined (__linux__) - { alt + "P", L("Set selected items as Ptrintable/Unprintable") }, - { alt + "0", L("Set default extruder for the selected items") }, - { alt + "1-9", L("Set extruder number for the selected items") }, -#else { "P", L("Set selected items as Ptrintable/Unprintable") }, { "0", L("Set default extruder for the selected items") }, { "1-9", L("Set extruder number for the selected items") }, -#endif }; m_full_shortcuts.push_back({ { _L("Objects List"), "" }, object_list_shortcuts }); From 3006213d3b3f2e24d51f21db02451729a3998aca Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 May 2021 14:25:13 +0200 Subject: [PATCH 75/79] Removed obsolete member variable from Canvas3D::Slope --- src/slic3r/GUI/GLCanvas3D.cpp | 2 -- src/slic3r/GUI/GLCanvas3D.hpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e77a6a324..3829ab0d1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -787,8 +787,6 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } -float GLCanvas3D::Slope::s_window_width; - wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f51f53f62..46de9b12e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -357,7 +357,6 @@ class GLCanvas3D { bool m_enabled{ false }; GLVolumeCollection& m_volumes; - static float s_window_width; public: Slope(GLVolumeCollection& volumes) : m_volumes(volumes) {} @@ -368,7 +367,6 @@ class GLCanvas3D void set_normal_angle(float angle_in_deg) const { m_volumes.set_slope_normal_z(-::cos(Geometry::deg2rad(90.0f - angle_in_deg))); } - static float get_window_width() { return s_window_width; }; }; class RenderTimer : public wxTimer { From 4be1d622591a8b7b68b467abeb7787a7b63cf533 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 May 2021 15:53:58 +0200 Subject: [PATCH 76/79] Fixed update of the "Supports" in frequently used parameters when Vase mode is selected --- src/slic3r/GUI/ConfigManipulation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index d55758538..c5633b8fa 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -91,6 +91,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); + bool support = true; if (!is_global_config || answer == wxID_YES) { new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); @@ -100,13 +101,17 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true)); new_conf.set_key_value("thin_walls", new ConfigOptionBool(false)); fill_density = 0; + support = false; } else { new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false)); } apply(config, &new_conf); - if (cb_value_change) + if (cb_value_change) { cb_value_change("fill_density", fill_density); + if (!support) + cb_value_change("support_material", false); + } } if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") && From 707ce9d3d46777af1d484cd744fbf51e5101d025 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 12 May 2021 17:04:36 +0200 Subject: [PATCH 77/79] Added a missing include (gcc) --- src/slic3r/GUI/GUI_ObjectList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f27623b09..0b6235425 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -21,6 +21,7 @@ #include #include +#include #include "slic3r/Utils/FixModelByWin10.hpp" From 044634d7d1ab2571a5a409e99d000324d79f8a57 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 May 2021 09:47:44 +0200 Subject: [PATCH 78/79] Fixed "Extruder sequence", when extruder changes are per mm --- src/slic3r/GUI/DoubleSlider.cpp | 6 +++--- src/slic3r/GUI/DoubleSlider.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 655ef496b..a10acb2e3 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -326,10 +326,10 @@ double Control::get_double_value(const SelectedSlider& selection) return m_values[selection == ssLower ? m_lower_value : m_higher_value]; } -int Control::get_tick_from_value(double value) +int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) { std::vector::iterator it; - if (m_is_wipe_tower) + if (m_is_wipe_tower && !force_lower_bound) it = std::find_if(m_values.begin(), m_values.end(), [value](const double & val) { return fabs(value - val) <= epsilon(); }); else @@ -2395,7 +2395,7 @@ void Control::edit_extruder_sequence() extruder = 0; if (m_extruders_sequence.is_mm_intervals) { value += m_extruders_sequence.interval_by_mm; - tick = get_tick_from_value(value); + tick = get_tick_from_value(value, true); if (tick < 0) break; } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 439b03a50..400265885 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -322,7 +322,7 @@ private: wxSize get_size() const; void get_size(int* w, int* h) const; double get_double_value(const SelectedSlider& selection); - int get_tick_from_value(double value); + int get_tick_from_value(double value, bool force_lower_bound = false); wxString get_tooltip(int tick = -1); int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange); From d54548367a946f4c1c388a91fca61f1589533fa2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 13 May 2021 10:59:13 +0200 Subject: [PATCH 79/79] Fixed imgui out of synch with mouse after switching between preview and 3D view --- src/slic3r/GUI/GLCanvas3D.cpp | 8 -------- src/slic3r/GUI/GLCanvas3D.hpp | 4 ---- 2 files changed, 12 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3829ab0d1..ea3bec131 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4527,14 +4527,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) if (m_canvas == nullptr && m_context == nullptr) return; -#if ENABLE_SCROLLABLE_LEGEND - const std::array new_size = { w, h }; - if (m_old_size == new_size) - return; - - m_old_size = new_size; -#endif // ENABLE_SCROLLABLE_LEGEND - auto *imgui = wxGetApp().imgui(); imgui->set_display_size(static_cast(w), static_cast(h)); const float font_size = 1.5f * wxGetApp().em_unit(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 46de9b12e..a5c010d95 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -425,10 +425,6 @@ private: Model* m_model; BackgroundSlicingProcess *m_process; -#if ENABLE_SCROLLABLE_LEGEND - std::array m_old_size{ 0, 0 }; -#endif // ENABLE_SCROLLABLE_LEGEND - // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; bool m_initialized;