From 5320af6c138b084ea0b1b1a26a757b0b39524c2b Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Wed, 4 Dec 2019 21:12:26 +0100 Subject: [PATCH 01/28] Don't show a confirmation dialog when saving a preset --- src/slic3r/GUI/Tab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ee1750c37..bef177edc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3026,7 +3026,7 @@ void Tab::save_preset(std::string name /*= ""*/) show_error(this, _(L("Cannot overwrite an external profile."))); return; } - if (existing/* && name != preset.name*/) + if (existing && name != preset.name) { wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exist."))) % name).str()); msg_text += "\n" + _(L("Replace?")); From 688457b2d00f9ae2b5f07c5c6e49dcbf0dc1afa3 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Wed, 4 Dec 2019 21:13:42 +0100 Subject: [PATCH 02/28] Grammar fix --- resources/localization/PrusaSlicer.pot | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/localization/PrusaSlicer.pot b/resources/localization/PrusaSlicer.pot index 1b1e74cba..ee9b565c1 100644 --- a/resources/localization/PrusaSlicer.pot +++ b/resources/localization/PrusaSlicer.pot @@ -4926,7 +4926,7 @@ msgid "Cannot overwrite an external profile." msgstr "" #: src/slic3r/GUI/Tab.cpp:3031 -msgid "Preset with name \"%1%\" already exist." +msgid "Preset with name \"%1%\" already exists." msgstr "" #: src/slic3r/GUI/Tab.cpp:3032 diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bef177edc..563f8cc66 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3028,7 +3028,7 @@ void Tab::save_preset(std::string name /*= ""*/) } if (existing && name != preset.name) { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exist."))) % name).str()); + wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % name).str()); msg_text += "\n" + _(L("Replace?")); wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO); From 53980d074ebdac691d3fee88cd64d17246268cdd Mon Sep 17 00:00:00 2001 From: KyleMaas Date: Sun, 19 Jan 2020 11:13:42 -0500 Subject: [PATCH 03/28] Fix bug #2901 --- src/libslic3r/GCode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bd22c29e5..e28cc637f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -196,7 +196,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) if (gcodegen.config().standby_temperature_delta.value != 0) { // we assume that heating is always slower than cooling, so no need to block gcode += gcodegen.writer().set_temperature - (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false); + (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id()); } return gcode; @@ -205,7 +205,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) std::string OozePrevention::post_toolchange(GCode &gcodegen) { return (gcodegen.config().standby_temperature_delta.value != 0) ? - gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true) : + gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : std::string(); } From fa3d138e91021dabbf3710ceb77c833f34e879b9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 13:13:52 +0100 Subject: [PATCH 04/28] Fix of the previous commit: Fixed number of unit tests to be executed. --- t/custom_gcode.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 44f952318..1bb52b618 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 41; +use Test::More tests => 38; use strict; use warnings; From b08788cf96de313a2702ae0f3ef0298a8ade226e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 2 Jan 2020 11:42:48 +0100 Subject: [PATCH 05/28] typo at Unmounting successful message --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b388ad1d2..80a3646f0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5051,7 +5051,7 @@ void Plater::drive_ejected_callback() if (RemovableDriveManager::get_instance().get_did_eject()) { RemovableDriveManager::get_instance().set_did_eject(false); - wxString message = "Unmounting succesesful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; + wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; wxMessageBox(message); } p->show_action_buttons(false); From 7e97576e564a32ac00d9432ca5df71c503458f4e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 2 Jan 2020 16:30:28 +0100 Subject: [PATCH 06/28] button for exporting gcode to harddrive --- src/slic3r/GUI/AppConfig.cpp | 25 +++++++++- src/slic3r/GUI/AppConfig.hpp | 6 ++- src/slic3r/GUI/Plater.cpp | 60 +++++++++++++++--------- src/slic3r/GUI/Plater.hpp | 3 +- src/slic3r/GUI/RemovableDriveManager.cpp | 6 ++- src/slic3r/GUI/RemovableDriveManager.hpp | 3 +- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 12302a5dc..a410f3ad8 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -78,6 +78,9 @@ void AppConfig::set_defaults() if (get("remember_output_path").empty()) set("remember_output_path", "1"); + if (get("remember_output_path_removable").empty()) + set("remember_output_path_removable", "1"); + if (get("use_custom_toolbar_size").empty()) set("use_custom_toolbar_size", "0"); @@ -388,7 +391,7 @@ void AppConfig::update_skein_dir(const std::string &dir) { this->set("recent", "skein_directory", dir); } - +/* std::string AppConfig::get_last_output_dir(const std::string &alt) const { @@ -406,6 +409,26 @@ void AppConfig::update_last_output_dir(const std::string &dir) { this->set("", "last_output_path", dir); } +*/ +std::string AppConfig::get_last_output_dir(const std::string& alt, const bool removable) const +{ + std::string s1 = (removable ? "last_output_path_removable" : "last_output_path"); + std::string s2 = (removable ? "remember_output_path_removable" : "remember_output_path"); + const auto it = m_storage.find(""); + if (it != m_storage.end()) { + const auto it2 = it->second.find(s1); + const auto it3 = it->second.find(s2); + if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1") + return it2->second; + } + return alt; +} + +void AppConfig::update_last_output_dir(const std::string& dir, const bool removable) +{ + this->set("", (removable ? "last_output_path_removable" : "last_output_path"), dir); +} + void AppConfig::reset_selections() { diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index b432367b6..32f1c32c8 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -102,8 +102,10 @@ public: void update_config_dir(const std::string &dir); void update_skein_dir(const std::string &dir); - std::string get_last_output_dir(const std::string &alt) const; - void update_last_output_dir(const std::string &dir); + //std::string get_last_output_dir(const std::string &alt) const; + //void update_last_output_dir(const std::string &dir); + std::string get_last_output_dir(const std::string& alt, const bool removable = false) const; + void update_last_output_dir(const std::string &dir, const bool removable = false); // reset the current print / filament / printer selections, so that // the PresetBundle::load_selections(const AppConfig &config) call will select diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 80a3646f0..c7f374085 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -701,6 +701,7 @@ struct Sidebar::priv wxButton *btn_reslice; ScalableButton *btn_send_gcode; ScalableButton *btn_remove_device; + ScalableButton* btn_export_gcode_removable; //exports to NON-removable drives (but appears only if removable drive is connected) priv(Plater *plater) : plater(plater) {} ~priv(); @@ -866,6 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to hard drive"))); // regular buttons "Slice now" and "Export G-code" @@ -887,6 +889,7 @@ Sidebar::Sidebar(Plater *parent) complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); complect_btns_sizer->Add(p->btn_remove_device); + complect_btns_sizer->Add(p->btn_export_gcode_removable); btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5); @@ -897,7 +900,7 @@ Sidebar::Sidebar(Plater *parent) SetSizer(sizer); // Events - p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); }); + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); @@ -909,6 +912,7 @@ Sidebar::Sidebar(Plater *parent) }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); + p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); } Sidebar::~Sidebar() {} @@ -1056,6 +1060,7 @@ void Sidebar::msw_rescale() p->btn_send_gcode->msw_rescale(); p->btn_remove_device->msw_rescale(); + p->btn_export_gcode_removable->msw_rescale(); const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4; p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); @@ -1289,12 +1294,14 @@ void Sidebar::enable_buttons(bool enable) p->btn_export_gcode->Enable(enable); p->btn_send_gcode->Enable(enable); p->btn_remove_device->Enable(enable); + p->btn_export_gcode_removable->Enable(enable); } -bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } -bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } -bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } -bool Sidebar::show_disconnect(bool show)const { return p->btn_remove_device->Show(show); } +bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } +bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } +bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } +bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); } +bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); } bool Sidebar::is_multifilament() { @@ -3596,12 +3603,8 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) if(!canceled && RemovableDriveManager::get_instance().get_is_writing()) { - //if (!RemovableDriveManager::get_instance().is_last_drive_removed()) - //{ - RemovableDriveManager::get_instance().set_is_writing(false); - show_action_buttons(false); - //} - + RemovableDriveManager::get_instance().set_is_writing(false); + show_action_buttons(false); } } @@ -4160,14 +4163,16 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const const auto prin_host_opt = config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); - bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed() ; // #dk_FIXME + bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed(); + bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0; // when a background processing is ON, export_btn and/or send_btn are showing if (wxGetApp().app_config->get("background_processing") == "1") { - if (sidebar->show_reslice(false) | - sidebar->show_export(true) | - sidebar->show_send(send_gcode_shown) | - sidebar->show_disconnect(disconnect_shown)) + if (sidebar->show_reslice(false) | + sidebar->show_export(true) | + sidebar->show_send(send_gcode_shown) | + sidebar->show_disconnect(disconnect_shown) | + sidebar->show_export_removable(export_removable_shown)) sidebar->Layout(); } else @@ -4175,7 +4180,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(is_ready_to_slice) | sidebar->show_export(!is_ready_to_slice) | sidebar->show_send(send_gcode_shown && !is_ready_to_slice) | - sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice)) + sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice) | + sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice)) sidebar->Layout(); } } @@ -4684,7 +4690,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe } } -void Plater::export_gcode() +void Plater::export_gcode(bool prefer_removable) { if (p->model.objects.empty()) return; @@ -4706,11 +4712,19 @@ void Plater::export_gcode() } default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - if (GUI::RemovableDriveManager::get_instance().update()) + bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update(); + if(prefer_removable) { - if (!RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir)) + if(removable_drives_connected) { - start_dir = RemovableDriveManager::get_instance().get_drive_path(); + auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true); + if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable)) + { + start_dir = start_dir_removable; + }else + { + start_dir = RemovableDriveManager::get_instance().get_drive_path(); + } } } wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")), @@ -4723,7 +4737,7 @@ void Plater::export_gcode() fs::path output_path; if (dlg.ShowModal() == wxID_OK) { fs::path path = into_path(dlg.GetPath()); - wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string())); output_path = std::move(path); } if (! output_path.empty()) @@ -4739,7 +4753,7 @@ void Plater::export_gcode() { RemovableDriveManager::get_instance().set_is_writing(true); RemovableDriveManager::get_instance().erase_callbacks(); - RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this)); + RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this)); } } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 1bea07795..f9891a252 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -120,6 +120,7 @@ public: bool show_export(bool show) const; bool show_send(bool show) const; bool show_disconnect(bool show)const; + bool show_export_removable(bool show) const; bool is_multifilament(); void update_mode(); @@ -186,7 +187,7 @@ public: void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); - void export_gcode(); + void export_gcode(bool prefer_removable = true); void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 5041e2120..99da30ed8 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -503,7 +503,7 @@ void RemovableDriveManager::check_and_notify() } } } -void RemovableDriveManager::add_callback(std::function callback) +void RemovableDriveManager::add_remove_callback(std::function callback) { m_callbacks.push_back(callback); } @@ -587,4 +587,8 @@ void RemovableDriveManager::set_did_eject(const bool b) { m_did_eject = b; } +size_t RemovableDriveManager::get_drives_count() +{ + return m_current_drives.size(); +} }}//namespace Slicer::Gui diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 1767490d1..b5b5c0952 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -45,7 +45,7 @@ public: std::vector get_all_drives(); bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed - void add_callback(std::function callback); + void add_remove_callback(std::function callback); // erases all callbacks added by add_callback() void erase_callbacks(); // marks one of the eveices in vector as last used @@ -59,6 +59,7 @@ public: bool get_did_eject(); void set_did_eject(const bool b); std::string get_drive_name(const std::string& path); + size_t get_drives_count(); private: RemovableDriveManager(); void search_for_drives(); From 53f04b4bfdddb6fbc533ffe9cfcafeda1243aea7 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 3 Jan 2020 10:33:44 +0100 Subject: [PATCH 07/28] callback for showing action buttons when device is connected/disconnected --- src/slic3r/GUI/Plater.cpp | 4 ++++ src/slic3r/GUI/RemovableDriveManager.cpp | 17 +++++++++++++++-- src/slic3r/GUI/RemovableDriveManager.hpp | 8 +++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7f374085..d54083e36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2199,6 +2199,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); + + //void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const + RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1)); } Plater::priv::~priv() @@ -4159,6 +4162,7 @@ void Plater::priv::update_object_menu() void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const { + RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice); wxWindowUpdateLocker noUpdater(sidebar); const auto prin_host_opt = config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 99da30ed8..74197d677 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -414,7 +414,8 @@ RemovableDriveManager::RemovableDriveManager(): m_last_save_name(""), m_last_save_path_verified(false), m_is_writing(false), - m_did_eject(false) + m_did_eject(false), + m_plater_ready_to_slice(true) #if __APPLE__ , m_rdmmm(new RDMMMWrapper()) #endif @@ -495,6 +496,10 @@ std::vector RemovableDriveManager::get_all_drives() } void RemovableDriveManager::check_and_notify() { + if(m_drive_count_changed_callback) + { + m_drive_count_changed_callback(m_plater_ready_to_slice); + } if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) @@ -507,10 +512,18 @@ void RemovableDriveManager::add_remove_callback(std::function callback) { m_callbacks.push_back(callback); } -void RemovableDriveManager::erase_callbacks() +void RemovableDriveManager::erase_callbacks() { m_callbacks.clear(); } +void RemovableDriveManager::set_drive_count_changed_callback(std::function callback) +{ + m_drive_count_changed_callback = callback; +} +void RemovableDriveManager::set_plater_ready_to_slice(bool b) +{ + m_plater_ready_to_slice = b; +} void RemovableDriveManager::set_last_save_path(const std::string& path) { m_last_save_path_verified = false; diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index b5b5c0952..8ba2bc64d 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -46,8 +46,12 @@ public: bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed void add_remove_callback(std::function callback); - // erases all callbacks added by add_callback() + // erases all remove callbacks added by add_remove_callback() void erase_callbacks(); + //drive_count_changed callback is called on every added or removed device + void set_drive_count_changed_callback(std::function callback); + //thi serves to set correct value for drive_count_changed callback + void set_plater_ready_to_slice(bool b); // marks one of the eveices in vector as last used void set_last_save_path(const std::string &path); void verify_last_save_path(); @@ -71,6 +75,7 @@ private: std::vector m_current_drives; std::vector> m_callbacks; + std::function m_drive_count_changed_callback; size_t m_drives_count; long m_last_update; std::string m_last_save_path; @@ -78,6 +83,7 @@ private: std::string m_last_save_name; bool m_is_writing;//on device bool m_did_eject; + bool m_plater_ready_to_slice; #if _WIN32 //registers for notifications by creating invisible window void register_window(); From 787a6264b1738cf55ed8045afa9d1c9100c65bb5 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 10 Jan 2020 16:25:32 +0100 Subject: [PATCH 08/28] changed button usage: save to hd is now save to sd card --- src/slic3r/GUI/Plater.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d54083e36..7fd272d0a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -701,7 +701,7 @@ struct Sidebar::priv wxButton *btn_reslice; ScalableButton *btn_send_gcode; ScalableButton *btn_remove_device; - ScalableButton* btn_export_gcode_removable; //exports to NON-removable drives (but appears only if removable drive is connected) + ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) priv(Plater *plater) : plater(plater) {} ~priv(); @@ -867,7 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); - init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to hard drive"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to SD card/ USB thumb drive"))); // regular buttons "Slice now" and "Export G-code" @@ -888,8 +888,9 @@ Sidebar::Sidebar(Plater *parent) auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL); complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); - complect_btns_sizer->Add(p->btn_remove_device); complect_btns_sizer->Add(p->btn_export_gcode_removable); + complect_btns_sizer->Add(p->btn_remove_device); + btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5); @@ -900,7 +901,7 @@ Sidebar::Sidebar(Plater *parent) SetSizer(sizer); // Events - p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); @@ -912,7 +913,7 @@ Sidebar::Sidebar(Plater *parent) }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); - p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); + p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); } Sidebar::~Sidebar() {} @@ -4175,8 +4176,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(false) | sidebar->show_export(true) | sidebar->show_send(send_gcode_shown) | - sidebar->show_disconnect(disconnect_shown) | - sidebar->show_export_removable(export_removable_shown)) + sidebar->show_export_removable(export_removable_shown) | + sidebar->show_disconnect(disconnect_shown)) sidebar->Layout(); } else @@ -4184,8 +4185,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(is_ready_to_slice) | sidebar->show_export(!is_ready_to_slice) | sidebar->show_send(send_gcode_shown && !is_ready_to_slice) | - sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice) | - sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice)) + sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) | + sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice)) sidebar->Layout(); } } From 2c1bedf50365e44578cb2861f0965b88062c6260 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 20 Jan 2020 16:39:59 +0100 Subject: [PATCH 09/28] new graphics for export button --- resources/icons/export_to_sd.svg | 145 +++++++++++++++++++++++++++++++ src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 resources/icons/export_to_sd.svg diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg new file mode 100644 index 000000000..c836b00a1 --- /dev/null +++ b/resources/icons/export_to_sd.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7fd272d0a..6906614f8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -867,7 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); - init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to SD card/ USB thumb drive"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive"))); // regular buttons "Slice now" and "Export G-code" From 8cf2a978076cad1a1cecb8b62e3d1df55586df78 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 10:23:50 +0100 Subject: [PATCH 10/28] removable drive manager bug fixes --- src/slic3r/GUI/Plater.cpp | 6 ++-- src/slic3r/GUI/RemovableDriveManager.cpp | 38 ++++++++++++++++++++---- src/slic3r/GUI/RemovableDriveManager.hpp | 4 +++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6906614f8..15820c68a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5060,8 +5060,8 @@ void Plater::send_gcode() void Plater::eject_drive() { RemovableDriveManager::get_instance().update(0, true); - //RemovableDriveManager::get_instance().erase_callbacks(); - //RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this)); + RemovableDriveManager::get_instance().erase_callbacks(); + RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this)); RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path()); } @@ -5070,7 +5070,7 @@ void Plater::drive_ejected_callback() if (RemovableDriveManager::get_instance().get_did_eject()) { RemovableDriveManager::get_instance().set_did_eject(false); - wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; + wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_ejected_name() + "(" + RemovableDriveManager::get_instance().get_ejected_path() + ")" + " can now be safely removed from the computer."; wxMessageBox(message); } p->show_action_buttons(false); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 74197d677..76e028994 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -109,6 +109,8 @@ void RemovableDriveManager::eject_drive(const std::string &path) CloseHandle(handle); m_did_eject = true; m_current_drives.erase(it); + m_ejected_path = m_last_save_path; + m_ejected_name = m_last_save_name; break; } } @@ -373,7 +375,8 @@ void RemovableDriveManager::eject_drive(const std::string &path) m_did_eject = true; m_current_drives.erase(it); - + m_ejected_path = m_last_save_path; + m_ejected_name = m_last_save_name; break; } @@ -415,7 +418,9 @@ RemovableDriveManager::RemovableDriveManager(): m_last_save_path_verified(false), m_is_writing(false), m_did_eject(false), - m_plater_ready_to_slice(true) + m_plater_ready_to_slice(true), + m_ejected_path(""), + m_ejected_name("") #if __APPLE__ , m_rdmmm(new RDMMMWrapper()) #endif @@ -452,7 +457,10 @@ bool RemovableDriveManager::update(const long time,const bool check) search_for_drives(); if (m_drives_count != m_current_drives.size()) { - if (check)check_and_notify(); + if (check) + { + check_and_notify(); + } m_drives_count = m_current_drives.size(); } return !m_current_drives.empty(); @@ -500,7 +508,8 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path)) + std::cout << m_callbacks.size() << m_last_save_path_verified << !is_drive_mounted(m_last_save_path) << std::endl; + if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) { @@ -526,8 +535,17 @@ void RemovableDriveManager::set_plater_ready_to_slice(bool b) } void RemovableDriveManager::set_last_save_path(const std::string& path) { - m_last_save_path_verified = false; - m_last_save_path = path; + if(m_last_save_path_verified)// if old path is on drive + { + if(get_drive_from_path(path) != "") //and new is too, rewrite the path + { + m_last_save_path_verified = false; + m_last_save_path = path; + }//else do nothing + }else + { + m_last_save_path = path; + } } void RemovableDriveManager::verify_last_save_path() { @@ -604,4 +622,12 @@ size_t RemovableDriveManager::get_drives_count() { return m_current_drives.size(); } +std::string RemovableDriveManager::get_ejected_path() +{ + return m_ejected_path; +} +std::string RemovableDriveManager::get_ejected_name() +{ + return m_ejected_name; +} }}//namespace Slicer::Gui diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 8ba2bc64d..6bd90e98c 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -64,6 +64,8 @@ public: void set_did_eject(const bool b); std::string get_drive_name(const std::string& path); size_t get_drives_count(); + std::string get_ejected_path(); + std::string get_ejected_name(); private: RemovableDriveManager(); void search_for_drives(); @@ -84,6 +86,8 @@ private: bool m_is_writing;//on device bool m_did_eject; bool m_plater_ready_to_slice; + std::string m_ejected_path; + std::string m_ejected_name; #if _WIN32 //registers for notifications by creating invisible window void register_window(); From 4c46bece96e7dc84768084213a8a14154ac025b8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 13:06:10 +0100 Subject: [PATCH 11/28] removable drive manager bug fixes linux --- src/slic3r/GUI/RemovableDriveManager.cpp | 42 ++++-------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 76e028994..4ed6d36a4 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -241,35 +241,10 @@ void RemovableDriveManager::search_for_drives() //search /media/* folder search_path("/media/*", "/media"); - //search /Volumes/* folder (OSX) //search_path("/Volumes/*", "/Volumes"); std::string path(std::getenv("USER")); std::string pp(path); - //std::cout << "user: "<< path << "\n"; - //if program is run with sudo, we have to search for all users - // but do we want that? - /* - if(path == "root"){ - while (true) { - passwd* entry = getpwent(); - if (!entry) { - break; - } - path = entry->pw_name; - pp = path; - //search /media/USERNAME/* folder - pp = "/media/"+pp; - path = "/media/" + path + "/*"; - search_path(path, pp); - //search /run/media/USERNAME/* folder - path = "/run" + path; - pp = "/run"+pp; - search_path(path, pp); - } - endpwent(); - }else - */ { //search /media/USERNAME/* folder pp = "/media/"+pp; @@ -312,7 +287,6 @@ void RemovableDriveManager::inspect_file(const std::string &path, const std::str { //free space boost::filesystem::space_info si = boost::filesystem::space(path); - //std::cout << "Free space: " << fs_si.free << "Available space: " << fs_si.available << " " << path << '\n'; if(si.available != 0) { //user id @@ -355,7 +329,7 @@ void RemovableDriveManager::eject_drive(const std::string &path) i++; } } - std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; + //std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; // there is no usable command in c++ so terminal command is used instead // but neither triggers "succesful safe removal messege" std::string command = ""; @@ -388,7 +362,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) if (m_current_drives.empty()) return false; std::size_t found = path.find_last_of("/"); - std::string new_path = path.substr(0,found); + std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { if(compare_filesystem_id(new_path, (*it).path)) @@ -399,7 +373,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) std::string RemovableDriveManager::get_drive_from_path(const std::string& path) { std::size_t found = path.find_last_of("/"); - std::string new_path = path.substr(0, found); + std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; //check if same filesystem for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { @@ -508,7 +482,6 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - std::cout << m_callbacks.size() << m_last_save_path_verified << !is_drive_mounted(m_last_save_path) << std::endl; if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) @@ -575,16 +548,15 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path) } bool RemovableDriveManager::is_last_drive_removed() { - //std::cout<<"is last: "< Date: Thu, 2 Jan 2020 11:28:37 +0100 Subject: [PATCH 12/28] Throwing exceptions with text after copy file check failure and renaming from .tmp failure --- src/libslic3r/utils.cpp | 10 +++++----- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 36653cdaf..f91d32d28 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -449,7 +449,7 @@ int copy_file(const std::string &from, const std::string &to, const bool with_ch ret_val = check_copy(from, to_temp); if (ret_val == 0 && rename_file(to_temp, to)) - ret_val = -1; + ret_val = -3; } return ret_val; } @@ -460,11 +460,11 @@ int check_copy(const std::string &origin, const std::string ©) std::ifstream f2(copy, std::ifstream::in | std::ifstream::binary | std::ifstream::ate); if (f1.fail() || f2.fail()) - return -1; + return -2; std::streampos fsize = f1.tellg(); if (fsize != f2.tellg()) - return -1; + return -2; f1.seekg(0, std::ifstream::beg); f2.seekg(0, std::ifstream::beg); @@ -481,12 +481,12 @@ int check_copy(const std::string &origin, const std::string ©) if (origin_cnt != copy_cnt || (origin_cnt > 0 && std::memcmp(buffer_origin.data(), buffer_copy.data(), origin_cnt) != 0)) // Files are different. - return -1; + return -2; fsize -= origin_cnt; } while (f1.good() && f2.good()); // All data has been read and compared equal. - return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -1; + return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -2; } // Ignore system and hidden files, which may be created by the DropBox synchronisation process. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8af721f9d..548a19f77 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -100,8 +100,23 @@ void BackgroundSlicingProcess::process_fff() //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); - if (copy_file(m_temp_output_path, export_path, GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path)) != 0) + GUI::RemovableDriveManager::get_instance().update(); + bool with_check = GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path); + int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check); + if (with_check && copy_ret_val == -2) + { + std::string err_msg = "Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at " + export_path + ".tmp."; + throw std::runtime_error(_utf8(L(err_msg))); + } + else if (copy_ret_val == -3) + { + std::string err_msg = "Renaming of the G-code after copying to the selected destination folder has failed. Current path is " + export_path + ".tmp. Please try exporting again."; + throw std::runtime_error(_utf8(L(err_msg))); + } + else if ( copy_ret_val != 0) + { throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?"))); + } m_print->set_status(95, _utf8(L("Running post-processing scripts"))); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); From dd09077bba74e717419bed1a0a717eef747afd46 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 21 Jan 2020 14:07:13 +0100 Subject: [PATCH 13/28] Reload from disk command enhanced to work with 3mf/amf files saved with PrusaSlicer 2.1.0 and earlier --- src/libslic3r/Technologies.hpp | 4 + src/slic3r/GUI/GUI_ObjectList.cpp | 10 +++ src/slic3r/GUI/Plater.cpp | 130 ++++++++++++++++++++++++++++-- src/slic3r/GUI/Plater.hpp | 5 ++ 4 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 727200ca8..0728dedc6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,4 +59,8 @@ // Enable 6 degrees of freedom camera #define ENABLE_6DOF_CAMERA (1 && ENABLE_2_2_0_BETA1) +// Enhance reload from disk to be able to work with 3mf/amf files saved with PrusaSlicer 2.1.0 and earlier +#define ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK (1 && ENABLE_2_2_0_BETA1) + + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 271e51bc2..31e83cc45 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1622,9 +1622,14 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const { +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), + [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu); +#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK } void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const @@ -3891,10 +3896,15 @@ void ObjectList::show_multi_selection_menu() _(L("Select extruder number for selected objects and/or parts")), [this](wxCommandEvent&) { extruder_selection(); }, "", menu); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), + [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu); +#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK wxGetApp().plater()->PopupMenu(menu); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b388ad1d2..f3af5ffeb 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -26,6 +26,9 @@ #include #include #include +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK +#include +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" @@ -1856,6 +1859,9 @@ struct Plater::priv bool is_view3D_shown() const { return current_panel == view3D; } void set_current_canvas_as_dirty(); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + GLCanvas3D* get_current_canvas3D(); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool init_view_toolbar(); @@ -1984,7 +1990,9 @@ struct Plater::priv bool can_fix_through_netfabb() const; bool can_set_instance_to_object() const; bool can_mirror() const; +#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool can_reload_from_disk() const; +#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK #if ENABLE_THUMBNAIL_GENERATOR void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); @@ -3244,6 +3252,10 @@ void Plater::priv::reload_from_disk() else missing_input_paths.push_back(volume->source.input_file); } +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + else if (!volume->name.empty()) + missing_input_paths.push_back(volume->name); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK } std::sort(missing_input_paths.begin(), missing_input_paths.end()); @@ -3255,10 +3267,9 @@ void Plater::priv::reload_from_disk() fs::path search = missing_input_paths.back(); wxString title = _(L("Please select the file to reload")); #if defined(__APPLE__) - title += " (" + from_u8(search.filename().string()) + "):"; -#else - title += ":"; + title += " (" + from_u8(search.filename().string()) + ")"; #endif // __APPLE__ + title += ":"; wxFileDialog dialog(q, title, "", from_u8(search.filename().string()), file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) return; @@ -3299,10 +3310,20 @@ void Plater::priv::reload_from_disk() std::sort(input_paths.begin(), input_paths.end()); input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end()); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + std::vector fail_list; +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + // load one file at a time for (size_t i = 0; i < input_paths.size(); ++i) { const auto& path = input_paths[i].string(); + +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + wxBusyCursor wait; + wxBusyInfo info(_(L("Reload from: ")) + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas()); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + Model new_model; try { @@ -3320,18 +3341,70 @@ void Plater::priv::reload_from_disk() } // update the selected volumes whose source is the current file - for (const SelectedVolume& old_v : selected_volumes) + for (const SelectedVolume& sel_v : selected_volumes) { - ModelObject* old_model_object = model.objects[old_v.object_idx]; - ModelVolume* old_volume = old_model_object->volumes[old_v.volume_idx]; + ModelObject* old_model_object = model.objects[sel_v.object_idx]; + ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx]; + +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + 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) +#else int new_volume_idx = old_volume->source.volume_idx; int new_object_idx = old_volume->source.object_idx; if (boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string())) +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK { +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + int new_volume_idx = -1; + int new_object_idx = -1; + if (has_source) + { + // take idxs from source + new_volume_idx = old_volume->source.volume_idx; + new_object_idx = old_volume->source.object_idx; + } + else + { + // take idxs from the 1st matching volume + 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) + { + new_volume_idx = (int)v; + new_object_idx = (int)o; + found = true; + break; + } + } + if (found) + break; + } + } + + 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; + } +#else assert(new_object_idx < (int)new_model.objects.size()); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK ModelObject* new_model_object = new_model.objects[new_object_idx]; +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + 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; + } +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK if (new_volume_idx < (int)new_model_object->volumes.size()) { old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]); @@ -3342,8 +3415,10 @@ void Plater::priv::reload_from_disk() new_volume->set_material_id(old_volume->material_id()); new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform); new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); +#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK new_volume->source.input_file = path; - std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back()); +#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + 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(); } @@ -3351,6 +3426,19 @@ void Plater::priv::reload_from_disk() } } +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + if (!fail_list.empty()) + { + wxString message = _(L("Unable to reload:")) + "\n"; + for (const wxString& s : fail_list) + { + message += s + "\n"; + } + wxMessageDialog dlg(q, message, _(L("Error during reload")), wxOK | wxOK_DEFAULT | wxICON_WARNING); + dlg.ShowModal(); + } +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + // update 3D scene update(); @@ -3831,8 +3919,13 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")), [this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), + [this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu); +#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK sidebar->obj_list()->append_menu_item_export_stl(menu); } @@ -3860,8 +3953,13 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q); menu->AppendSeparator(); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")), + [this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr); +#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")), [this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")), [this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr, @@ -3964,6 +4062,13 @@ void Plater::priv::set_current_canvas_as_dirty() preview->set_as_dirty(); } +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK +GLCanvas3D* Plater::priv::get_current_canvas3D() +{ + return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); +} +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + bool Plater::priv::init_view_toolbar() { if (view_toolbar.get_items_count() > 0) @@ -4034,6 +4139,7 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } +#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool Plater::priv::can_reload_from_disk() const { // struct to hold selected ModelVolumes by their indices @@ -4079,6 +4185,7 @@ bool Plater::priv::can_reload_from_disk() const return !paths.empty(); } +#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) { @@ -5334,6 +5441,13 @@ GLCanvas3D* Plater::canvas3D() return p->view3D->get_canvas3d(); } +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK +GLCanvas3D* Plater::get_current_canvas3D() +{ + return p->get_current_canvas3D(); +} +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + BoundingBoxf Plater::bed_shape_bb() const { return p->bed_shape_bb(); @@ -5526,7 +5640,9 @@ bool Plater::can_copy_to_clipboard() const bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); } bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); } +#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } +#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 1bea07795..a85a3540a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -238,6 +238,9 @@ public: int get_selected_object_idx(); bool is_single_full_object_selection() const; GLCanvas3D* canvas3D(); +#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK + GLCanvas3D* get_current_canvas3D(); +#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK BoundingBoxf bed_shape_bb() const; void set_current_canvas_as_dirty(); @@ -262,7 +265,9 @@ public: bool can_copy_to_clipboard() const; bool can_undo() const; bool can_redo() const; +#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool can_reload_from_disk() const; +#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK void msw_rescale(); From 15391da512f68e5bd748fe52d0f53b841fdbed46 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 14:34:22 +0100 Subject: [PATCH 14/28] OSX handler for 3dconnexion drivers --- src/CMakeLists.txt | 2 +- src/platform/osx/entitlements.plist | 9 + src/slic3r/CMakeLists.txt | 1 + src/slic3r/GUI/Mouse3DController.cpp | 78 ++++++-- src/slic3r/GUI/Mouse3DController.hpp | 42 ++++- src/slic3r/GUI/Mouse3DHandlerMac.mm | 255 +++++++++++++++++++++++++++ 6 files changed, 364 insertions(+), 23 deletions(-) create mode 100644 src/platform/osx/entitlements.plist create mode 100644 src/slic3r/GUI/Mouse3DHandlerMac.mm diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54ebf33b9..11996af1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,7 @@ endif (MINGW) if (NOT WIN32) # Binary name on unix like systems (OSX, Linux) - set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") + set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () target_link_libraries(PrusaSlicer libslic3r cereal) diff --git a/src/platform/osx/entitlements.plist b/src/platform/osx/entitlements.plist new file mode 100644 index 000000000..59274f955 --- /dev/null +++ b/src/platform/osx/entitlements.plist @@ -0,0 +1,9 @@ + + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9fd846272..751a1fcaf 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -173,6 +173,7 @@ if (APPLE) Utils/MacDarkMode.mm GUI/RemovableDriveManagerMM.mm GUI/RemovableDriveManagerMM.h + GUI/Mouse3DHandlerMac.mm ) #DK FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index e28336913..fd58447b5 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -15,6 +15,9 @@ #include +//unofficial linux lib +//#include + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -204,7 +207,11 @@ Mouse3DController::Mouse3DController() , m_device_str("") , m_running(false) , m_show_settings_dialog(false) + , m_mac_mouse_connected(false) , m_settings_dialog_closed_by_user(false) +#if __APPLE__ + ,m_handler_mac(new Mouse3DHandlerMac(this)) +#endif //__APPLE__ { m_last_time = std::chrono::high_resolution_clock::now(); } @@ -244,7 +251,7 @@ bool Mouse3DController::apply(Camera& camera) return false; // check if the user unplugged the device - if (!m_running && is_device_connected()) + if (!is_running() && is_device_connected()) { disconnect_device(); // hides the settings dialog if the user un-plug the device @@ -261,7 +268,7 @@ bool Mouse3DController::apply(Camera& camera) void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const { - if (!m_running || !m_show_settings_dialog) + if (!is_running() || !m_show_settings_dialog) return; // when the user clicks on [X] or [Close] button we need to trigger @@ -397,6 +404,9 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const bool Mouse3DController::connect_device() { +#ifdef __APPLE__ + return false; +#endif//__APPLE__ static const long long DETECTION_TIME_MS = 2000; // two seconds if (is_device_connected()) @@ -405,7 +415,7 @@ bool Mouse3DController::connect_device() // check time since last detection took place if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS) return false; - + m_last_time = std::chrono::high_resolution_clock::now(); // Enumerates devices @@ -528,7 +538,7 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { -#ifdef __linux__ +#if defined(__linux__) hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); if (test_device != nullptr) { @@ -536,7 +546,7 @@ bool Mouse3DController::connect_device() #else if (device.second.front().has_valid_usage()) { -#endif // __linux__ +#endif // __linux__ vendor_id = device.first.first; product_id = device.first.second; break; @@ -553,6 +563,7 @@ bool Mouse3DController::connect_device() #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + #ifdef __linux__ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) @@ -567,7 +578,7 @@ bool Mouse3DController::connect_device() hid_close(test_device); break; } -#else +#else // !__linux__ if (data.has_valid_usage()) { path = data.path; @@ -632,7 +643,9 @@ bool Mouse3DController::connect_device() BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")"; if (!path.empty()) BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'"; - +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Opened device." << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present double translation_speed = 4.0; float rotation_speed = 4.0; @@ -717,7 +730,7 @@ void Mouse3DController::run() } void Mouse3DController::collect_input() { - DataPacket packet = { 0 }; + DataPacketRaw packet = { 0 }; int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); if (res < 0) { @@ -725,10 +738,47 @@ void Mouse3DController::collect_input() stop(); return; } - + handle_input(packet, res); +} + +void Mouse3DController::handle_input_axis(const DataPacketAxis& packet) +{ + if (!wxGetApp().IsActive()) + return; + bool appended = false; + //translation + double deadzone = m_state.get_translation_deadzone(); + Vec3d translation(std::abs(packet[0]) > deadzone ? -packet[0] : 0.0, + std::abs(packet[1]) > deadzone ? packet[1] : 0.0, + std::abs(packet[2]) > deadzone ? packet[2] : 0.0); + if (!translation.isApprox(Vec3d::Zero())) + { + m_state.append_translation(translation); + appended = true; + } + //rotation + deadzone = m_state.get_rotation_deadzone(); + Vec3f rotation(std::abs(packet[3]) > deadzone ? -(float)packet[3] : 0.0, + std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0, + std::abs(packet[5]) > deadzone ? -(float)packet[5] : 0.0); + if (!rotation.isApprox(Vec3f::Zero())) + { + m_state.append_rotation(rotation); + appended = true; + } + if (appended) + { + wxGetApp().plater()->set_current_canvas_as_dirty(); + // ask for an idle event to update 3D scene + wxWakeUpIdle(); + } +} +void Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght) +{ if (!wxGetApp().IsActive()) return; + int res = packet_lenght; bool updated = false; if (res == 7) @@ -751,7 +801,7 @@ void Mouse3DController::collect_input() } } -bool Mouse3DController::handle_packet(const DataPacket& packet) +bool Mouse3DController::handle_packet(const DataPacketRaw& packet) { switch (packet[0]) { @@ -795,7 +845,7 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) return false; } -bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) +bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet) { switch (packet[0]) { @@ -842,7 +892,7 @@ double convert_input(unsigned char first, unsigned char second, double deadzone) return (std::abs(ret) > deadzone) ? ret : 0.0; } -bool Mouse3DController::handle_packet_translation(const DataPacket& packet) +bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet) { double deadzone = m_state.get_translation_deadzone(); Vec3d translation(-convert_input(packet[1], packet[2], deadzone), @@ -858,7 +908,7 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) return false; } -bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) +bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte) { double deadzone = (double)m_state.get_rotation_deadzone(); #if ENABLE_6DOF_CAMERA @@ -880,7 +930,7 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne return false; } -bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) +bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size) { unsigned int data = 0; for (unsigned int i = 1; i < packet_size; ++i) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index f3826f8e2..018cb98e6 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -14,9 +14,15 @@ #include #include + + namespace Slic3r { namespace GUI { +#if __APPLE__ + class Mouse3DHandlerMac; +#endif//__APPLE__ + struct Camera; class GLCanvas3D; @@ -141,6 +147,7 @@ class Mouse3DController hid_device* m_device; std::string m_device_str; bool m_running; + bool m_mac_mouse_connected; mutable bool m_show_settings_dialog; // set to true when ther user closes the dialog by clicking on [X] or [Close] buttons mutable bool m_settings_dialog_closed_by_user; @@ -152,9 +159,11 @@ public: void init(); void shutdown(); - bool is_device_connected() const { return m_device != nullptr; } - bool is_running() const { return m_running; } + bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; } + bool is_running() const { return m_running || m_mac_mouse_connected; } + void set_mac_mouse_connected(bool b){m_mac_mouse_connected = b;}; + bool process_mouse_wheel() { return m_state.process_mouse_wheel(); } bool apply(Camera& camera); @@ -163,26 +172,43 @@ public: void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); } void render_settings_dialog(GLCanvas3D& canvas) const; + typedef std::array DataPacketAxis; + void handle_input_axis(const DataPacketAxis& packet); private: bool connect_device(); void disconnect_device(); void start(); void stop() { m_running = false; } + typedef std::array DataPacketRaw; // secondary thread methods void run(); void collect_input(); + void handle_input(const DataPacketRaw& packet, const int packet_lenght); + bool handle_packet(const DataPacketRaw& packet); + bool handle_wireless_packet(const DataPacketRaw& packet); + bool handle_packet_translation(const DataPacketRaw& packet); + bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte); + bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size); - typedef std::array DataPacket; - bool handle_packet(const DataPacket& packet); - bool handle_wireless_packet(const DataPacket& packet); - bool handle_packet_translation(const DataPacket& packet); - bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); - bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); +#if __APPLE__ + Mouse3DHandlerMac* m_handler_mac; +#endif//__APPLE__ }; +#if __APPLE__ +class Mouse3DHandlerMac{ + public: + Mouse3DHandlerMac(Mouse3DController* controller); + ~Mouse3DHandlerMac(); + + bool available(); +}; +#endif//__APPLE__ + } // namespace GUI } // namespace Slic3r + #endif // slic3r_Mouse3DController_hpp_ diff --git a/src/slic3r/GUI/Mouse3DHandlerMac.mm b/src/slic3r/GUI/Mouse3DHandlerMac.mm new file mode 100644 index 000000000..60006b8c1 --- /dev/null +++ b/src/slic3r/GUI/Mouse3DHandlerMac.mm @@ -0,0 +1,255 @@ + +#include "Mouse3DController.hpp" + +#include +#include + +#include + +#include +#include + + +static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL; + +static uint16_t clientID = 0; + +static bool driver_loaded = false; +static bool has_new_driver = false; // drivers >= 10.2.2 are "new", and can process events on a separate thread + +// replicate just enough of the 3Dx API for our uses, not everything the driver provides + +#define kConnexionClientModeTakeOver 1 +#define kConnexionMaskAxis 0x3f00 +#define kConnexionMaskAll 0x3fff +#define kConnexionMaskAllButtons 0xffffffff +#define kConnexionCmdHandleButtons 2 +#define kConnexionCmdHandleAxis 3 +#define kConnexionCmdAppSpecific 10 +#define kConnexionMsgDeviceState '3dSR' +#define kConnexionCtlGetDeviceID '3did' + +#pragma pack(push, 2) +struct ConnexionDeviceState { + uint16_t version; + uint16_t client; + uint16_t command; + int16_t param; + int32_t value; + uint64_t time; + uint8_t report[8]; + uint16_t buttons8; // obsolete! (pre-10.x drivers) + int16_t axis[6]; // tx, ty, tz, rx, ry, rz + uint16_t address; + uint32_t buttons; +}; +#pragma pack(pop) + +// callback functions: +typedef void (*AddedHandler)(uint32_t); +typedef void (*RemovedHandler)(uint32_t); +typedef void (*MessageHandler)(uint32_t, uint32_t msg_type, void *msg_arg); + +// driver functions: +typedef int16_t (*SetConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler, bool); +typedef int16_t (*InstallConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler); +typedef void (*CleanupConnexionHandlers_ptr)(); +typedef uint16_t (*RegisterConnexionClient_ptr)(uint32_t signature, + const char *name, + uint16_t mode, + uint32_t mask); +typedef void (*SetConnexionClientButtonMask_ptr)(uint16_t clientID, uint32_t buttonMask); +typedef void (*UnregisterConnexionClient_ptr)(uint16_t clientID); +typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID, + uint32_t message, + int32_t param, + int32_t *result); + +#define DECLARE_FUNC(name) name##_ptr name = NULL + +DECLARE_FUNC(SetConnexionHandlers); +DECLARE_FUNC(InstallConnexionHandlers); +DECLARE_FUNC(CleanupConnexionHandlers); +DECLARE_FUNC(RegisterConnexionClient); +DECLARE_FUNC(SetConnexionClientButtonMask); +DECLARE_FUNC(UnregisterConnexionClient); +DECLARE_FUNC(ConnexionClientControl); + +static void *load_func(void *module, const char *func_name) +{ + void *func = dlsym(module, func_name); + +//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (func) { + BOOST_LOG_TRIVIAL(info) << func_name <<" loaded"; + } + else { + //printf(" %s\n", dlerror()); + BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror(); + } +//#endif + + return func; +} + +#define LOAD_FUNC(name) name = (name##_ptr)load_func(module, #name) + +static void *module; // handle to the whole driver + +static bool load_driver_functions() +{ + if (driver_loaded) { + return true; + } + + module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient", + RTLD_LAZY | RTLD_LOCAL); + + if (module) { + BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers"; + LOAD_FUNC(SetConnexionHandlers); + + if (SetConnexionHandlers != NULL) { + driver_loaded = true; + has_new_driver = true; + } + else { + BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers"; + LOAD_FUNC(InstallConnexionHandlers); + + driver_loaded = (InstallConnexionHandlers != NULL); + } + + if (driver_loaded) { + LOAD_FUNC(CleanupConnexionHandlers); + LOAD_FUNC(RegisterConnexionClient); + LOAD_FUNC(SetConnexionClientButtonMask); + LOAD_FUNC(UnregisterConnexionClient); + LOAD_FUNC(ConnexionClientControl); + } + } + else { + BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ; +#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf(" %s\n", dlerror()); +#endif + } +#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf("loaded: %s\n", driver_loaded ? "YES" : "NO"); + printf("new: %s\n", has_new_driver ? "YES" : "NO"); +#endif + BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ; + return driver_loaded; +} + +static void unload_driver() +{ + dlclose(module); +} + +static void DeviceAdded(uint32_t unused) +{ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout<<"3D device added"<> 16; + int16_t productID = result & 0xffff; + + //TODO: verify device + + + mouse_3d_controller->set_mac_mouse_connected(true); +} + +static void DeviceRemoved(uint32_t unused) +{ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf("3d device removed\n"); +#endif + BOOST_LOG_TRIVIAL(info) << "3dx device removed\n"; + mouse_3d_controller->set_mac_mouse_connected(true); +} + +static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg) +{ + if (msg_type == kConnexionMsgDeviceState) { + ConnexionDeviceState *s = (ConnexionDeviceState *)msg_arg; + if (s->client == clientID) { + switch (s->command) { + case kConnexionCmdHandleAxis: { + /* + The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500. + */ + //Actually we are getting values way over 1024. Max is probably 2048 now. + std::array packet; + for (int i = 0; i < 6; i++) { + packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value. + } + mouse_3d_controller->handle_input_axis(packet); + + + break; + } + case kConnexionCmdHandleButtons: + break; + case kConnexionCmdAppSpecific: + break; + default: + break; + } + } + } + +} + +namespace Slic3r { +namespace GUI { +Mouse3DHandlerMac::Mouse3DHandlerMac(Mouse3DController* controller) +{ + BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts"; + if (load_driver_functions()) { + mouse_3d_controller = controller; + + uint16_t error; + if (has_new_driver) { + error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false); + } + else { + error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved); + } + + if (error) { + return; + } + + // Registration is done either by 4letter constant (CFBundleSignature - obsolete + //and we dont have that) or Executable name in pascal string(first byte is string lenght). + //If no packets are recieved the name might be different - check cmake. If debugging try commenting + // set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") + + clientID = RegisterConnexionClient( + 0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis); + BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered"; + } +} + +Mouse3DHandlerMac::~Mouse3DHandlerMac() +{ + if (driver_loaded) { + UnregisterConnexionClient(clientID); + CleanupConnexionHandlers(); + unload_driver(); + } + mouse_3d_controller = nullptr; +} + +bool Mouse3DHandlerMac::available() +{ + return driver_loaded; +} + +}}//namespace Slic3r::GUI From 1392609c75ff15f5626d557e62c57da109a5f6c2 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 15:55:59 +0100 Subject: [PATCH 15/28] osx 3dmouse change of rotation directions --- src/slic3r/GUI/Mouse3DController.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index fd58447b5..acadc5b2e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -758,9 +758,9 @@ void Mouse3DController::handle_input_axis(const DataPacketAxis& packet) } //rotation deadzone = m_state.get_rotation_deadzone(); - Vec3f rotation(std::abs(packet[3]) > deadzone ? -(float)packet[3] : 0.0, - std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0, - std::abs(packet[5]) > deadzone ? -(float)packet[5] : 0.0); + Vec3f rotation(std::abs(packet[3]) > deadzone ? (float)packet[3] : 0.0, + std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0, + std::abs(packet[5]) > deadzone ? (float)packet[5] : 0.0); if (!rotation.isApprox(Vec3f::Zero())) { m_state.append_rotation(rotation); From d8b5f2950d1a02c7d577ca4459745d42d7c138c2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 21 Jan 2020 16:33:44 +0100 Subject: [PATCH 16/28] Select first printer for 3rd vendor. + fixed typo in output message --- src/slic3r/GUI/ConfigWizard.cpp | 5 ++++- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index add680a40..880d1127b 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1702,6 +1702,9 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i if (page->install && !install) page->select_all(false); page->install = install; + // if some 3rd vendor is selected, select first printer for them + if (install) + page->printer_pickers[0]->select_one(0, true); page->Layout(); } @@ -2020,7 +2023,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_temps = new PageTemperatures(this)); // Pages for 3rd party vendors - p->create_3rdparty_pages(); // Needs to ne done _before_ creating PageVendors + p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors p->add_page(p->page_vendors = new PageVendors(this)); p->load_pages(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a00668e82..a86dd5ceb 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4669,7 +4669,7 @@ void Plater::remove(size_t obj_idx) { p->remove(obj_idx); } void Plater::reset() { p->reset(); } void Plater::reset_with_confirm() { - if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue ?")), wxString(SLIC3R_APP_NAME) + " - " + _(L("Delete all")), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) + if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue?")), wxString(SLIC3R_APP_NAME) + " - " + _(L("Delete all")), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) reset(); } From 094cef26d463e5041c6106eead859abec34ea719 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 17:11:41 +0100 Subject: [PATCH 17/28] Fix of the new PlaceholderParser int() conversion. Fixes https://github.com/prusa3d/PrusaSlicer/pull/3271 Also some old errors (typos, UBs) were fixed. --- src/libslic3r/PlaceholderParser.cpp | 14 +++++++------- tests/libslic3r/test_placeholder_parser.cpp | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index bbf32a141..db3134ded 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -338,7 +338,7 @@ namespace client case TYPE_INT : return expr(this->i(), start_pos, this->it_range.end()); case TYPE_DOUBLE: - return expr((int)(this->d()), start_pos, this->it_range.end()); + return expr(static_cast(this->d()), start_pos, this->it_range.end()); default: this->throw_exception("Cannot convert to integer."); } @@ -418,7 +418,7 @@ namespace client { this->throw_if_not_numeric("Cannot divide a non-numeric type."); rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); - if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) + if ((rhs.type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) rhs.throw_exception("Division by zero"); if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { double d = this->as_d() / rhs.as_d(); @@ -434,7 +434,7 @@ namespace client { this->throw_if_not_numeric("Cannot divide a non-numeric type."); rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); - if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) + if ((rhs.type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) rhs.throw_exception("Division by zero"); if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { double d = std::fmod(this->as_d(), rhs.as_d()); @@ -845,7 +845,7 @@ namespace client } else { // Use the human readable error message. msg += ". "; - msg + it->second; + msg += it->second; } } msg += '\n'; @@ -1134,7 +1134,7 @@ namespace client static void string_(boost::iterator_range &it_range, expr &out) { out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); } static void expr_(expr &value, Iterator &end_pos, expr &out) - { out = expr(std::move(value), out.it_range.begin(), end_pos); } + { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } static void minus_(expr &value, expr &out) { out = value.unary_minus(out.it_range.begin()); } static void not_(expr &value, expr &out) @@ -1152,8 +1152,7 @@ namespace client [ px::bind(&expr::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&expr::max, _val, _2) ] - //FIXME this is likley not correct - | (kw["int"] > '(' > unary_expression(_r1) /* > ')' */ ) [ px::bind(&FactorActions::to_int, _1, _val) ] + | (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1181,6 +1180,7 @@ namespace client keywords.add ("and") ("if") + ("int") //("inf") ("else") ("elsif") diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 5802862b7..4d8217c16 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -28,8 +28,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); } SECTION("math: 2*3/12") { REQUIRE(parser.process("{2*3/12}") == "0"); } SECTION("math: 2.*3/12") { REQUIRE(std::stod(parser.process("{2.*3/12}")) == Approx(0.5)); } -// SECTION("math: 10 % 2.5") { REQUIRE(parser.process("{10%2.5}") == "0"); } -// SECTION("math: 11 / 2.5") { REQUIRE(parser.process("{11/2.5-1}") == "1"); } + SECTION("math: 10 % 2.5") { REQUIRE(std::stod(parser.process("{10%2.5}")) == Approx(0.)); } + SECTION("math: 11 % 2.5") { REQUIRE(std::stod(parser.process("{11%2.5}")) == Approx(1.)); } SECTION("math: 2*(3-12)") { REQUIRE(parser.process("{2*(3-12)}") == "-18"); } SECTION("math: 2*foo*(3-12)") { REQUIRE(parser.process("{2*foo*(3-12)}") == "0"); } SECTION("math: 2*bar*(3-12)") { REQUIRE(parser.process("{2*bar*(3-12)}") == "-36"); } @@ -38,7 +38,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: max(12, 14)") { REQUIRE(parser.process("{max(12, 14)}") == "14"); } SECTION("math: min(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{min(13.4, -1238.1)}")) == Approx(-1238.1)); } SECTION("math: max(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{max(13.4, -1238.1)}")) == Approx(13.4)); } -// SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } + SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } + SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); } // Test the boolean expression parser. auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); }; From 8d9cfa0e98078474c3305b3277d641a83977a93a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 22 Jan 2020 10:03:58 +0100 Subject: [PATCH 18/28] resize of export_to_sd button --- resources/icons/export_to_sd.svg | 227 +++++++++++++++++-------------- src/slic3r/CMakeLists.txt | 3 +- 2 files changed, 126 insertions(+), 104 deletions(-) diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg index c836b00a1..516cec435 100644 --- a/resources/icons/export_to_sd.svg +++ b/resources/icons/export_to_sd.svg @@ -7,9 +7,9 @@ xmlns="http://www.w3.org/2000/svg" id="svg8" version="1.1" - viewBox="0 0 210 297" - height="297mm" - width="210mm"> + viewBox="0 0 3.7041666 3.7041667" + height="3.7041667mm" + width="3.7041667mm"> + + + + + + @@ -33,111 +47,120 @@ - - + transform="translate(-265.33929,12.851203)" + id="layer1-0"> + transform="matrix(1.31769,0,0,1.31769,-167.28747,-111.35623)" + id="layer1-3"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="g4685" + transform="matrix(0.00352778,0,0,-0.00352778,105.26858,151.76571)"> + d="M 381.663,302.607 H 558.791 V 65.846 H 381.663 Z" + style="fill:#d8d8db;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path4687" /> + d="m 470.227,302.607 h 95.411 V 65.846 h -95.411 z" + style="fill:#f7f7f8;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path4689" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 751a1fcaf..8f38cb21b 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -175,7 +175,6 @@ if (APPLE) GUI/RemovableDriveManagerMM.h GUI/Mouse3DHandlerMac.mm ) - #DK FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) endif () @@ -185,7 +184,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) -#DK + if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) endif() From cea7cbfaa09b6fb159bcb48e694271ed192b63a3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 22 Jan 2020 10:50:14 +0100 Subject: [PATCH 19/28] The print order for sequential print now corresponds to the order at the object list. Partial fix of https://github.com/prusa3d/PrusaSlicer/issues/3455 --- src/libslic3r/GCode.cpp | 51 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4cbab67a6..128ddca17 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -8,6 +8,7 @@ #include "GCode/WipeTower.hpp" #include "ShortestPath.hpp" #include "Utils.hpp" +#include "libslic3r.h" #include #include @@ -1086,6 +1087,52 @@ namespace DoExport { } } +// Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints. +static inline std::vector sort_objects_by_z(const Print &print) +{ + std::vector objects(print.objects().begin(), print.objects().end()); + std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); }); + return objects; +} + +// Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model(). +static inline std::vector sort_objects_by_model_order(const Print &print) +{ + const Model &model = print.model(); + // Pair ModelObjects with PrintObjects, remember the order of ModelObjects in the model above. + struct ModelObjectOrder { + const ModelObject *model_object; + const PrintObject *print_object; + size_t order; + }; + // Initialize model_object_order with ModelObjects and their order. + std::vector model_object_order; + model_object_order.reserve(model.objects.size()); + { + size_t order = 0; + for (const ModelObject *model_object : model.objects) + model_object_order.emplace_back(ModelObjectOrder{ model_object, nullptr, order ++ }); + } + // Sort by pointer to ModelObject. + std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.model_object < rhs.model_object; }); + // Assign PrintObject pointer to ModelObject. + for (const PrintObject *print_object : print.objects()) { + auto it = Slic3r::lower_bound_by_predicate(model_object_order.begin(), model_object_order.end(), [print_object](const ModelObjectOrder &model_object_order) { return model_object_order.model_object < print_object->model_object(); }); + // The non-printable objects (objects outside of the print volume or suppressed objects) will have no partner in the print.objects() list. + if (it != model_object_order.end() && it->model_object == print_object->model_object()) + it->print_object = print_object; + } + // Sort back to the initial order. + std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.order < rhs.order; }); + // Produce the output vector of PrintObjects, sorted by the order of ModelObjects in Model. + std::vector objects; + objects.reserve(model_object_order.size()); + for (ModelObjectOrder &order : model_object_order) + if (order.print_object != nullptr) + objects.emplace_back(order.print_object); + return objects; +} + #if ENABLE_THUMBNAIL_GENERATOR void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) #else @@ -1318,8 +1365,8 @@ void GCode::_do_export(Print& print, FILE* file) if (print.config().complete_objects.value) { // Print objects from the smallest to the tallest to avoid collisions // when moving onto next object starting point. - std::vector objects(print.objects()); - std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); }); + std::vector objects = sort_objects_by_model_order(print); +// std::vector objects = sort_objects_by_z(print); size_t finished_objects = 0; for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { const PrintObject &object = *objects[object_id]; From 29fa176d2762faf38e196715be93787a89fc5730 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Jan 2020 15:00:59 +0100 Subject: [PATCH 20/28] DoubleSlider: Implemented code switch between ToolChangeCode and ColorChangeCode for MultiAsSingle mode --- src/slic3r/GUI/wxExtensions.cpp | 168 ++++++++++++++++++++------------ src/slic3r/GUI/wxExtensions.hpp | 7 +- 2 files changed, 108 insertions(+), 67 deletions(-) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8f43f6c30..d4b207dc1 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -3212,7 +3212,7 @@ void DoubleSlider::OnMotion(wxMouseEvent& event) } } -void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu) +void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) @@ -3233,7 +3233,9 @@ void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu) [is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater()); } - const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? _(L("Change extruder")) : _(L("Change extruder (N/A)")); + const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? + (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : + _(L("Change extruder (N/A)")); wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, "change_extruder")); @@ -3244,7 +3246,7 @@ void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu) } } -void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu) +void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) @@ -3265,7 +3267,9 @@ void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu) [is_used_extruder]() { return is_used_extruder; }, Slic3r::GUI::wxGetApp().plater()); } - const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()); + const wxString menu_name = switch_current_code ? + from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()) : + from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()); wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); } @@ -3427,20 +3431,13 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - // if on this Z doesn't exist tick - auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - if (it == m_ticks.ticks.end()) - { + if (m_ticks.ticks.find(TICK_CODE{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick // show context menu on OnRightUp() m_show_context_menu = true; - return; - } - if (it->gcode != Slic3r::ToolChangeCode) - { + else // show "Edit" and "Delete" menu on OnRightUp() m_show_edit_menu = true; - return; - } + return; } detect_selected_slider(event.GetLogicalPosition(dc)); @@ -3489,7 +3486,7 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) std::set used_extruders; auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), Slic3r::LayerTools(m_values[tick])); - for (; it_layer_tools != tool_ordering.end(); it_layer_tools++) + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { const std::vector& extruders = it_layer_tools->extruders; for (const auto& extruder : extruders) @@ -3508,7 +3505,10 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) auto it_start = ticks.lower_bound(TICK_CODE{tick}); auto it = it_start; - if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode) { + if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode && + tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { used_extruders.emplace(it->extruder); if (tick < it->tick) used_extruders.emplace(default_initial_extruder); @@ -3516,8 +3516,7 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) while (it != ticks.begin()) { --it; - if(it->gcode == Slic3r::ToolChangeCode) - { + if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) { used_extruders.emplace(it->extruder); break; } @@ -3526,12 +3525,9 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) if (it == ticks.begin() && used_extruders.empty()) used_extruders.emplace(default_initial_extruder); - it = it_start; - while (it != ticks.end()) { - if(it->gcode == Slic3r::ToolChangeCode) + for (it = it_start; it != ticks.end(); ++it) + if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) used_extruders.emplace(it->extruder); - ++it; - } return used_extruders; } @@ -3574,13 +3570,20 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); const bool is_color_change = it->gcode == Slic3r::ColorChangeCode; - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Edit pause print message")) : - _(L("Edit custom G-code")), "", - [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); + if (it->gcode == Slic3r::ToolChangeCode) + append_add_color_change_menu_item(&menu, true); + else + append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : + it->gcode == Slic3r::PausePrintCode ? _(L("Edit pause print message")) : + _(L("Edit custom G-code")), "", + [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); + + if (it->gcode == Slic3r::ColorChangeCode && m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu, true); append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Delete color change")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : + it->gcode == Slic3r::ToolChangeCode ? _(L("Delete tool change")) : + it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : _(L("Delete custom G-code")), "", [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); @@ -3645,13 +3648,23 @@ void DoubleSlider::add_code_as_tick(std::string code, int selected_extruder/* = return; const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - if (m_ticks.ticks.find(TICK_CODE{ tick }) != m_ticks.ticks.end() || // if on this Z doesn't exist tick - !check_ticks_changed_event(code)) + if ( !check_ticks_changed_event(code) ) return; const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); - - if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + const auto it = m_ticks.ticks.find(TICK_CODE{ tick }); + + if ( it == m_ticks.ticks.end() ) { + // try to add tick + if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + return; + } + else if (code == Slic3r::ToolChangeCode || code == Slic3r::ColorChangeCode) { + // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly + if (!m_ticks.switch_code_for_tick(it, code, extruder)) + return; + } + else return; post_ticks_changed_event(code); @@ -3679,7 +3692,10 @@ void DoubleSlider::add_current_tick(bool call_from_keyboard /*= false*/) else append_add_color_change_menu_item(&menu); - wxPoint pos = wxDefaultPosition; + wxPoint pos = wxDefaultPosition; + /* Menu position will be calculated from mouse click position, but... + * if function is called from keyboard (pressing "+"), we should to calculate it + * */ if (call_from_keyboard) { int width, height; @@ -3701,17 +3717,15 @@ void DoubleSlider::delete_current_tick() { if (m_selection == ssUndef) return; + auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; - if (it != m_ticks.ticks.end()) - { - if (!check_ticks_changed_event(it->gcode)) - return; - - const std::string code = it->gcode; - m_ticks.ticks.erase(it); - post_ticks_changed_event(code); - } + const std::string code = it->gcode; + m_ticks.ticks.erase(it); + post_ticks_changed_event(code); } void DoubleSlider::edit_tick() @@ -3719,7 +3733,7 @@ void DoubleSlider::edit_tick() const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; const std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ tick }); - if (it == m_ticks.ticks.end() || // if on this Z exists tick + if (it == m_ticks.ticks.end() || !check_ticks_changed_event(it->gcode)) return; @@ -3842,6 +3856,31 @@ bool DoubleSlider::check_ticks_changed_event(const std::string& gcode) return true; } + +std::string DoubleSlider::TICK_CODE_INFO::get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder) +{ + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::string color = colors[extruder - 1]; + + if (code == Slic3r::ColorChangeCode) + { + if (!ticks.empty()) + { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + } + return color; +} + bool DoubleSlider::TICK_CODE_INFO::add_tick(const int tick, std::string& code, const int extruder, double print_z) { std::string color; @@ -3864,28 +3903,10 @@ bool DoubleSlider::TICK_CODE_INFO::add_tick(const int tick, std::string& code, c } else { - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - color = colors[extruder - 1]; - - if (code == Slic3r::ColorChangeCode) - { - if (!ticks.empty()) - { - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), TICK_CODE{ tick }); - while (before_tick_it != ticks.begin()) { - --before_tick_it; - if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { - color = before_tick_it->color; - break; - } - } - } - - color = get_new_color(color); - if (color.empty()) - return false; - } - } + color = get_color_for_tick(TICK_CODE{ tick }, code, extruder); + if (color.empty()) + return false; + } ticks.emplace(TICK_CODE{ tick, code, extruder, color }); return true; @@ -3937,6 +3958,23 @@ void DoubleSlider::TICK_CODE_INFO::switch_code(const std::string& code_from, con ++it; } +bool DoubleSlider::TICK_CODE_INFO::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) +{ + const std::string color = get_color_for_tick(*it, code_to, extruder); + if (color.empty()) + return false; + + TICK_CODE changed_tick = *it; + changed_tick.gcode = code_to; + changed_tick.extruder = extruder; + changed_tick.color = color; + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + void DoubleSlider::TICK_CODE_INFO::erase_all_ticks_with_code(const std::string& gcode) { for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 8cae495ef..7511e8a19 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -910,8 +910,8 @@ private: void post_ticks_changed_event(const std::string& gcode = ""); bool check_ticks_changed_event(const std::string& gcode); - void append_change_extruder_menu_item(wxMenu*); - void append_add_color_change_menu_item(wxMenu*); + void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false); + void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); bool is_osx { false }; wxFont m_font; @@ -988,6 +988,7 @@ private: bool add_tick (const int tick, std::string &code, int extruder, double print_z); bool edit_tick (std::set::iterator it, double print_z); void switch_code(const std::string& code_from, const std::string& code_to); + bool switch_code_for_tick (std::set::iterator it, const std::string& code_to, const int extruder); void erase_all_ticks_with_code (const std::string& gcode); bool has_tick_with_code (const std::string& gcode); @@ -1002,6 +1003,8 @@ private: std::string pause_print_msg = ""; bool m_suppress_plus = false; bool m_suppress_minus = false; + + std::string get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder); } m_ticks; From 62cf800db69b78f27b1b3a34782aade8f51610c0 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 22 Jan 2020 17:05:26 +0100 Subject: [PATCH 21/28] adding constantness & removing unused code --- src/slic3r/GUI/RemovableDriveManager.cpp | 48 ++++++++++++------------ src/slic3r/GUI/RemovableDriveManager.hpp | 20 +++++----- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 4ed6d36a4..e5e228ef1 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -27,7 +27,9 @@ namespace Slic3r { namespace GUI { #if _WIN32 +/* currently not used, left for possible future use INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +*/ void RemovableDriveManager::search_for_drives() { m_current_drives.clear(); @@ -44,7 +46,7 @@ void RemovableDriveManager::search_for_drives() if (drive_type == DRIVE_REMOVABLE) { // get name of drive - std::wstring wpath = boost::nowide::widen(path);//std::wstring(path.begin(), path.end()); + std::wstring wpath = boost::nowide::widen(path); std::wstring volume_name; volume_name.resize(1024); std::wstring file_system_name; @@ -54,12 +56,6 @@ void RemovableDriveManager::search_for_drives() if(error != 0) { volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end()); - /* - if (volume_name == L"") - { - volume_name = L"REMOVABLE DRIVE"; - } - */ if (file_system_name != L"") { ULARGE_INTEGER free_space; @@ -147,6 +143,7 @@ void RemovableDriveManager::register_window() { //creates new unvisible window that is recieving callbacks from system // structure to register + /* currently not used, left for possible future use WNDCLASSEX wndClass; wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; @@ -182,13 +179,15 @@ void RemovableDriveManager::register_window() } //ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); + */ } - +/* currently not used, left for possible future use INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // here we need to catch messeges about device removal // problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device. //uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates + LRESULT lRet = 1; static HDEVNOTIFY hDeviceNotify; @@ -221,8 +220,9 @@ INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP break; } return lRet; + } - +*/ #else void RemovableDriveManager::search_for_drives() { @@ -370,7 +370,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) } return false; } -std::string RemovableDriveManager::get_drive_from_path(const std::string& path) +std::string RemovableDriveManager::get_drive_from_path(const std::string& path) { std::size_t found = path.find_last_of("/"); std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; @@ -440,7 +440,7 @@ bool RemovableDriveManager::update(const long time,const bool check) return !m_current_drives.empty(); } -bool RemovableDriveManager::is_drive_mounted(const std::string &path) +bool RemovableDriveManager::is_drive_mounted(const std::string &path) const { for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { @@ -451,7 +451,7 @@ bool RemovableDriveManager::is_drive_mounted(const std::string &path) } return false; } -std::string RemovableDriveManager::get_drive_path() +std::string RemovableDriveManager::get_drive_path() { if (m_current_drives.size() == 0) { @@ -462,17 +462,17 @@ std::string RemovableDriveManager::get_drive_path() return m_last_save_path; return m_current_drives.back().path; } -std::string RemovableDriveManager::get_last_save_path() +std::string RemovableDriveManager::get_last_save_path() const { if (!m_last_save_path_verified) return ""; return m_last_save_path; } -std::string RemovableDriveManager::get_last_save_name() +std::string RemovableDriveManager::get_last_save_name() const { return m_last_save_name; } -std::vector RemovableDriveManager::get_all_drives() +std::vector RemovableDriveManager::get_all_drives() const { return m_current_drives; } @@ -482,7 +482,7 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) + if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) { @@ -533,7 +533,7 @@ void RemovableDriveManager::verify_last_save_path() reset_last_save_path(); } } -std::string RemovableDriveManager::get_drive_name(const std::string& path) +std::string RemovableDriveManager::get_drive_name(const std::string& path) const { if (m_current_drives.size() == 0) return ""; @@ -546,7 +546,7 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path) } return ""; } -bool RemovableDriveManager::is_last_drive_removed() +bool RemovableDriveManager::is_last_drive_removed() { if(!m_last_save_path_verified) { @@ -578,27 +578,27 @@ void RemovableDriveManager::set_is_writing(const bool b) m_did_eject = false; } } -bool RemovableDriveManager::get_is_writing() +bool RemovableDriveManager::get_is_writing() const { return m_is_writing; } -bool RemovableDriveManager::get_did_eject() +bool RemovableDriveManager::get_did_eject() const { return m_did_eject; } -void RemovableDriveManager::set_did_eject(const bool b) +void RemovableDriveManager::set_did_eject(const bool b) { m_did_eject = b; } -size_t RemovableDriveManager::get_drives_count() +size_t RemovableDriveManager::get_drives_count() const { return m_current_drives.size(); } -std::string RemovableDriveManager::get_ejected_path() +std::string RemovableDriveManager::get_ejected_path() const { return m_ejected_path; } -std::string RemovableDriveManager::get_ejected_name() +std::string RemovableDriveManager::get_ejected_name() const { return m_ejected_name; } diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 6bd90e98c..032eef682 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -35,14 +35,14 @@ public: void init(); //update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime() bool update(const long time = 0,const bool check = false); - bool is_drive_mounted(const std::string &path); + bool is_drive_mounted(const std::string &path) const; void eject_drive(const std::string &path); //returns path to last drive which was used, if none was used, returns device that was enumerated last - std::string get_last_save_path(); - std::string get_last_save_name(); + std::string get_last_save_path() const; + std::string get_last_save_name() const; //returns path to last drive which was used, if none was used, returns empty string std::string get_drive_path(); - std::vector get_all_drives(); + std::vector get_all_drives() const; bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed void add_remove_callback(std::function callback); @@ -59,13 +59,13 @@ public: // param as update() bool is_last_drive_removed_with_update(const long time = 0); void set_is_writing(const bool b); - bool get_is_writing(); - bool get_did_eject(); + bool get_is_writing() const; + bool get_did_eject() const; void set_did_eject(const bool b); - std::string get_drive_name(const std::string& path); - size_t get_drives_count(); - std::string get_ejected_path(); - std::string get_ejected_name(); + std::string get_drive_name(const std::string& path) const; + size_t get_drives_count() const; + std::string get_ejected_path() const; + std::string get_ejected_name() const; private: RemovableDriveManager(); void search_for_drives(); From 71fa41110049a79ec1d5c3052d22647a38d01bed Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 09:53:06 +0100 Subject: [PATCH 22/28] Refactored PrintObject::m_copies to PrintInstances, so that the ordering code at G-code export may work directly with pointers to PrintInstances instead of with pair of . Also the PrintInstance knows its source ModelInstance, which allows sorting of PrintInstances for sequential printing in the order they appear in Plater's object list. --- src/libslic3r/GCode.cpp | 230 +++++++++++++-------------- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/PrintExtents.cpp | 4 +- src/libslic3r/GCode/ToolOrdering.cpp | 6 +- src/libslic3r/Print.cpp | 97 ++++++----- src/libslic3r/Print.hpp | 32 +++- src/libslic3r/PrintObject.cpp | 38 ++--- src/libslic3r/ShortestPath.cpp | 14 +- src/libslic3r/ShortestPath.hpp | 3 +- src/slic3r/GUI/GLCanvas3D.cpp | 7 +- 10 files changed, 226 insertions(+), 209 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 128ddca17..decadafdf 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -165,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP cnt = (cnt + 1) / 2; } // And collect copies of the objects. - for (const Point © : object->copies()) { + for (const PrintInstance &instance : object->instances()) { // All the layers were reduced to the 1st item of polygons_per_layer. size_t i = islands.size(); polygons_append(islands, polygons_per_layer.front()); for (; i < islands.size(); ++ i) - islands[i].translate(copy); + islands[i].translate(instance.shift); } } return islands; @@ -1088,49 +1088,38 @@ namespace DoExport { } // Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints. -static inline std::vector sort_objects_by_z(const Print &print) +static inline std::vector sort_object_instances_by_max_z(const Print &print) { std::vector objects(print.objects().begin(), print.objects().end()); - std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); }); - return objects; + std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); }); + std::vector instances; + instances.reserve(objects.size()); + for (const PrintObject *object : objects) + for (size_t i = 0; i < object->instances().size(); ++ i) + instances.emplace_back(&object->instances()[i]); + return instances; } // Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model(). -static inline std::vector sort_objects_by_model_order(const Print &print) +static inline std::vector sort_object_instances_by_model_order(const Print &print) { - const Model &model = print.model(); - // Pair ModelObjects with PrintObjects, remember the order of ModelObjects in the model above. - struct ModelObjectOrder { - const ModelObject *model_object; - const PrintObject *print_object; - size_t order; - }; - // Initialize model_object_order with ModelObjects and their order. - std::vector model_object_order; - model_object_order.reserve(model.objects.size()); - { - size_t order = 0; - for (const ModelObject *model_object : model.objects) - model_object_order.emplace_back(ModelObjectOrder{ model_object, nullptr, order ++ }); - } - // Sort by pointer to ModelObject. - std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.model_object < rhs.model_object; }); - // Assign PrintObject pointer to ModelObject. - for (const PrintObject *print_object : print.objects()) { - auto it = Slic3r::lower_bound_by_predicate(model_object_order.begin(), model_object_order.end(), [print_object](const ModelObjectOrder &model_object_order) { return model_object_order.model_object < print_object->model_object(); }); - // The non-printable objects (objects outside of the print volume or suppressed objects) will have no partner in the print.objects() list. - if (it != model_object_order.end() && it->model_object == print_object->model_object()) - it->print_object = print_object; - } - // Sort back to the initial order. - std::sort(model_object_order.begin(), model_object_order.end(), [](const ModelObjectOrder &lhs, const ModelObjectOrder &rhs) { return lhs.order < rhs.order; }); - // Produce the output vector of PrintObjects, sorted by the order of ModelObjects in Model. - std::vector objects; - objects.reserve(model_object_order.size()); - for (ModelObjectOrder &order : model_object_order) - if (order.print_object != nullptr) - objects.emplace_back(order.print_object); - return objects; + // Build up map from ModelInstance* to PrintInstance* + std::vector> model_instance_to_print_instance; + model_instance_to_print_instance.reserve(print.num_object_instances()); + for (const PrintObject *print_object : print.objects()) + for (const PrintInstance &print_instance : print_object->instances()) + model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance); + std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first < r.first; }); + + std::vector instances; + instances.reserve(model_instance_to_print_instance.size()); + for (const ModelObject *model_object : print.model().objects) + for (const ModelInstance *model_instance : model_object->instances) { + auto it = std::lower_bound(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), std::make_pair(model_instance, nullptr), [](auto &l, auto &r) { return l.first < r.first; }); + if (it != model_instance_to_print_instance.end() && it->first == model_instance) + instances.emplace_back(it->second); + } + return instances; } #if ENABLE_THUMBNAIL_GENERATOR @@ -1164,7 +1153,7 @@ void GCode::_do_export(Print& print, FILE* file) for (auto layer : object->support_layers()) zs.push_back(layer->print_z); std::sort(zs.begin(), zs.end()); - m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); + m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } } else { // Print all objects with the same print_z together. @@ -1257,13 +1246,18 @@ void GCode::_do_export(Print& print, FILE* file) ToolOrdering tool_ordering; unsigned int initial_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1; - size_t initial_print_object_id = 0; bool has_wipe_tower = false; + std::vector print_object_instances_ordering; + std::vector::const_iterator print_object_instance_sequential_active; if (print.config().complete_objects.value) { + // Order object instances for sequential print. + print_object_instances_ordering = sort_object_instances_by_model_order(print); +// print_object_instances_ordering = sort_object_instances_by_max_z(print); // Find the 1st printing object, find its tool ordering and the initial extruder ID. - for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) { - tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id); - if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) + print_object_instance_sequential_active = print_object_instances_ordering.begin(); + for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) { + tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id); + if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast(-1)) break; } // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. @@ -1283,6 +1277,8 @@ void GCode::_do_export(Print& print, FILE* file) // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // Therefore initialize the printing extruders from there. this->set_extruders(tool_ordering.all_extruders()); + // Order object instances using a nearest neighbor search. + print_object_instances_ordering = chain_print_object_instances(print); } if (initial_extruder_id == (unsigned int)-1) { // Nothing to print! @@ -1363,72 +1359,64 @@ void GCode::_do_export(Print& print, FILE* file) // Do all objects for each layer. if (print.config().complete_objects.value) { - // Print objects from the smallest to the tallest to avoid collisions - // when moving onto next object starting point. - std::vector objects = sort_objects_by_model_order(print); -// std::vector objects = sort_objects_by_z(print); size_t finished_objects = 0; - for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { - const PrintObject &object = *objects[object_id]; - for (const Point © : object.copies()) { - // Get optimal tool ordering to minimize tool switches of a multi-exruder print. - if (object_id != initial_print_object_id || © != object.copies().data()) { - // Don't initialize for the first object and first copy. - tool_ordering = ToolOrdering(object, final_extruder_id); - unsigned int new_extruder_id = tool_ordering.first_extruder(); - if (new_extruder_id == (unsigned int)-1) - // Skip this object. - continue; - initial_extruder_id = new_extruder_id; - final_extruder_id = tool_ordering.last_extruder(); - assert(final_extruder_id != (unsigned int)-1); - } - print.throw_if_canceled(); - this->set_origin(unscale(copy)); - if (finished_objects > 0) { - // Move to the origin position for the copy we're going to print. - // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. - m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer - m_avoid_crossing_perimeters.use_external_mp_once = true; - _write(file, this->retract()); - _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); - m_enable_cooling_markers = true; - // Disable motion planner when traveling to first object point. - m_avoid_crossing_perimeters.disable_once = true; - // Ff we are printing the bottom layer of an object, and we have already finished - // another one, set first layer temperatures. This happens before the Z move - // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); - std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); - // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. - this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); - this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); - _writeln(file, between_objects_gcode); - } - // Reset the cooling buffer internal state (the current position, feed rate, accelerations). - m_cooling_buffer->reset(); - m_cooling_buffer->set_current_extruder(initial_extruder_id); - // Pair the object layers with the support layers by z, extrude them. - std::vector layers_to_print = collect_layers_to_print(object); - for (const LayerToPrint <p : layers_to_print) { - std::vector lrs; - lrs.emplace_back(std::move(ltp)); - this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, © - object.copies().data()); - print.throw_if_canceled(); - } -#ifdef HAS_PRESSURE_EQUALIZER - if (m_pressure_equalizer) - _write(file, m_pressure_equalizer->process("", true)); -#endif /* HAS_PRESSURE_EQUALIZER */ - ++ finished_objects; - // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. - // Reset it when starting another object from 1st layer. - m_second_layer_things_done = false; + const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object; + for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) { + const PrintObject &object = *(*print_object_instance_sequential_active)->print_object; + if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) { + tool_ordering = ToolOrdering(object, final_extruder_id); + unsigned int new_extruder_id = tool_ordering.first_extruder(); + if (new_extruder_id == (unsigned int)-1) + // Skip this object. + continue; + initial_extruder_id = new_extruder_id; + final_extruder_id = tool_ordering.last_extruder(); + assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); + this->set_origin(unscale((*print_object_instance_sequential_active)->shift)); + if (finished_objects > 0) { + // Move to the origin position for the copy we're going to print. + // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. + m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer + m_avoid_crossing_perimeters.use_external_mp_once = true; + _write(file, this->retract()); + _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + m_enable_cooling_markers = true; + // Disable motion planner when traveling to first object point. + m_avoid_crossing_perimeters.disable_once = true; + // Ff we are printing the bottom layer of an object, and we have already finished + // another one, set first layer temperatures. This happens before the Z move + // is triggered, so machine has more time to reach such temperatures. + m_placeholder_parser.set("current_object_idx", int(finished_objects)); + std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); + // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. + this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); + this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); + _writeln(file, between_objects_gcode); + } + // Reset the cooling buffer internal state (the current position, feed rate, accelerations). + m_cooling_buffer->reset(); + m_cooling_buffer->set_current_extruder(initial_extruder_id); + // Pair the object layers with the support layers by z, extrude them. + std::vector layers_to_print = collect_layers_to_print(object); + for (const LayerToPrint <p : layers_to_print) { + std::vector lrs; + lrs.emplace_back(std::move(ltp)); + this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, *print_object_instance_sequential_active - object.instances().data()); + print.throw_if_canceled(); + } +#ifdef HAS_PRESSURE_EQUALIZER + if (m_pressure_equalizer) + _write(file, m_pressure_equalizer->process("", true)); +#endif /* HAS_PRESSURE_EQUALIZER */ + ++ finished_objects; + // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. + // Reset it when starting another object from 1st layer. + m_second_layer_things_done = false; + prev_object = &object; } } else { - // Order object instances using a nearest neighbor search. - std::vector> print_object_instances_ordering = chain_print_object_instances(print); // Sort layers by Z. // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); @@ -1730,12 +1718,12 @@ inline std::vector& object_islands_by_extruder( } std::vector GCode::sort_print_object_instances( - std::vector &objects_by_extruder, - const std::vector &layers, + std::vector &objects_by_extruder, + const std::vector &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. - const size_t single_object_instance_idx) + const size_t single_object_instance_idx) { std::vector out; @@ -1762,13 +1750,13 @@ std::vector GCode::sort_print_object_instances( if (! sorted.empty()) { const Print &print = *sorted.front().first->print(); out.reserve(sorted.size()); - for (const std::pair &instance_id : *ordering) { - const PrintObject &print_object = *print.objects()[instance_id.first]; + for (const PrintInstance *instance : *ordering) { + const PrintObject &print_object = *instance->print_object; std::pair key(&print_object, nullptr); auto it = std::lower_bound(sorted.begin(), sorted.end(), key); if (it != sorted.end() && it->first == &print_object) // ObjectByExtruder for this PrintObject was found. - out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second); + out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data()); } } } @@ -1912,16 +1900,16 @@ namespace Skirt { // and performing the extruder specific extrusions together. void GCode::process_layer( // Write into the output file. - FILE *file, - const Print &print, + FILE *file, + const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. - const std::vector &layers, - const LayerTools &layer_tools, + const std::vector &layers, + const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. - const size_t single_object_instance_idx) + const size_t single_object_instance_idx) { assert(! layers.empty()); // assert(! layer_tools.extruders.empty()); @@ -2130,7 +2118,7 @@ void GCode::process_layer( // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) correct_extruder_id = layer_tools.extruders.back(); } - entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size()); if (entity_overrides == nullptr) { printing_extruders.emplace_back(correct_extruder_id); } else { @@ -2244,7 +2232,7 @@ void GCode::process_layer( if (this->config().gcode_label_objects) gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; // When starting a new object, use the external motion planner for the first travel move. - const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id]; + const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; std::pair this_object_copy(&instance_to_print.print_object, offset); if (m_last_obj_copy != this_object_copy) m_avoid_crossing_perimeters.use_external_mp_once = true; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 0344924a1..10463277b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -226,7 +226,7 @@ private: const std::vector &layers, const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx = size_t(-1)); @@ -300,7 +300,7 @@ private: // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const std::vector &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. const size_t single_object_instance_idx); diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 1fedcf3f0..7a8271e30 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object if (support_layer) for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) bbox_this.merge(extrusionentity_extents(extrusion_entity)); - for (const Point &offset : print_object.copies()) { + for (const PrintInstance &instance : print_object.instances()) { BoundingBoxf bbox_translated(bbox_this); - bbox_translated.translate(unscale(offset)); + bbox_translated.translate(unscale(instance.shift)); bbox.merge(bbox_translated); } } diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 3e01e2594..3b6bf87af 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -491,7 +491,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) { // The custom G-code applies to the current layer. if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode || - (custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)])) + (custom_gcode.extruder <= int(num_extruders) && extruder_printing_above[unsigned(custom_gcode.extruder - 1)])) // If it is color change, it will actually be useful as the exturder above will print. lt.custom_gcode = &custom_gcode; // Consume that custom G-code event. @@ -602,7 +602,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); if (this_layer == nullptr) continue; - size_t num_of_copies = object->copies().size(); + size_t num_of_copies = object->instances().size(); // 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) { @@ -677,7 +677,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); if (this_layer == nullptr) continue; - size_t num_of_copies = object->copies().size(); + 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) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1cb16b4de..a41122dfa 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -326,7 +326,7 @@ unsigned int Print::num_object_instances() const { unsigned int instances = 0; for (const PrintObject *print_object : m_objects) - instances += (unsigned int)print_object->copies().size(); + instances += (unsigned int)print_object->instances().size(); return instances; } @@ -447,33 +447,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d & return true; } -struct PrintInstances +struct PrintObjectTrafoAndInstances { - Transform3d trafo; - Points copies; - bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } + 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) +static std::vector print_objects_from_model_object(const ModelObject &model_object) { - std::set trafos; - PrintInstances trafo; - trafo.copies.assign(1, Point()); + 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]); // Set the Z axis of the transformation. - trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); trafo.trafo.data()[12] = 0; trafo.trafo.data()[13] = 0; - auto it = trafos.find(trafo); - if (it == trafos.end()) - trafos.emplace(trafo); - else - const_cast(*it).copies.emplace_back(trafo.copies.front()); + // 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()); + return std::vector(trafos.begin(), trafos.end()); } // Compare just the layer ranges and their layer heights, not the associated configs. @@ -891,12 +888,27 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // 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; - 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); - } + // 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 { + // Just synchronize the content of the instances. This avoids memory allocation and it does not invalidate ModelInstance pointers, + // which may be accessed by G-code export in the meanwhile to deduce sequential print order. + 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; + } + } } } @@ -917,13 +929,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // Generate a list of trafos and XY offsets for instances of a ModelObject PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders); - std::vector new_print_instances = print_objects_from_model_object(*model_object); + std::vector new_print_instances = print_objects_from_model_object(*model_object); if (old.empty()) { // Simple case, just generate new instances. - for (const PrintInstances &print_instances : new_print_instances) { + for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) { PrintObject *print_object = new PrintObject(this, model_object, false); - print_object->set_trafo(print_instances.trafo); - print_object->set_copies(print_instances.copies); + print_object->set_trafo_and_instances(print_instances.trafo, std::move(print_instances.instances)); print_object->config_apply(config); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); @@ -936,13 +947,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ 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 (const PrintInstances &new_instances : new_print_instances) { + 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, false); - print_object->set_trafo(new_instances.trafo); - print_object->set_copies(new_instances.copies); + print_object->set_trafo_and_instances(new_instances.trafo, std::move(new_instances.instances)); print_object->config_apply(config); print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); @@ -951,7 +961,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ const_cast(*it_old)->status = PrintObjectStatus::Deleted; } else { // The PrintObject already exists and the copies differ. - PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies); + 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); @@ -1159,7 +1169,7 @@ std::string Print::validate() const Polygons convex_hulls_other; for (const PrintObject *print_object : m_objects) { assert(! print_object->model_object()->instances.empty()); - assert(! print_object->copies().empty()); + assert(! print_object->instances().empty()); // Get convex hull of all meshes assigned to this print object. ModelInstance *model_instance0 = print_object->model_object()->instances.front(); Vec3d rotation = model_instance0->get_rotation(); @@ -1174,9 +1184,9 @@ std::string Print::validate() const Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())), float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front(); // Now we check that no instance of convex_hull intersects any of the previously checked object instances. - for (const Point © : print_object->copies()) { + for (const PrintInstance &instance : print_object->instances()) { Polygon convex_hull = convex_hull0; - convex_hull.translate(copy); + convex_hull.translate(instance.shift); if (! intersection(convex_hulls_other, convex_hull).empty()) return L("Some objects are too close; your extruder will collide with them."); polygons_append(convex_hulls_other, convex_hull); @@ -1187,7 +1197,7 @@ std::string Print::validate() const { std::vector object_height; for (const PrintObject *object : m_objects) - object_height.insert(object_height.end(), object->copies().size(), object->size(2)); + object_height.insert(object_height.end(), object->instances().size(), object->size(2)); std::sort(object_height.begin(), object_height.end()); // Ignore the tallest *copy* (this is why we repeat height for all of them): // it will be printed as last one so its height doesn't matter. @@ -1200,7 +1210,7 @@ std::string Print::validate() const if (m_config.spiral_vase) { size_t total_copies_count = 0; for (const PrintObject *object : m_objects) - total_copies_count += object->copies().size(); + total_copies_count += object->instances().size(); // #4043 if (total_copies_count > 1 && ! m_config.complete_objects.value) return L("The Spiral Vase option can only be used when printing a single object."); @@ -1417,10 +1427,9 @@ BoundingBox Print::bounding_box() const { BoundingBox bb; for (const PrintObject *object : m_objects) - for (Point copy : object->m_copies) { - bb.merge(copy); - copy += to_2d(object->size); - bb.merge(copy); + for (const PrintInstance &instance : object->instances()) { + bb.merge(instance.shift); + bb.merge(instance.shift + to_2d(object->size)); } return bb; } @@ -1657,10 +1666,10 @@ void Print::_make_skirt() append(object_points, extrusion_entity->as_polyline().points); } // Repeat points for each object copy. - for (const Point &shift : object->m_copies) { + for (const PrintInstance &instance : object->instances()) { Points copy_points = object_points; for (Point &pt : copy_points) - pt += shift; + pt += instance.shift; append(points, copy_points); } } @@ -1778,11 +1787,11 @@ void Print::_make_brim() object_islands.push_back(expoly.contour); if (! object->support_layers().empty()) object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); - islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); - for (const Point &pt : object->m_copies) + islands.reserve(islands.size() + object_islands.size() * object->instances().size()); + for (const PrintInstance &instance : object->instances()) for (Polygon &poly : object_islands) { islands.push_back(poly); - islands.back().translate(pt); + islands.back().translate(instance.shift); } } Polygons loops; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 53a6f97b9..2d83da43d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -92,6 +92,21 @@ typedef std::vector LayerPtrs; typedef std::vector SupportLayerPtrs; class BoundingBoxf3; // TODO: for temporary constructor parameter +// Single instance of a PrintObject. +// As multiple PrintObjects may be generated for a single ModelObject (their instances differ in rotation around Z), +// ModelObject's instancess will be distributed among these multiple PrintObjects. +struct PrintInstance +{ + // Parent PrintObject + PrintObject *print_object; + // Source ModelInstance of a ModelObject, for which this print_object was created. + const ModelInstance *model_instance; + // Shift of this instance towards its PrintObject + Point shift; +}; + +typedef std::vector PrintInstances; + class PrintObject : public PrintObjectBaseWithState { private: // Prevents erroneous use by other classes. @@ -111,8 +126,8 @@ public: const LayerPtrs& layers() const { return m_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; } const Transform3d& trafo() const { return m_trafo; } - const Points& copies() const { return m_copies; } - const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } + const PrintInstances& instances() const { return m_instances; } + const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } // since the object is aligned to origin, bounding box coincides with size BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } @@ -126,9 +141,9 @@ public: // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id // since they have different semantics. - size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } - size_t layer_count() const { return m_layers.size(); } - void clear_layers(); + size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } + size_t layer_count() const { return m_layers.size(); } + void clear_layers(); const Layer* get_layer(int idx) const { return m_layers[idx]; } Layer* get_layer(int idx) { return m_layers[idx]; } // Get a layer exactly at print_z. @@ -177,7 +192,7 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } -protected: +private: // to be called from Print only. friend class Print; @@ -187,7 +202,8 @@ protected: 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 set_trafo(const Transform3d& trafo) { m_trafo = trafo; } - PrintBase::ApplyStatus set_copies(const Points &points); + PrintBase::ApplyStatus set_instances(PrintInstances &&instances); + void set_trafo_and_instances(const Transform3d& trafo, PrintInstances &&instances) { this->set_trafo(trafo); this->set_instances(std::move(instances)); } // Invalidates the step, and its depending steps in PrintObject and Print. bool invalidate_step(PrintObjectStep step); // Invalidates all PrintObject and Print steps. @@ -223,7 +239,7 @@ private: // Translation in Z + Rotation + Scaling / Mirroring. Transform3d m_trafo = Transform3d::Identity(); // Slic3r::Point objects in scaled G-code coordinates - Points m_copies; + std::vector m_instances; // scaled coordinates to add to copies (to compensate for the alignment // operated when creating the object but still preserving a coherent API // for external callers) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8a59b6c3b..adae28e4b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -60,32 +60,32 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta } if (add_instances) { - Points copies; - copies.reserve(m_model_object->instances.size()); + PrintInstances instances; + instances.reserve(m_model_object->instances.size()); for (const ModelInstance *mi : m_model_object->instances) { assert(mi->is_printable()); - const Vec3d& offset = mi->get_offset(); - copies.emplace_back(Point::new_scale(offset(0), offset(1))); + const Vec3d &offset = mi->get_offset(); + instances.emplace_back(PrintInstance{ nullptr, mi, Point::new_scale(offset(0), offset(1)) }); } - this->set_copies(copies); + this->set_instances(std::move(instances)); } } -PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) +PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) { - // Order copies with a nearest-neighbor search. - std::vector copies; - copies.reserve(points.size()); - for (const Point &pt : points) - copies.emplace_back(pt + m_copies_shift); // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; - if (copies != m_copies) { + bool equal_length = instances.size() == m_instances.size(); + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); + if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) || - (copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower))) + (! equal_length && m_print->invalidate_step(psWipeTower))) status = PrintBase::APPLY_STATUS_INVALIDATED; - m_copies = copies; + m_instances = instances; + for (PrintInstance &i : m_instances) + i.print_object = this; } return status; } @@ -669,7 +669,7 @@ void PrintObject::detect_surfaces_type() m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; Layer *layer = m_layers[idx_layer]; - LayerRegion *layerm = layer->get_region(idx_region); + LayerRegion *layerm = layer->m_regions[idx_region]; // 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; @@ -684,7 +684,7 @@ void PrintObject::detect_surfaces_type() Surfaces top; if (upper_layer) { Polygons upper_slices = interface_shells ? - to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) : + to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) : to_polygons(upper_layer->lslices); surfaces_append(top, //FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice. @@ -727,7 +727,7 @@ void PrintObject::detect_surfaces_type() offset2_ex( diff( intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported - to_polygons(lower_layer->get_region(idx_region)->slices.surfaces), + to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces), true), -offset, offset), stBottom); @@ -796,7 +796,7 @@ void PrintObject::detect_surfaces_type() if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) - m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); + m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]); } BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; @@ -806,7 +806,7 @@ void PrintObject::detect_surfaces_type() [this, idx_region, interface_shells, &surfaces_new](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]->get_region(idx_region); + LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region]; layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index e9df4c5b5..a5d3a41ab 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1946,24 +1946,26 @@ ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const Clippe return chain_path_items(points, items); } -std::vector> chain_print_object_instances(const Print &print) +std::vector chain_print_object_instances(const Print &print) { // Order objects using a nearest neighbor search. Points object_reference_points; std::vector> instances; for (size_t i = 0; i < print.objects().size(); ++ i) { const PrintObject &object = *print.objects()[i]; - for (size_t j = 0; j < object.copies().size(); ++ j) { - object_reference_points.emplace_back(object.copy_center(j)); + for (size_t j = 0; j < object.instances().size(); ++ j) { + object_reference_points.emplace_back(object.instance_center(j)); instances.emplace_back(i, j); } } auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; }; std::vector> ordered = chain_segments_greedy(segment_end_point, instances.size(), nullptr); - std::vector> out; + std::vector out; out.reserve(instances.size()); - for (auto &segment_and_reversal : ordered) - out.emplace_back(instances[segment_and_reversal.first]); + for (auto &segment_and_reversal : ordered) { + const std::pair &inst = instances[segment_and_reversal.first]; + out.emplace_back(&print.objects()[inst.first]->instances()[inst.second]); + } return out; } diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index cd342015d..65d8b7f23 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -30,7 +30,8 @@ std::vector chain_clipper_polynodes(const Points &points // Chain instances of print objects by an approximate shortest path. // Returns pairs of PrintObject idx and instance of that PrintObject. class Print; -std::vector> chain_print_object_instances(const Print &print); +struct PrintInstance; +std::vector chain_print_object_instances(const Print &print); } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7cceb1a5f..9a5cea511 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5251,7 +5251,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c struct Ctxt { - const Points *shifted_copies; + const PrintInstances *shifted_copies; std::vector layers; bool has_perimeters; bool has_infill; @@ -5384,7 +5384,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c ctxt.is_single_material_print = this->fff_print()->extruders().size()==1; ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt(); - ctxt.shifted_copies = &print_object.copies(); + ctxt.shifted_copies = &print_object.instances(); // order layers by print_z { @@ -5473,7 +5473,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->offsets.push_back(vol->indexed_vertex_array.quad_indices.size()); vol->offsets.push_back(vol->indexed_vertex_array.triangle_indices.size()); } - for (const Point © : *ctxt.shifted_copies) { + for (const PrintInstance &instance : *ctxt.shifted_copies) { + const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { if (is_selected_separate_extruder) { From 4f8f6196864770f01f33d16c0d147f61bc3cbdd1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 10:35:03 +0100 Subject: [PATCH 23/28] Fixed Perl bindings after refactoring. --- xs/xsp/Print.xsp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 74c0f4b0c..2e329bfa6 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -48,12 +48,8 @@ _constant() Ref model_object(); Ref config() %code%{ RETVAL = &THIS->config(); %}; - Points copies(); Clone bounding_box(); - Points _shifted_copies() - %code%{ RETVAL = THIS->copies(); %}; - size_t layer_count(); Ref get_layer(int idx); From 3d9ac0ada19107c20c75ea6fa21336276cd2b935 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 16:04:17 +0100 Subject: [PATCH 24/28] Little optimization of the profile aliases. --- src/slic3r/GUI/Preset.cpp | 27 ++++++++++++++++++--------- src/slic3r/GUI/Preset.hpp | 9 +++++++-- src/slic3r/GUI/PresetBundle.cpp | 7 +++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 797fa28f8..081b886d1 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -600,6 +600,7 @@ void PresetCollection::reset(bool delete_files) m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end()); this->select_preset(0); } + m_map_alias_to_profile_name.clear(); m_map_system_profile_renamed.clear(); } @@ -944,15 +945,15 @@ PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const P const std::string& PresetCollection::get_preset_name_by_alias(const std::string& alias) const { - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset& preset = this->m_presets[i]; - if (!preset.is_visible || (!preset.is_compatible && i != m_idx_selected)) - continue; - - if (preset.alias == alias) - return preset.name; - } - + for ( + // Find the 1st profile name with the alias. + auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto &l){ return l.first < alias; }); + // Continue over all profile names with the same alias. + it != m_map_alias_to_profile_name.end() && it->first == alias; ++ it) + if (auto it_preset = this->find_preset_internal(it->second); + it_preset != m_presets.end() && it_preset->name == it->second && + it_preset->is_visible && (it_preset->is_compatible || (it_preset - m_presets.begin()) == m_idx_selected)) + return it_preset->name; return alias; } @@ -1426,6 +1427,14 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe return duplicates; } +void PresetCollection::update_map_alias_to_profile_name() +{ + m_map_alias_to_profile_name.clear(); + for (const Preset &preset : m_presets) + m_map_alias_to_profile_name.emplace_back(preset.alias, preset.name); + std::sort(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [](auto &l, auto &r) { return l.first < r.first; }); +} + void PresetCollection::update_map_system_profile_renamed() { m_map_system_profile_renamed.clear(); diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 742d3e01d..c85933520 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -166,7 +166,7 @@ public: DynamicPrintConfig config; // Alias of the preset - std::string alias = ""; + std::string alias; // List of profile names, from which this profile was renamed at some point of time. // This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf, // and to match the "inherits" field of user profiles with updated system profiles. @@ -356,7 +356,7 @@ public: // used to update preset_choice from Tab const std::deque& get_presets() const { return m_presets; } - int get_idx_selected() { return m_idx_selected; } + int get_idx_selected() { return m_idx_selected; } static const std::string& get_suffix_modified(); // Return a preset possibly with modifications. @@ -475,6 +475,9 @@ protected: // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetCollection &&other, const VendorMap &new_vendors); + // Update m_map_alias_to_profile_name from loaded system profiles. + void update_map_alias_to_profile_name(); + // Update m_map_system_profile_renamed from loaded system profiles. void update_map_system_profile_renamed(); @@ -522,6 +525,8 @@ private: // Use deque to force the container to allocate an object per each entry, // so that the addresses of the presets don't change during resizing of the container. std::deque m_presets; + // System profiles may have aliases. Map to the full profile name. + std::vector> m_map_alias_to_profile_name; // Map from old system profile name to a current system profile name. 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. diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index bb08ce2d0..7e823f925 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -288,11 +288,18 @@ std::string PresetBundle::load_system_presets() // No config bundle loaded, reset. this->reset(false); } + this->prints .update_map_system_profile_renamed(); this->sla_prints .update_map_system_profile_renamed(); this->filaments .update_map_system_profile_renamed(); this->sla_materials.update_map_system_profile_renamed(); this->printers .update_map_system_profile_renamed(); + + this->prints .update_map_alias_to_profile_name(); + this->sla_prints .update_map_alias_to_profile_name(); + this->filaments .update_map_alias_to_profile_name(); + this->sla_materials.update_map_alias_to_profile_name(); + return errors_cummulative; } From 1ed313ab7983bed9a1911da153ce2e3e0b473d6f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 23 Jan 2020 12:49:39 +0100 Subject: [PATCH 25/28] Synchronized mode of color_print data to/from 3mf/amf + Code refactoring " CustomGCode extracted to separate namespace and file --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/CustomGCode.cpp | 72 +++++++++++++++++++++++++ src/libslic3r/CustomGCode.hpp | 81 ++++++++++++++++++++++++++++ src/libslic3r/Format/3mf.cpp | 20 +++++-- src/libslic3r/Format/AMF.cpp | 47 ++++++++++++---- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode/ToolOrdering.cpp | 6 +-- src/libslic3r/GCode/ToolOrdering.hpp | 2 +- src/libslic3r/Model.cpp | 41 ++------------ src/libslic3r/Model.hpp | 52 +----------------- src/libslic3r/Print.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 20 +++---- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 8 +-- src/slic3r/GUI/GUI_Preview.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/PresetBundle.cpp | 2 +- src/slic3r/GUI/wxExtensions.cpp | 8 +-- src/slic3r/GUI/wxExtensions.hpp | 6 +-- 19 files changed, 247 insertions(+), 132 deletions(-) create mode 100644 src/libslic3r/CustomGCode.cpp create mode 100644 src/libslic3r/CustomGCode.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c8e259caa..1c110d7cd 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -114,6 +114,8 @@ add_library(libslic3r STATIC Line.hpp Model.cpp Model.hpp + CustomGCode.cpp + CustomGCode.hpp Arrange.hpp Arrange.cpp MotionPlanner.cpp diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp new file mode 100644 index 000000000..7c505c978 --- /dev/null +++ b/src/libslic3r/CustomGCode.cpp @@ -0,0 +1,72 @@ +#include "CustomGCode.hpp" +#include "Config.hpp" +#include "GCode/PreviewData.hpp" +#include "GCodeWriter.hpp" + +namespace Slic3r { + +namespace CustomGCode { + +// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), +// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format +// then CustomGCode::Info.gcodes should be updated considering this option. +extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config) +{ + auto *colorprint_heights = config->option("colorprint_heights"); + if (colorprint_heights == nullptr) + return; + if (info.gcodes.empty() && ! colorprint_heights->values.empty()) { + // Convert the old colorprint_heighs only if there is no equivalent data in a new format. + const std::vector& colors = GCodePreviewData::ColorPrintColors(); + const auto& colorprint_values = colorprint_heights->values; + info.gcodes.clear(); + info.gcodes.reserve(colorprint_values.size()); + int i = 0; + for (auto val : colorprint_values) + info.gcodes.emplace_back(Item{ val, ColorChangeCode, 1, colors[(++i)%7] }); + + info.mode = SingleExtruder; + } + + // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted + // to a new format and therefore it shall be erased. + config->erase("colorprint_heights"); +} + +// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined. +// So, we should set CustomGCode::Info.mode should be updated considering code values from items. +extern void check_mode_for_custom_gcode_per_print_z(Info& info) +{ + if (info.mode != Undef) + return; + + bool is_single_extruder = true; + for (auto item : info.gcodes) + { + if (item.gcode == ToolChangeCode) { + info.mode = MultiAsSingle; + return; + } + if (item.gcode == ColorChangeCode && item.extruder > 1) + is_single_extruder = false; + } + + info.mode = is_single_extruder ? SingleExtruder : MultiExtruder; +} + +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// print_z corresponds to the first layer printed with the new extruder. +std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders) +{ + std::vector> custom_tool_changes; + for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes) + if (custom_gcode.gcode == ToolChangeCode) { + // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders + custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); + } + return custom_tool_changes; +} + +} // namespace CustomGCode + +} // namespace Slic3r diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp new file mode 100644 index 000000000..4f1be3f31 --- /dev/null +++ b/src/libslic3r/CustomGCode.hpp @@ -0,0 +1,81 @@ +#ifndef slic3r_CustomGCode_hpp_ +#define slic3r_CustomGCode_hpp_ + +#include +#include + +namespace Slic3r { + +class DynamicPrintConfig; + +namespace CustomGCode { + +struct Item +{ + bool operator<(const Item& rhs) const { return this->print_z < rhs.print_z; } + bool operator==(const Item& rhs) const + { + return (rhs.print_z == this->print_z ) && + (rhs.gcode == this->gcode ) && + (rhs.extruder == this->extruder ) && + (rhs.color == this->color ); + } + bool operator!=(const Item& rhs) const { return ! (*this == rhs); } + + double print_z; + std::string gcode; + int extruder; // Informative value for ColorChangeCode and ToolChangeCode + // "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder + // "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder + std::string color; // if gcode is equal to PausePrintCode, + // this field is used for save a short message shown on Printer display +}; + +enum Mode +{ + Undef, + SingleExtruder, // Single extruder printer preset is selected + MultiAsSingle, // Multiple extruder printer preset is selected, but + // this mode works just for Single extruder print + // (For all print from objects settings is used just one extruder) + MultiExtruder // Multiple extruder printer preset is selected +}; + +// string anlogue of custom_code_per_height mode +static constexpr char SingleExtruderMode[] = "SingleExtruder"; +static constexpr char MultiAsSingleMode [] = "MultiAsSingle"; +static constexpr char MultiExtruderMode [] = "MultiExtruder"; + +struct Info +{ + Mode mode = Undef; + std::vector gcodes; + + bool operator==(const Info& rhs) const + { + return (rhs.mode == this->mode ) && + (rhs.gcodes == this->gcodes ); + } + bool operator!=(const Info& rhs) const { return !(*this == rhs); } +}; + +// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), +// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format +// then CustomGCode::Info.gcodes should be updated considering this option. +extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config); + +// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined. +// So, we should set CustomGCode::Info.mode should be updated considering code values from items. +extern void check_mode_for_custom_gcode_per_print_z(Info& info); + +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// print_z corresponds to the first layer printed with the new extruder. +std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); + +} // namespace CustomGCode + +} // namespace Slic3r + + + +#endif /* slic3r_CustomGCode_hpp_ */ diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 14177ed88..8acab22e0 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1092,6 +1092,14 @@ namespace Slic3r { for (const auto& code : code_tree) { + if (code.first == "mode") + { + pt::ptree tree = code.second; + std::string mode = tree.get(".value"); + m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder : + mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle : + CustomGCode::Mode::MultiExtruder; + } if (code.first != "code") continue; pt::ptree tree = code.second; @@ -1100,7 +1108,7 @@ namespace Slic3r { int extruder = tree.get (".extruder" ); std::string color = tree.get (".color" ); - m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ; + m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ; } } } @@ -2615,7 +2623,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv pt::ptree tree; pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", ""); - for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes) + for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) { pt::ptree& code_tree = main_tree.add("code", ""); // store minX and maxZ @@ -2623,7 +2631,13 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv code_tree.put(".gcode" , code.gcode ); code_tree.put(".extruder" , code.extruder ); code_tree.put(".color" , code.color ); - } + } + + pt::ptree& mode_tree = main_tree.add("mode", ""); + // store mode of a custom_gcode_per_print_z + mode_tree.put(".value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode : + CustomGCode::MultiExtruderMode); if (!tree.empty()) { diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 7041956ba..ede4f403b 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -13,6 +13,7 @@ #include "../Utils.hpp" #include "../I18N.hpp" #include "../Geometry.hpp" +#include "../CustomGCode.hpp" #include "AMF.hpp" @@ -156,6 +157,7 @@ struct AMFParserContext NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code + NODE_TYPE_CUSTOM_GCODE_MODE, // amf/custom_code_per_height/mode NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; @@ -308,12 +310,18 @@ void AMFParserContext::startElement(const char *name, const char **atts) else this->stop(); } - else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) { - node_type_new = NODE_TYPE_GCODE_PER_HEIGHT; - m_value[0] = get_attribute(atts, "height"); - m_value[1] = get_attribute(atts, "gcode"); - m_value[2] = get_attribute(atts, "extruder"); - m_value[3] = get_attribute(atts, "color"); + else if (m_path[1] == NODE_TYPE_CUSTOM_GCODE) { + if (strcmp(name, "code") == 0) { + node_type_new = NODE_TYPE_GCODE_PER_HEIGHT; + m_value[0] = get_attribute(atts, "print_z"); + m_value[1] = get_attribute(atts, "gcode"); + m_value[2] = get_attribute(atts, "extruder"); + m_value[3] = get_attribute(atts, "color"); + } + else if (strcmp(name, "mode") == 0) { + node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE; + m_value[0] = get_attribute(atts, "value"); + } } break; case 3: @@ -632,18 +640,29 @@ void AMFParserContext::endElement(const char * /* name */) break; case NODE_TYPE_GCODE_PER_HEIGHT: { - double height = double(atof(m_value[0].c_str())); + double print_z = double(atof(m_value[0].c_str())); const std::string& gcode = m_value[1]; int extruder = atoi(m_value[2].c_str()); const std::string& color = m_value[3]; - m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color}); + m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}); for (std::string& val: m_value) val.clear(); break; } + case NODE_TYPE_CUSTOM_GCODE_MODE: { + const std::string& mode = m_value[0]; + + m_model.custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder : + mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle : + CustomGCode::Mode::MultiExtruder; + for (std::string& val: m_value) + val.clear(); + break; + } + case NODE_TYPE_METADATA: if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) m_config->load_from_gcode_string(m_value[1].c_str()); @@ -1237,16 +1256,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); - for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes) + for (const CustomGCode::Item& code : model->custom_gcode_per_print_z.gcodes) { pt::ptree& code_tree = main_tree.add("code", ""); - // store minX and maxZ + // store custom_gcode_per_print_z gcodes information code_tree.put(".print_z" , code.print_z ); code_tree.put(".gcode" , code.gcode ); code_tree.put(".extruder" , code.extruder ); code_tree.put(".color" , code.color ); } + pt::ptree& mode_tree = main_tree.add("mode", ""); + // store mode of a custom_gcode_per_print_z + mode_tree.put(".value", + model->custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + model->custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? + CustomGCode::MultiAsSingleMode : CustomGCode::MultiExtruderMode); + if (!tree.empty()) { std::ostringstream oss; @@ -1259,6 +1285,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) // Post processing("beautification") of the output string boost::replace_all(out, ">\n \n <", ">\n<"); stream << out << "\n"; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index decadafdf..cc83461d3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1767,7 +1767,7 @@ namespace ProcessLayer { static std::string emit_custom_gcode_per_print_z( - const Model::CustomGCode *custom_gcode, + const CustomGCode::Item *custom_gcode, // ID of the first extruder printing this layer. unsigned int first_extruder_id, bool single_material_print) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 3b6bf87af..f5ff33e41 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -133,7 +133,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool num_extruders > 1 && print.object_extruders().size() == 1) { // Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material). // There may be custom per-layer tool changes available at the model. - per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders); + per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_extruders); } // Collect extruders reuqired to print the layers. @@ -462,7 +462,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) // Only valid for non-sequential print. assert(! print.config().complete_objects.value); - const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z; + const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z; if (custom_gcode_per_print_z.gcodes.empty()) return; @@ -483,7 +483,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) // Custom G-codes were processed. break; // Some custom G-code is configured for this layer or a layer below. - const Model::CustomGCode &custom_gcode = *custom_gcode_it; + const CustomGCode::Item &custom_gcode = *custom_gcode_it; // print_z of the layer below the current layer. coordf_t print_z_below = 0.; if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend()) diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 08dd72656..de0460f1b 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -114,7 +114,7 @@ public: size_t wipe_tower_partitions = 0; coordf_t wipe_tower_layer_height = 0.; // Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print. - const Model::CustomGCode *custom_gcode = nullptr; + const CustomGCode::Item *custom_gcode = nullptr; WipingExtrusions& wiping_extrusions() { m_wiping_extrusions.set_layer_tools_ptr(this); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d5a40e79f..b01b2b99d 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -126,7 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c if (add_default_instances) model.add_default_instances(); - update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config); + CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config); + CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z); return model; } @@ -163,7 +164,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig if (add_default_instances) model.add_default_instances(); - update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config); + CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config); + CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z); return model; } @@ -1841,19 +1843,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } -// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. -// print_z corresponds to the first layer printed with the new extruder. -std::vector> custom_tool_changes(const Model &model, size_t num_extruders) -{ - std::vector> custom_tool_changes; - for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes) - if (custom_gcode.gcode == ToolChangeCode) { - // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders - custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); - } - return custom_tool_changes; -} - // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) @@ -1942,28 +1931,6 @@ extern bool model_has_advanced_features(const Model &model) return false; } -extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config) -{ - auto *colorprint_heights = config->option("colorprint_heights"); - if (colorprint_heights == nullptr) - return; - - if (custom_gcode_per_print_z.empty() && ! colorprint_heights->values.empty()) { - // Convert the old colorprint_heighs only if there is no equivalent data in a new format. - const std::vector& colors = GCodePreviewData::ColorPrintColors(); - const auto& colorprint_values = colorprint_heights->values; - custom_gcode_per_print_z.clear(); - custom_gcode_per_print_z.reserve(colorprint_values.size()); - int i = 0; - for (auto val : colorprint_values) - custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] }); - } - - // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted - // to a new format and therefore it shall be erased. - config->erase("colorprint_heights"); -} - #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index fa5d8f155..5f5b36b47 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -11,6 +11,7 @@ #include "SLA/SLACommon.hpp" #include "TriangleMesh.hpp" #include "Arrange.hpp" +#include "CustomGCode.hpp" #include #include @@ -749,48 +750,7 @@ public: ModelWipeTower wipe_tower; // Extensions for color print - struct CustomGCode - { - bool operator<(const CustomGCode& rhs) const { return this->print_z < rhs.print_z; } - bool operator==(const CustomGCode& rhs) const - { - return (rhs.print_z == this->print_z ) && - (rhs.gcode == this->gcode ) && - (rhs.extruder == this->extruder ) && - (rhs.color == this->color ); - } - bool operator!=(const CustomGCode& rhs) const { return ! (*this == rhs); } - - double print_z; - std::string gcode; - int extruder; // Informative value for ColorChangeCode and ToolChangeCode - // "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder - // "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder - std::string color; // if gcode is equal to PausePrintCode, - // this field is used for save a short message shown on Printer display - }; - - struct CustomGCodeInfo - { - enum MODE - { - SingleExtruder, // single extruder printer preset is selected - MultiAsSingle, // multiple extruder printer preset is selected, but - // this mode works just for Single extruder print - // (For all print from objects settings is used just one extruder) - MultiExtruder // multiple extruder printer preset is selected - } mode; - - std::vector gcodes; - - bool operator==(const CustomGCodeInfo& rhs) const - { - return (rhs.mode == this->mode ) && - (rhs.gcodes == this->gcodes ); - } - bool operator!=(const CustomGCodeInfo& rhs) const { return !(*this == rhs); } - } - custom_gcode_per_print_z; + CustomGCode::Info custom_gcode_per_print_z; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -872,10 +832,6 @@ private: #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE #undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE -// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. -// print_z corresponds to the first layer printed with the new extruder. -extern std::vector> custom_tool_changes(const Model &model, size_t num_extruders); - // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. extern bool model_object_list_equal(const Model &model_old, const Model &model_new); @@ -893,10 +849,6 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const extern bool model_has_multi_part_objects(const Model &model); // If the model has advanced features, then it cannot be processed in simple mode. extern bool model_has_advanced_features(const Model &model); -// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), -// and if model.custom_gcode_per_print_z is empty (there is no color print data available in a new format -// then model.custom_gcode_per_print_z should be updated considering this option. -extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config); #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a41122dfa..cc0554bd5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -491,7 +491,7 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_ } // 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) +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(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9a5cea511..09d6eda2e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -892,7 +892,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector& colors, std::vector& cp_legend_items) { - std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; const int extruders_cnt = wxGetApp().extruders_edited_cnt(); if (extruders_cnt == 1) @@ -2410,7 +2410,7 @@ void GLCanvas3D::load_sla_preview() } } -void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values) +void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values) { const Print *print = this->fff_print(); if (print == nullptr) @@ -5245,7 +5245,7 @@ void GLCanvas3D::_load_print_toolpaths() volume->indexed_vertex_array.finalize_geometry(m_initialized); } -void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values) +void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values) { std::vector tool_colors = _parse_colors(str_tool_colors); @@ -5259,7 +5259,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const std::vector* tool_colors; bool is_single_material_print; int extruders_cnt; - const std::vector* color_print_values; + const std::vector* color_print_values; static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish @@ -5274,7 +5274,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // For coloring by a color_print(M600), return a parsed color. bool color_by_color_print() const { return color_print_values!=nullptr; } const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const { - const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""}; + const CustomGCode::Item value{layers[layer_idx]->print_z + EPSILON, "", 0, ""}; auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); return (it - color_print_values->begin()) % number_tools(); } @@ -5284,7 +5284,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const coordf_t print_z = layers[layer_idx]->print_z; auto it = std::find_if(color_print_values->begin(), color_print_values->end(), - [print_z](const Model::CustomGCode& code) + [print_z](const CustomGCode::Item& code) { return fabs(code.print_z - print_z) < EPSILON; }); if (it != color_print_values->end()) { @@ -5305,7 +5305,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } - const Model::CustomGCode value{print_z + EPSILON, "", 0, ""}; + const CustomGCode::Item value{print_z + EPSILON, "", 0, ""}; it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); while (it != color_print_values->begin()) { @@ -5325,7 +5325,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } private: - int get_m600_color_idx(std::vector::const_iterator it) const + int get_m600_color_idx(std::vector::const_iterator it) const { int shift = 0; while (it != color_print_values->begin()) { @@ -5336,7 +5336,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c return extruders_cnt + shift; } - int get_color_idx_for_tool_change(std::vector::const_iterator it, const int extruder) const + int get_color_idx_for_tool_change(std::vector::const_iterator it, const int extruder) const { const int current_extruder = it->extruder == 0 ? extruder : it->extruder; if (number_tools() == extruders_cnt + 1) // there is no one "M600" @@ -5352,7 +5352,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c return std::min(extruders_cnt - 1, std::max(current_extruder - 1, 0)); } - int get_color_idx_for_color_change(std::vector::const_iterator it, const int extruder) const + int get_color_idx_for_color_change(std::vector::const_iterator it, const int extruder) const { if (extruders_cnt == 1) return get_m600_color_idx(it); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d4386f7f3..9ea7568e4 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -557,7 +557,7 @@ public: void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); void load_sla_preview(); - void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); + void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); void bind_event_handlers(); void unbind_event_handlers(); @@ -720,7 +720,7 @@ private: // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, // one for perimeters, one for infill and one for supports. void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, - const std::vector& color_print_values); + const std::vector& color_print_values); // Create 3D thick extrusion lines for wipe tower extrusions void _load_wipe_tower_toolpaths(const std::vector& str_tool_colors); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index f7010a503..c60547abc 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -633,7 +633,7 @@ static int find_close_layer_idx(const std::vector& zs, double &z, double return -1; } -void Preview::check_slider_values(std::vector& ticks_from_model, +void Preview::check_slider_values(std::vector& ticks_from_model, const std::vector& layers_z) { // All ticks that would end up outside the slider range should be erased. @@ -641,7 +641,7 @@ void Preview::check_slider_values(std::vector& ticks_from_mo // this function is e.g. not called when the last object is deleted unsigned int old_size = ticks_from_model.size(); ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(), - [layers_z](Model::CustomGCode val) + [layers_z](CustomGCode::Item val) { auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon()); return it == layers_z.end(); @@ -669,7 +669,7 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee // Detect and set manipulation mode for double slider update_double_slider_mode(); - Model::CustomGCodeInfo &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; + CustomGCode::Info &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; check_slider_values(ticks_info_from_model.gcodes, layers_z); m_slider->SetSliderValues(layers_z); @@ -830,7 +830,7 @@ void Preview::load_print_as_fff(bool keep_z_range) bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); // Collect colors per extruder. std::vector colors; - std::vector color_print_values = {}; + std::vector color_print_values = {}; // set color print values, if it si selected "ColorPrint" view type if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ae93e2ff0..ef311e307 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -155,7 +155,7 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); - void check_slider_values(std::vector &ticks_from_model, + void check_slider_values(std::vector &ticks_from_model, const std::vector &layers_z); void reset_double_slider(); void update_double_slider(const std::vector& layers_z, bool keep_z_range = false); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a86dd5ceb..8baa0657e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5423,7 +5423,7 @@ std::vector Plater::get_colors_for_color_print() const std::vector colors = get_extruder_colors_from_plater_config(); colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size()); - for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z.gcodes) + for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes) if (code.gcode == ColorChangeCode) colors.emplace_back(code.color); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 7e823f925..6c80a9eab 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -884,7 +884,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); - update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes, &this->project_config); + update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); break; } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index d4b207dc1..626d6f392 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -2531,10 +2531,10 @@ double DoubleSlider::get_double_value(const SelectedSlider& selection) return m_values[selection == ssLower ? m_lower_value : m_higher_value]; } -using t_custom_code = Slic3r::Model::CustomGCode; -Slic3r::Model::CustomGCodeInfo DoubleSlider::GetTicksValues() const +using t_custom_code = Slic3r::CustomGCode::Item; +Slic3r::CustomGCode::Info DoubleSlider::GetTicksValues() const { - Slic3r::Model::CustomGCodeInfo custom_gcode_per_print_z; + Slic3r::CustomGCode::Info custom_gcode_per_print_z; std::vector& values = custom_gcode_per_print_z.gcodes; const int val_size = m_values.size(); @@ -2550,7 +2550,7 @@ Slic3r::Model::CustomGCodeInfo DoubleSlider::GetTicksValues() const return custom_gcode_per_print_z; } -void DoubleSlider::SetTicksValues(const Slic3r::Model::CustomGCodeInfo& custom_gcode_per_print_z) +void DoubleSlider::SetTicksValues(const Slic3r::CustomGCode::Info& custom_gcode_per_print_z) { if (m_values.empty()) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 7511e8a19..322358ffe 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -782,7 +782,7 @@ public: const wxString& name = wxEmptyString); ~DoubleSlider() {} - using t_mode = Slic3r::Model::CustomGCodeInfo::MODE; + using t_mode = Slic3r::CustomGCode::Mode; /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. * So, let use same value as a permissible error for layer height. @@ -810,8 +810,8 @@ public: void SetKoefForLabels(const double koef) { m_label_koef = koef; } void SetSliderValues(const std::vector& values) { m_values = values; } void ChangeOneLayerLock(); - Slic3r::Model::CustomGCodeInfo GetTicksValues() const; - void SetTicksValues(const Slic3r::Model::CustomGCodeInfo &custom_gcode_per_print_z); + Slic3r::CustomGCode::Info GetTicksValues() const; + void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } void DisableTickManipulation() { EnableTickManipulation(false); } From 5ff8ae955be3dfcae2387fe04386ee40ec868598 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 23 Jan 2020 15:07:31 +0100 Subject: [PATCH 26/28] Implemented editing for extruder of existing ToolChangeCode tick + Code refactoring: DoubleSlider is extracted from wxExtensions --- resources/localization/list.txt | 1 + src/libslic3r/CustomGCode.hpp | 5 + src/libslic3r/GCodeWriter.cpp | 1 + src/libslic3r/GCodeWriter.hpp | 5 - src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/DoubleSlider.cpp | 1766 +++++++++++++++++++++ src/slic3r/GUI/DoubleSlider.hpp | 337 ++++ src/slic3r/GUI/ExtruderSequenceDialog.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 3 +- src/slic3r/GUI/GUI_Preview.cpp | 6 +- src/slic3r/GUI/GUI_Preview.hpp | 7 +- src/slic3r/GUI/Plater.cpp | 1 + src/slic3r/GUI/wxExtensions.cpp | 1721 +------------------- src/slic3r/GUI/wxExtensions.hpp | 312 +--- 14 files changed, 2128 insertions(+), 2041 deletions(-) create mode 100644 src/slic3r/GUI/DoubleSlider.cpp create mode 100644 src/slic3r/GUI/DoubleSlider.hpp diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 93ee2f441..fc33097df 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -46,6 +46,7 @@ src/slic3r/GUI/Tab.hpp src/slic3r/GUI/UpdateDialogs.cpp src/slic3r/GUI/WipeTowerDialog.cpp src/slic3r/GUI/wxExtensions.cpp +src/slic3r/GUI/DoubleSlider.cpp src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/Utils/Duet.cpp src/slic3r/Utils/OctoPrint.cpp diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index 4f1be3f31..5ab4c76ef 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -8,6 +8,11 @@ namespace Slic3r { class DynamicPrintConfig; +// Additional Codes which can be set by user using DoubleSlider +static constexpr char ColorChangeCode[] = "M600"; +static constexpr char PausePrintCode[] = "M601"; +static constexpr char ToolChangeCode[] = "tool_change"; + namespace CustomGCode { struct Item diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 4c53048dc..38a1c3ebe 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,4 +1,5 @@ #include "GCodeWriter.hpp" +#include "CustomGCode.hpp" #include #include #include diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index abeaf0024..3a57c8bd2 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -10,11 +10,6 @@ namespace Slic3r { -// Additional Codes which can be set by user using DoubleSlider -static constexpr char ColorChangeCode[] = "M600"; -static constexpr char PausePrintCode[] = "M601"; -static constexpr char ToolChangeCode[] = "tool_change"; - class GCodeWriter { public: GCodeConfig config; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 8f38cb21b..9e4fb84eb 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -142,6 +142,8 @@ set(SLIC3R_GUI_SOURCES GUI/PrintHostDialogs.hpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp + GUI/DoubleSlider.cpp + GUI/DoubleSlider.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp new file mode 100644 index 000000000..a47d6706c --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -0,0 +1,1766 @@ +#include "wxExtensions.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "ExtruderSequenceDialog.hpp" +#include "libslic3r/Print.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Slic3r { + +using GUI::from_u8; +using GUI::into_u8; + +namespace DoubleSlider { + +wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +Control::Control( wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) : + wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), + m_lower_value(lowerValue), + m_higher_value (higherValue), + m_min_value(minValue), + m_max_value(maxValue), + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) +{ +#ifdef __WXOSX__ + is_osx = true; +#endif //__WXOSX__ + if (!is_osx) + SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX + + const float scale_factor = get_svg_scale_factor(this); + + m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); + m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); + m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); + + m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); + m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); + m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); + m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); + m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); + + m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); + m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); + m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); + m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); + m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); + + m_bmp_revert = ScalableBitmap(this, "undo"); + m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); + m_bmp_cog = ScalableBitmap(this, "cog"); + m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); + + m_selection = ssUndef; + m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); + + // slider events + this->Bind(wxEVT_PAINT, &Control::OnPaint, this); + this->Bind(wxEVT_CHAR, &Control::OnChar, this); + this->Bind(wxEVT_LEFT_DOWN, &Control::OnLeftDown, this); + this->Bind(wxEVT_MOTION, &Control::OnMotion, this); + this->Bind(wxEVT_LEFT_UP, &Control::OnLeftUp, this); + this->Bind(wxEVT_MOUSEWHEEL, &Control::OnWheel, this); + this->Bind(wxEVT_ENTER_WINDOW,&Control::OnEnterWin, this); + this->Bind(wxEVT_LEAVE_WINDOW,&Control::OnLeaveWin, this); + this->Bind(wxEVT_KEY_DOWN, &Control::OnKeyDown, this); + this->Bind(wxEVT_KEY_UP, &Control::OnKeyUp, this); + this->Bind(wxEVT_RIGHT_DOWN, &Control::OnRightDown,this); + this->Bind(wxEVT_RIGHT_UP, &Control::OnRightUp, this); + + // control's view variables + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); + ORANGE_PEN = wxPen(wxColour(253, 126, 66)); + LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); + + DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); + GREY_PEN = wxPen(wxColour(164, 164, 164)); + LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); + + m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; + m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; + + const wxFont& font = GetFont(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); +} + +void Control::msw_rescale() +{ + const wxFont& font = GUI::wxGetApp().normal_font(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); + + m_bmp_thumb_higher.msw_rescale(); + m_bmp_thumb_lower .msw_rescale(); + m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); + + m_bmp_add_tick_on .msw_rescale(); + m_bmp_add_tick_off.msw_rescale(); + m_bmp_del_tick_on .msw_rescale(); + m_bmp_del_tick_off.msw_rescale(); + m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; + + m_bmp_one_layer_lock_on .msw_rescale(); + m_bmp_one_layer_lock_off .msw_rescale(); + m_bmp_one_layer_unlock_on .msw_rescale(); + m_bmp_one_layer_unlock_off.msw_rescale(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; + + m_bmp_revert.msw_rescale(); + m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; + m_bmp_cog.msw_rescale(); + m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; + + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + SetMinSize(get_min_size()); + GetParent()->Layout(); +} + +int Control::GetActiveValue() const +{ + return m_selection == ssLower ? + m_lower_value : m_selection == ssHigher ? + m_higher_value : -1; +} + +wxSize Control::get_min_size() const +{ + const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? (is_osx ? 8 : 6) : 10 ); + + return wxSize(min_side, min_side); +} + +wxSize Control::DoGetBestSize() const +{ + const wxSize size = wxControl::DoGetBestSize(); + if (size.x > 1 && size.y > 1) + return size; + return get_min_size(); +} + +void Control::SetLowerValue(const int lower_val) +{ + m_selection = ssLower; + m_lower_value = lower_val; + correct_lower_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetHigherValue(const int higher_val) +{ + m_selection = ssHigher; + m_higher_value = higher_val; + correct_higher_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetSelectionSpan(const int lower_val, const int higher_val) +{ + m_lower_value = std::max(lower_val, m_min_value); + m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); + if (m_lower_value < m_higher_value) + m_is_one_layer = false; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetMaxValue(const int max_value) +{ + m_max_value = max_value; + Refresh(); + Update(); +} + +void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) +{ + int width; + int height; + get_size(&width, &height); + + wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; + wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; + wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; + wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; + + wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; + wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; + wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; + wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; + + for (size_t id = 0; id < m_line_pens.size(); id++) + { + dc.SetPen(*m_line_pens[id]); + dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); + dc.SetPen(*m_segm_pens[id]); + dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); + if (is_horizontal()) + line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; + else + line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; + } +} + +double Control::get_scroll_step() +{ + const wxSize sz = get_size(); + const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; + return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); +} + +// get position on the slider line from entered value +wxCoord Control::get_position_from_value(const int value) +{ + const double step = get_scroll_step(); + const int val = is_horizontal() ? value : m_max_value - value; + return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); +} + +wxSize Control::get_size() +{ + int w, h; + get_size(&w, &h); + return wxSize(w, h); +} + +void Control::get_size(int *w, int *h) +{ + GetSize(w, h); + is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; +} + +double Control::get_double_value(const SelectedSlider& selection) +{ + if (m_values.empty() || m_lower_value<0) + return 0.0; + if (m_values.size() <= m_higher_value) { + correct_higher_value(); + return m_values.back(); + } + return m_values[selection == ssLower ? m_lower_value : m_higher_value]; +} + +using t_custom_code = CustomGCode::Item; +CustomGCode::Info Control::GetTicksValues() const +{ + CustomGCode::Info custom_gcode_per_print_z; + std::vector& values = custom_gcode_per_print_z.gcodes; + + const int val_size = m_values.size(); + if (!m_values.empty()) + for (const TickCode& tick : m_ticks.ticks) { + if (tick.tick > val_size) + break; + values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); + } + + custom_gcode_per_print_z.mode = m_force_mode_apply ? m_mode : m_ticks.mode; + + return custom_gcode_per_print_z; +} + +void Control::SetTicksValues(const CustomGCode::Info& custom_gcode_per_print_z) +{ + if (m_values.empty()) + { + m_ticks.mode = m_mode; + return; + } + + const bool was_empty = m_ticks.empty(); + + m_ticks.ticks.clear(); + const std::vector& heights = custom_gcode_per_print_z.gcodes; + for (auto h : heights) { + auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); + + if (it == m_values.end()) + continue; + + m_ticks.ticks.emplace(TickCode{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); + } + + if (!was_empty && m_ticks.empty()) + // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one + post_ticks_changed_event(); + + m_ticks.mode = custom_gcode_per_print_z.mode; + + Refresh(); + Update(); +} + +void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) +{ + const double step = get_scroll_step(); + if (is_horizontal()) { + lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); + higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); + } + else { + lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); + higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); + } +} + +void Control::draw_focus_rect() +{ + if (!m_is_focused) + return; + const wxSize sz = GetSize(); + wxPaintDC dc(this); + const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); + dc.SetPen(pen); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); + dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); +} + +void Control::render() +{ + SetBackgroundColour(GetParent()->GetBackgroundColour()); + draw_focus_rect(); + + wxPaintDC dc(this); + dc.SetFont(m_font); + + const wxCoord lower_pos = get_position_from_value(m_lower_value); + const wxCoord higher_pos = get_position_from_value(m_higher_value); + + // draw colored band on the background of a scroll line + // and only in a case of no-empty m_values + draw_colored_band(dc); + + // draw line + draw_scroll_line(dc, lower_pos, higher_pos); + + //draw color print ticks + draw_ticks(dc); + + // draw both sliders + draw_thumbs(dc, lower_pos, higher_pos); + + //draw lock/unlock + draw_one_layer_icon(dc); + + //draw revert bitmap (if it's shown) + draw_revert_icon(dc); + + //draw cog bitmap (if it's shown) + draw_cog_icon(dc); +} + +void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + // suppress add tick on first layer + if (tick == 0) + return; + + wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) + icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; + if (m_selection == ssLower) + is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; + 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); + + //update rect of the tick action icon + m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); +} + +void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) +{ + if (m_selection == selection) { + //draw info line + dc.SetPen(DARK_ORANGE_PEN); + const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); + const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); + dc.DrawLine(pt_beg, pt_end); + + //draw action icon + if (m_is_enabled_tick_manipulation) + draw_action_icon(dc, pt_beg, pt_end); + } +} + +wxString Control::get_label(const SelectedSlider& selection) const +{ + const int value = selection == ssLower ? m_lower_value : m_higher_value; + + if (m_label_koef == 1.0 && m_values.empty()) + return wxString::Format("%d", value); + if (value >= m_values.size()) + return "ErrVal"; + + const wxString str = m_values.empty() ? + wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : + wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); + return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); +} + +void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const +{ + if ( selection == ssUndef || + ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) + return; + wxCoord text_width, text_height; + const wxString label = get_label(selection); + dc.GetMultiLineTextExtent(label, &text_width, &text_height); + wxPoint text_pos; + if (selection ==ssLower) + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : + wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); + else + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : + wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); + dc.DrawText(label, text_pos); +} + +void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) +{ + wxCoord x_draw, y_draw; + if (selection == ssLower) { + if (is_horizontal()) { + x_draw = pos.x - m_thumb_size.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + else{ + if (is_horizontal()) { + x_draw = pos.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); + + // Update thumb rect + update_thumb_rect(x_draw, y_draw, selection); +} + +void Control::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); + + // Draw thumb + draw_thumb_item(dc, pos, selection); + + // Draw info_line + draw_info_line_with_icon(dc, pos, selection); + + // Draw thumb text + draw_thumb_text(dc, pos, selection); +} + +void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); + const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); + + // Draw lower thumb + draw_thumb_item(dc, pos_l, ssLower); + // Draw lower info_line + draw_info_line_with_icon(dc, pos_l, ssLower); + + // Draw higher thumb + draw_thumb_item(dc, pos_h, ssHigher); + // Draw higher info_line + draw_info_line_with_icon(dc, pos_h, ssHigher); + // Draw higher thumb text + draw_thumb_text(dc, pos_h, ssHigher); + + // Draw lower thumb text + draw_thumb_text(dc, pos_l, ssLower); +} + +void Control::draw_ticks(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); + int height, width; + get_size(&width, &height); + const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; + for (auto tick : m_ticks.ticks) + { + const wxCoord pos = get_position_from_value(tick.tick); + + is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : + dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); + is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : + dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); + + wxBitmap icon = wxNullBitmap; + + // Draw icon for "Pause print" or "Custom Gcode" + if (tick.gcode != ColorChangeCode && tick.gcode != ToolChangeCode) + icon = create_scaled_bitmap(this, tick.gcode == PausePrintCode ? "pause_print" : "edit_gcode"); + else + { + if ((tick.gcode == ColorChangeCode && ( + (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) )) || + (tick.gcode == ToolChangeCode && + (m_ticks.mode == t_mode::MultiAsSingle && m_mode != t_mode::MultiAsSingle ) )) + icon = create_scaled_bitmap(this, "error_tick"); + } + + if (!icon.IsNull()) + { + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; + is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; + + dc.DrawBitmap(icon, x_draw, y_draw); + } + } +} + +std::string Control::get_color_for_tool_change_tick(std::set::const_iterator it) const +{ + const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; + + auto it_n = it; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ColorChangeCode && it_n->extruder == current_extruder) + return it_n->color; + } + + return it->color; +} + +std::string Control::get_color_for_color_change_tick(std::set::const_iterator it) const +{ + const int def_extruder = std::max(1, m_only_extruder); + auto it_n = it; + bool is_tool_change = false; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ToolChangeCode) { + is_tool_change = true; + if (it_n->extruder == it->extruder) + return it->color; + break; + } + } + if (!is_tool_change && it->extruder == def_extruder) + return it->color; + + return ""; +} + +void Control::draw_colored_band(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + int height, width; + get_size(&width, &height); + + const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; + + wxRect main_band = is_horizontal() ? + wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), + width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : + wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, + lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); + + auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) + { + dc.SetPen(clr); + dc.SetBrush(clr); + dc.DrawRectangle(band_rc); + }; + + // don't color a band for MultiExtruder mode + if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) + { + draw_band(dc, GetParent()->GetBackgroundColour(), main_band); + return; + } + + const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; + draw_band(dc, wxColour(GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); + + std::set::const_iterator tick_it = m_ticks.ticks.begin(); + + while (tick_it != m_ticks.ticks.end()) + { + if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == ColorChangeCode ) || + (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == ToolChangeCode || tick_it->gcode == ColorChangeCode)) ) + { + const wxCoord pos = get_position_from_value(tick_it->tick); + is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : + main_band.SetBottom(pos - 1); + + const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : + tick_it->gcode == ToolChangeCode ? + get_color_for_tool_change_tick(tick_it) : + get_color_for_color_change_tick(tick_it); + + if (!clr_str.empty()) + draw_band(dc, wxColour(clr_str), main_band); + } + ++tick_it; + } +} + +void Control::draw_one_layer_icon(wxDC& dc) +{ + const wxBitmap& icon = m_is_one_layer ? + m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : + m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; + is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; + + dc.DrawBitmap(icon, x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); +} + +void Control::draw_revert_icon(wxDC& dc) +{ + if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; + is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; + + dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); +} + +void Control::draw_cog_icon(wxDC& dc) +{ + if (m_mode != t_mode::MultiExtruder) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; + is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; + + dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); +} + +void Control::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) +{ + const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); + if (selection == ssLower) + m_rect_lower_thumb = rect; + else + m_rect_higher_thumb = rect; +} + +int Control::get_value_from_position(const wxCoord x, const wxCoord y) +{ + const int height = get_size().y; + const double step = get_scroll_step(); + + if (is_horizontal()) + return int(double(x - SLIDER_MARGIN) / step + 0.5); + + return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); +} + +void Control::detect_selected_slider(const wxPoint& pt) +{ + m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : + is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; +} + +bool Control::is_point_in_rect(const wxPoint& pt, const wxRect& rect) +{ + return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && + rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); +} + +int Control::is_point_near_tick(const wxPoint& pt) +{ + for (auto tick : m_ticks.ticks) { + const wxCoord pos = get_position_from_value(tick.tick); + + if (is_horizontal()) { + if (pos - 4 <= pt.x && pt.x <= pos + 4) + return tick.tick; + } + else { + if (pos - 4 <= pt.y && pt.y <= pos + 4) + return tick.tick; + } + } + return -1; +} + +void Control::ChangeOneLayerLock() +{ + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnLeftDown(wxMouseEvent& event) +{ + if (HasCapture()) + return; + this->CaptureMouse(); + + wxClientDC dc(this); + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end()) + m_force_add_tick = true; + else + m_force_delete_tick = true; + return; + } + + m_is_left_down = true; + if (is_point_in_rect(pos, m_rect_one_layer_icon)) { + // switch on/off one layer mode + m_is_one_layer = !m_is_one_layer; + if (!m_is_one_layer) { + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + } + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + } + else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) { + // discard all color changes + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + m_ticks.ticks.clear(); + post_ticks_changed_event(); + } + else if (is_point_in_rect(pos, m_rect_cog_icon) && m_mode == t_mode::MultiExtruder) { + // show dialog for set extruder sequence + m_force_edit_extruder_sequence = true; + return; + } + else + detect_selected_slider(pos); + + if (!m_selection) { + const int tick_val = is_point_near_tick(pos); + /* Set current thumb position to the nearest tick (if it is) + * OR to a value corresponding to the mouse click + * */ + const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : + get_value_from_position(pos.x, pos.y); + if (mouse_val >= 0) + { + // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { + if ( mouse_val <= m_lower_value ) { + SetLowerValue(mouse_val); + correct_lower_value(); + m_selection = ssLower; + } + else { + SetHigherValue(mouse_val); + correct_higher_value(); + m_selection = ssHigher; + } + } + } + + Refresh(); + Update(); + event.Skip(); +} + +void Control::correct_lower_value() +{ + if (m_lower_value < m_min_value) + m_lower_value = m_min_value; + else if (m_lower_value > m_max_value) + m_lower_value = m_max_value; + + if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) + m_higher_value = m_lower_value; +} + +void Control::correct_higher_value() +{ + if (m_higher_value > m_max_value) + m_higher_value = m_max_value; + else if (m_higher_value < m_min_value) + m_higher_value = m_min_value; + + if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) + m_lower_value = m_higher_value; +} + +wxString Control::get_tooltip(IconFocus icon_focus) +{ + wxString tooltip(wxEmptyString); + if (m_is_one_layer_icon_focesed) + tooltip = _(L("One layer mode")); + + if (icon_focus == ifRevert) + tooltip = _(L("Discard all custom changes")); + if (icon_focus == ifCog) + tooltip = _(L("Set extruder sequence for whole print")); + else if (m_is_action_icon_focesed) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const auto tick_code_it = m_ticks.ticks.find(TickCode{tick}); + tooltip = tick_code_it == m_ticks.ticks.end() ? (m_mode == t_mode::MultiAsSingle ? + _(L("For add change extruder use left mouse button click")) : + _(L("For add color change use left mouse button click")) ) + "\n" + + _(L("For add another code use right mouse button click")) : + tick_code_it->gcode == ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ? + _(L("For Delete color change use left mouse button click\n" + "For Edit color use right mouse button click")) : + from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ): + tick_code_it->gcode == PausePrintCode ? + _(L("Delete pause")) : + tick_code_it->gcode == ToolChangeCode ? + from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) : + from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n" + "For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str()); + } + + return tooltip; +} + +void Control::OnMotion(wxMouseEvent& event) +{ + bool action = false; + + const wxClientDC dc(this); + const wxPoint pos = event.GetLogicalPosition(dc); + + m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); + IconFocus icon_focus = ifNone; + + if (!m_is_left_down && !m_is_one_layer) { + m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); + if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + icon_focus = ifRevert; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + icon_focus = ifCog; + } + else if (m_is_left_down || m_is_right_down) { + if (m_selection == ssLower) { + int current_value = m_lower_value; + m_lower_value = get_value_from_position(pos.x, pos.y); + correct_lower_value(); + action = (current_value != m_lower_value); + } + else if (m_selection == ssHigher) { + int current_value = m_higher_value; + m_higher_value = get_value_from_position(pos.x, pos.y); + correct_higher_value(); + action = (current_value != m_higher_value); + } + } + Refresh(); + Update(); + event.Skip(); + + // Set tooltips with information for each icon + this->SetToolTip(get_tooltip(icon_focus)); + + if (action) + { + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + e.SetString("moving"); + ProcessWindowEvent(e); + } +} + +void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_active_extruder = i == active_extruders[0] || i == active_extruders[1]; + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_active_extruder ? " (" + _(L("active")) + ")" : ""); + + if (m_mode == t_mode::MultiAsSingle) + append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ToolChangeCode, i); }, "", menu, + [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); + } + + const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? + (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : + _(L("Change extruder (N/A)")); + + wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, active_extruders[1] > 0 ? "edit_uni" : "change_extruder")); + + GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { + enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, + change_extruder_menu_item->GetId()); + } +} + +void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + std::set used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder + used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_used_extruder ? " (" + _(L("used")) + ")" : ""); + + append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ColorChangeCode, i); }, "", menu, + [is_used_extruder]() { return is_used_extruder; }, GUI::wxGetApp().plater()); + } + + const wxString menu_name = switch_current_code ? + from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) : + from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % ColorChangeCode).str()); + wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); + add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); + } +} + +void Control::OnLeftUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_left_down = false; + + if (m_force_delete_tick) + { + delete_current_tick(); + m_force_delete_tick = false; + } + else + if (m_force_add_tick) + { + add_current_tick(); + m_force_add_tick = false; + } + else + if (m_force_edit_extruder_sequence) { + edit_extruder_sequence(); + m_force_edit_extruder_sequence = false; + } + + Refresh(); + Update(); + event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::enter_window(wxMouseEvent& event, const bool enter) +{ + m_is_focused = enter; + Refresh(); + Update(); + event.Skip(); +} + +// "condition" have to be true for: +// - value increase (if wxSL_VERTICAL) +// - value decrease (if wxSL_HORIZONTAL) +void Control::move_current_thumb(const bool condition) +{ +// m_is_one_layer = wxGetKeyState(WXK_CONTROL); + int delta = condition ? -1 : 1; + if (is_horizontal()) + delta *= -1; + + if (m_selection == ssLower) { + m_lower_value -= delta; + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value -= delta; + correct_higher_value(); + } + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnWheel(wxMouseEvent& event) +{ + // Set nearest to the mouse thumb as a selected, if there is not selected thumb + if (m_selection == ssUndef) + { + const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); + + if (is_horizontal()) + m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= + abs(pt.x - m_rect_higher_thumb.GetLeft()) ? + ssLower : ssHigher; + else + m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= + abs(pt.y - m_rect_higher_thumb.GetBottom()) ? + ssLower : ssHigher; + } + + move_current_thumb(event.GetWheelRotation() > 0); +} + +void Control::OnKeyDown(wxKeyEvent &event) +{ + const int key = event.GetKeyCode(); + if (key == WXK_NUMPAD_ADD) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. + // To avoid this case we should suppress second add_tick() call. + m_ticks.suppress_plus(true); + add_current_tick(true); + } + else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. + // To avoid this case we should suppress second delete_tick() call. + m_ticks.suppress_minus(true); + delete_current_tick(); + } + else if (is_horizontal()) + { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + m_selection = key == WXK_UP ? ssHigher : ssLower; + Refresh(); + } + } + else { + if (key == WXK_LEFT || key == WXK_RIGHT) { + m_selection = key == WXK_LEFT ? ssHigher : ssLower; + Refresh(); + } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); + } + + event.Skip(); // !Needed to have EVT_CHAR generated as well +} + +void Control::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode() == WXK_CONTROL) + m_is_one_layer = false; + Refresh(); + Update(); + event.Skip(); +} + +void Control::OnChar(wxKeyEvent& event) +{ + const int key = event.GetKeyCode(); + if (key == '+' && !m_ticks.suppressed_plus()) { + add_current_tick(true); + m_ticks.suppress_plus(false); + } + else if (key == '-' && !m_ticks.suppressed_minus()) { + delete_current_tick(); + m_ticks.suppress_minus(false); + } +} + +void Control::OnRightDown(wxMouseEvent& event) +{ + if (HasCapture()) return; + this->CaptureMouse(); + + const wxClientDC dc(this); + + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + if (m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick + // show context menu on OnRightUp() + m_show_context_menu = true; + else + // show "Edit" and "Delete" menu on OnRightUp() + m_show_edit_menu = true; + return; + } + + detect_selected_slider(event.GetLogicalPosition(dc)); + if (!m_selection) + return; + + if (m_selection == ssLower) + m_higher_value = m_lower_value; + else + m_lower_value = m_higher_value; + + // set slider to "one layer" mode + m_is_right_down = m_is_one_layer = true; + + Refresh(); + Update(); + event.Skip(); +} + +// Get active extruders for tick. +// Means one current extruder for not existing tick OR +// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) +// Use those values to disable selection of active extruders +std::array Control::get_active_extruders_for_tick(int tick) const +{ + int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(1, m_only_extruder) : 1; + std::array extruders = { default_initial_extruder, -1 }; + if (m_ticks.empty()) + return extruders; + + auto it = m_ticks.ticks.lower_bound(TickCode{tick}); + + if (it->tick == tick) // current tick exists + extruders[1] = it->extruder; + + while (it != m_ticks.ticks.begin()) { + --it; + if(it->gcode == ToolChangeCode) { + extruders[0] = it->extruder; + break; + } + } + + return extruders; +} + +// Get used extruders for tick. +// Means all extruders(toools) will be used during printing from current tick to the end +std::set Control::get_used_extruders_for_tick(int tick) const +{ + if (m_mode == t_mode::MultiExtruder) + { + // #ys_FIXME: get tool ordering from _correct_ place + const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); + + if (tool_ordering.empty()) + return {}; + + std::set used_extruders; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(m_values[tick])); + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) + { + const std::vector& extruders = it_layer_tools->extruders; + for (const auto& extruder : extruders) + used_extruders.emplace(extruder+1); + } + + return used_extruders; + } + + const int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(m_only_extruder, 1) : 1; + if (m_ticks.empty()) + return {default_initial_extruder}; + + std::set used_extruders; + const std::set& ticks = m_ticks.ticks; + + auto it_start = ticks.lower_bound(TickCode{tick}); + auto it = it_start; + if (it == ticks.begin() && it->gcode == ToolChangeCode && + tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { + used_extruders.emplace(it->extruder); + if (tick < it->tick) + used_extruders.emplace(default_initial_extruder); + } + + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode && tick != it->tick) { + used_extruders.emplace(it->extruder); + break; + } + } + + if (it == ticks.begin() && used_extruders.empty()) + used_extruders.emplace(default_initial_extruder); + + for (it = it_start; it != ticks.end(); ++it) + if (it->gcode == ToolChangeCode && tick != it->tick) + used_extruders.emplace(it->extruder); + + return used_extruders; +} + +void Control::OnRightUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_right_down = m_is_one_layer = false; + + if (m_show_context_menu) { + wxMenu menu; + + if (m_mode == t_mode::SingleExtruder) + append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", + [this](wxCommandEvent&) { add_code_as_tick(ColorChangeCode); }, "colorchange_add_m", &menu, + [](){return true;}, this); + else + { + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu); + } + + append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", + [this](wxCommandEvent&) { add_code_as_tick(PausePrintCode); }, "pause_print", &menu, + []() {return true; }, this); + + append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", + [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu, + []() {return true; }, this); + + GUI::wxGetApp().plater()->PopupMenu(&menu); + + m_show_context_menu = false; + } + else if (m_show_edit_menu) { + wxMenu menu; + + std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + + if (it->gcode == ToolChangeCode) { + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu, true); + } + else + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Edit color")) : + it->gcode == PausePrintCode ? _(L("Edit pause print message")) : + _(L("Edit custom G-code")), "", + [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); + + if (it->gcode == ColorChangeCode && m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu, true); + + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Delete color change")) : + it->gcode == ToolChangeCode ? _(L("Delete tool change")) : + it->gcode == PausePrintCode ? _(L("Delete pause print")) : + _(L("Delete custom G-code")), "", + [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); + + GUI::wxGetApp().plater()->PopupMenu(&menu); + + m_show_edit_menu = false; + } + + Refresh(); + Update(); + event.Skip(); +} + +static std::string get_new_color(const std::string& color) +{ + wxColour clr(color); + if (!clr.IsOk()) + clr = wxColour(0, 0, 0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(nullptr, data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + return ""; +} + +static std::string get_custom_code(const std::string& code_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, + wxTextEntryDialogStyle | wxTE_MULTILINE); + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return dlg.GetValue().ToStdString(); +} + +static std::string get_pause_print_msg(const std::string& msg_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display during pause print"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), + wxTextEntryDialogStyle); + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return into_u8(dlg.GetValue()); +} + +void Control::add_code_as_tick(std::string code, int selected_extruder/* = -1*/) +{ + if (m_selection == ssUndef) + return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + if ( !check_ticks_changed_event(code) ) + return; + + const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); + const auto it = m_ticks.ticks.find(TickCode{ tick }); + + if ( it == m_ticks.ticks.end() ) { + // try to add tick + if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + return; + } + else if (code == ToolChangeCode || code == ColorChangeCode) { + // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly + if (!m_ticks.switch_code_for_tick(it, code, extruder)) + return; + } + else + return; + + post_ticks_changed_event(code); +} + +void Control::add_current_tick(bool call_from_keyboard /*= false*/) +{ + if (m_selection == ssUndef) + return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + auto it = m_ticks.ticks.find(TickCode{ tick }); + + if (it != m_ticks.ticks.end() || // this tick is already exist + !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? ToolChangeCode : ColorChangeCode)) + return; + + if (m_mode == t_mode::SingleExtruder) + add_code_as_tick(ColorChangeCode); + else + { + wxMenu menu; + + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + else + append_add_color_change_menu_item(&menu); + + wxPoint pos = wxDefaultPosition; + /* Menu position will be calculated from mouse click position, but... + * if function is called from keyboard (pressing "+"), we should to calculate it + * */ + if (call_from_keyboard) + { + int width, height; + get_size(&width, &height); + + const wxCoord coord = 0.75 * (is_horizontal() ? height : width); + this->GetPosition(&width, &height); + + pos = is_horizontal() ? + wxPoint(get_position_from_value(tick), height + coord) : + wxPoint(width + coord, get_position_from_value(tick)); + } + + GUI::wxGetApp().plater()->PopupMenu(&menu, pos); + } +} + +void Control::delete_current_tick() +{ + if (m_selection == ssUndef) + return; + + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + m_ticks.ticks.erase(it); + post_ticks_changed_event(code); +} + +void Control::edit_tick() +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); + + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + if (m_ticks.edit_tick(it, m_values[it->tick])) + post_ticks_changed_event(code); +} + +void Control::edit_extruder_sequence() +{ + if (!check_ticks_changed_event(ToolChangeCode)) + return; + + GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); + if (dlg.ShowModal() != wxID_OK) + return; + + const ExtrudersSequence& from_dlg_val = dlg.GetValue(); + if (m_extruders_sequence == from_dlg_val) + return; + + m_extruders_sequence = from_dlg_val; + + m_ticks.erase_all_ticks_with_code(ToolChangeCode); + + int tick = 0; + double value = 0.0; + int extruder = 0; + const int extr_cnt = m_extruders_sequence.extruders.size(); + + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + while (tick <= m_max_value) + { + const int cur_extruder = m_extruders_sequence.extruders[extruder]; + m_ticks.ticks.emplace(TickCode{tick, ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); + + extruder++; + if (extruder == extr_cnt) + extruder = 0; + if (m_extruders_sequence.is_mm_intervals) + { + value += m_extruders_sequence.interval_by_mm; + auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + + if (val_it == m_values.end()) + break; + + tick = val_it - m_values.begin(); + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + post_ticks_changed_event(ToolChangeCode); +} + +void Control::post_ticks_changed_event(const std::string& gcode /*= ""*/) +{ + m_force_mode_apply = (gcode.empty() || gcode == ColorChangeCode || gcode == ToolChangeCode); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); +} + +bool Control::check_ticks_changed_event(const std::string& gcode) +{ + if ( m_ticks.mode == m_mode || + (gcode != ColorChangeCode && gcode != ToolChangeCode) || + (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) || // All ColorChanges will be applied for 1st extruder + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) ) // Just mark ColorChanges for all unused extruders + return true; + + if ((m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) ) + { + if (!m_ticks.has_tick_with_code(ColorChangeCode)) + return true; + + wxString message = (m_ticks.mode == t_mode::SingleExtruder ? + _(L("The last color change data was saved for a single extruder printer profile.")) : + _(L("The last color change data was saved for a multiple extruder printer profile.")) + ) + "\n" + + _(L("Your current changes will cause a deletion of all saved color changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")); + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO); + if (msg.ShowModal() == wxID_YES) { + m_ticks.erase_all_ticks_with_code(ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + // m_ticks_mode == t_mode::MultiAsSingle + if( m_ticks.has_tick_with_code(ToolChangeCode) ) + { + wxString message = m_mode == t_mode::SingleExtruder ? ( + _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + + _(L("Select YES if you want to delete all saved tool changes, \n\t" + "NO if you want all tool changes switch to color changes, \n\t" + "or CANCEL for do nothing")) + "\n\n\t" + + _(L("Do you want to delete all saved tool changes?")) + ) : ( // t_mode::MultiExtruder + _(L("The last color change data was saved for a multi extruder printing with tool changes for whole print.")) + "\n\n" + + _(L("Your current changes will cause a deletion of all saved tool changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")) ) ; + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO | (m_mode == t_mode::SingleExtruder ? wxCANCEL : 0)); + const int answer = msg.ShowModal(); + if (answer == wxID_YES) { + m_ticks.erase_all_ticks_with_code(ToolChangeCode); + post_ticks_changed_event(ToolChangeCode); + } + else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { + m_ticks.switch_code(ToolChangeCode, ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + + return true; +} + + +std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& code, const int extruder) +{ + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::string color = colors[extruder - 1]; + + if (code == ColorChangeCode) + { + if (!ticks.empty()) + { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->gcode == ColorChangeCode && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + } + return color; +} + +bool TickCodeInfo::add_tick(const int tick, std::string& code, const int extruder, double print_z) +{ + std::string color; + if (code.empty()) // custom Gcode + { + code = get_custom_code(custom_gcode, print_z); + if (code.empty()) + return false; + custom_gcode = code; + } + else if (code == PausePrintCode) + { + /* PausePrintCode doesn't need a color, so + * this field is used for save a short message shown on Printer display + * */ + color = get_pause_print_msg(pause_print_msg, print_z); + if (color.empty()) + return false; + pause_print_msg = color; + } + else + { + color = get_color_for_tick(TickCode{ tick }, code, extruder); + if (color.empty()) + return false; + } + + ticks.emplace(TickCode{ tick, code, extruder, color }); + return true; +} + +bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) +{ + std::string edited_value; + if (it->gcode == ColorChangeCode) + edited_value = get_new_color(it->color); + else if (it->gcode == PausePrintCode) + edited_value = get_pause_print_msg(it->color, print_z); + else + edited_value = get_custom_code(it->gcode, print_z); + + if (edited_value.empty()) + return false; + + TickCode changed_tick = *it; + if (it->gcode == ColorChangeCode || it->gcode == PausePrintCode) { + if (it->color == edited_value) + return false; + changed_tick.color = edited_value; + } + else { + if (it->gcode == edited_value) + return false; + changed_tick.gcode = edited_value; + } + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeInfo::switch_code(const std::string& code_from, const std::string& code_to) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) + if (it->gcode == code_from) + { + TickCode tick = *it; + tick.gcode = code_to; + tick.extruder = 1; + ticks.erase(it); + it = ticks.emplace(tick).first; + } + else + ++it; +} + +bool TickCodeInfo::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) +{ + const std::string color = get_color_for_tick(*it, code_to, extruder); + if (color.empty()) + return false; + + TickCode changed_tick = *it; + changed_tick.gcode = code_to; + changed_tick.extruder = extruder; + changed_tick.color = color; + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeInfo::erase_all_ticks_with_code(const std::string& gcode) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { + if (it->gcode == gcode) + it = ticks.erase(it); + else + ++it; + } +} + +bool TickCodeInfo::has_tick_with_code(const std::string& gcode) +{ + for (const TickCode& tick : ticks) + if (tick.gcode == gcode) + return true; + + return false; +} + +} // DoubleSlider + +} // Slic3r + + diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp new file mode 100644 index 000000000..02a98ebb2 --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -0,0 +1,337 @@ +#ifndef slic3r_GUI_DoubleSlider_hpp_ +#define slic3r_GUI_DoubleSlider_hpp_ + +#include "libslic3r/CustomGCode.hpp" +#include "wxExtensions.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +class wxMenu; + +namespace Slic3r { + +namespace DoubleSlider { + +/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. + * So, let use same value as a permissible error for layer height. + */ +static double epsilon() { return 0.0011;} + +// custom message the slider sends to its parent to notify a tick-change: +wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +enum SelectedSlider { + ssUndef, + ssLower, + ssHigher +}; + +enum IconFocus { + ifNone, + ifRevert, + ifCog +}; + +using t_mode = CustomGCode::Mode; + +struct TickCode +{ + bool operator<(const TickCode& other) const { return other.tick > this->tick; } + bool operator>(const TickCode& other) const { return other.tick < this->tick; } + + int tick = 0; + std::string gcode = ColorChangeCode; + int extruder = 0; + std::string color; +}; + +class TickCodeInfo +{ + std::string custom_gcode; + std::string pause_print_msg; + bool m_suppress_plus = false; + bool m_suppress_minus = false; + + std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder); + +public: + std::set ticks {}; + t_mode mode = t_mode::SingleExtruder; + + bool empty() const { return ticks.empty(); } + void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } + + bool add_tick(const int tick, std::string& code, int extruder, double print_z); + bool edit_tick(std::set::iterator it, double print_z); + void switch_code(const std::string& code_from, const std::string& code_to); + bool switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder); + void erase_all_ticks_with_code(const std::string& gcode); + bool has_tick_with_code(const std::string& gcode); + + void suppress_plus (bool suppress) { m_suppress_plus = suppress; } + void suppress_minus(bool suppress) { m_suppress_minus = suppress; } + bool suppressed_plus () { return m_suppress_plus; } + bool suppressed_minus() { return m_suppress_minus; } +}; + + +struct ExtrudersSequence +{ + bool is_mm_intervals = true; + double interval_by_mm = 3.0; + int interval_by_layers = 10; + std::vector extruders = { 0 }; + + bool operator==(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals == this->is_mm_intervals ) && + (other.interval_by_mm == this->interval_by_mm ) && + (other.interval_by_layers == this->interval_by_layers ) && + (other.extruders == this->extruders ) ; + } + bool operator!=(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals != this->is_mm_intervals ) && + (other.interval_by_mm != this->interval_by_mm ) && + (other.interval_by_layers != this->interval_by_layers ) && + (other.extruders != this->extruders ) ; + } + + void add_extruder(size_t pos) + { + extruders.insert(extruders.begin() + pos+1, size_t(0)); + } + + void delete_extruder(size_t pos) + { + if (extruders.size() == 1) + return;// last item can't be deleted + extruders.erase(extruders.begin() + pos); + } +}; + +class Control : public wxControl +{ +public: + Control( + wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxSL_VERTICAL, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxEmptyString); + ~Control() {} + + void msw_rescale(); + + int GetMinValue() const { return m_min_value; } + int GetMaxValue() const { return m_max_value; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } + int GetLowerValue() const { return m_lower_value; } + int GetHigherValue() const { return m_higher_value; } + int GetActiveValue() const; + double GetLowerValueD() { return get_double_value(ssLower); } + double GetHigherValueD() { return get_double_value(ssHigher); } + wxSize DoGetBestSize() const override; + wxSize get_min_size() const ; + + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetLowerValue (const int lower_val); + void SetHigherValue(const int higher_val); + void SetSelectionSpan(const int lower_val, const int higher_val); + + void SetMaxValue(const int max_value); + void SetKoefForLabels(const double koef) { m_label_koef = koef; } + void SetSliderValues(const std::vector& values) { m_values = values; } + void ChangeOneLayerLock(); + + CustomGCode::Info GetTicksValues() const; + void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); + + void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } + void DisableTickManipulation() { EnableTickManipulation(false); } + + void SetManipulationMode(t_mode mode) { m_mode = mode; } + t_mode GetManipulationMode() const { return m_mode; } + void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) + { + m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : + only_extruder < 0 ? t_mode::SingleExtruder : + t_mode::MultiAsSingle; + m_only_extruder = only_extruder; + } + + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_one_layer() const { return m_is_one_layer; } + bool is_lower_at_min() const { return m_lower_value == m_min_value; } + bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + + void OnPaint(wxPaintEvent& ) { render();} + void OnLeftDown(wxMouseEvent& event); + void OnMotion(wxMouseEvent& event); + void OnLeftUp(wxMouseEvent& event); + void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } + void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } + void OnWheel(wxMouseEvent& event); + void OnKeyDown(wxKeyEvent &event); + void OnKeyUp(wxKeyEvent &event); + void OnChar(wxKeyEvent &event); + void OnRightDown(wxMouseEvent& event); + void OnRightUp(wxMouseEvent& event); + + void add_code_as_tick(std::string code, int selected_extruder = -1); + // add default action for tick, when press "+" + void add_current_tick(bool call_from_keyboard = false); + // delete current tick, when press "-" + void delete_current_tick(); + void edit_tick(); + void edit_extruder_sequence(); + + ExtrudersSequence m_extruders_sequence; + +protected: + + void render(); + void draw_focus_rect(); + void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); + void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); + void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); + void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); + void draw_ticks(wxDC& dc); + void draw_colored_band(wxDC& dc); + void draw_one_layer_icon(wxDC& dc); + void draw_revert_icon(wxDC& dc); + void draw_cog_icon(wxDC &dc); + void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); + void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); + void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; + + void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); + void detect_selected_slider(const wxPoint& pt); + void correct_lower_value(); + void correct_higher_value(); + void move_current_thumb(const bool condition); + void enter_window(wxMouseEvent& event, const bool enter); + +private: + + bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); + int is_point_near_tick(const wxPoint& pt); + + double get_scroll_step(); + wxString get_label(const SelectedSlider& selection) const; + void get_lower_and_higher_position(int& lower_pos, int& higher_pos); + int get_value_from_position(const wxCoord x, const wxCoord y); + wxCoord get_position_from_value(const int value); + wxSize get_size(); + void get_size(int *w, int *h); + double get_double_value(const SelectedSlider& selection); + wxString get_tooltip(IconFocus icon_focus); + + std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; + std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + + // Get active extruders for tick. + // Means one current extruder for not existing tick OR + // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) + // Use those values to disable selection of active extruders + std::array get_active_extruders_for_tick(int tick) const; + + // Get used extruders for tick. + // Means all extruders(toools) will be used during printing from current tick to the end + std::set get_used_extruders_for_tick(int tick) const; + + void post_ticks_changed_event(const std::string& gcode = ""); + bool check_ticks_changed_event(const std::string& gcode); + + void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false); + void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); + + bool is_osx { false }; + wxFont m_font; + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + ScalableBitmap m_bmp_thumb_higher; + ScalableBitmap m_bmp_thumb_lower; + ScalableBitmap m_bmp_add_tick_on; + ScalableBitmap m_bmp_add_tick_off; + ScalableBitmap m_bmp_del_tick_on; + ScalableBitmap m_bmp_del_tick_off; + ScalableBitmap m_bmp_one_layer_lock_on; + ScalableBitmap m_bmp_one_layer_lock_off; + ScalableBitmap m_bmp_one_layer_unlock_on; + ScalableBitmap m_bmp_one_layer_unlock_off; + ScalableBitmap m_bmp_revert; + ScalableBitmap m_bmp_cog; + SelectedSlider m_selection; + bool m_is_left_down = false; + bool m_is_right_down = false; + bool m_is_one_layer = false; + bool m_is_focused = false; + bool m_is_action_icon_focesed = false; + bool m_is_one_layer_icon_focesed = false; + bool m_is_enabled_tick_manipulation = true; + bool m_show_context_menu = false; + bool m_show_edit_menu = false; + bool m_force_edit_extruder_sequence = false; + bool m_force_mode_apply = true; + bool m_force_add_tick = false; + bool m_force_delete_tick = false; + t_mode m_mode = t_mode::SingleExtruder; + int m_only_extruder = -1; + + wxRect m_rect_lower_thumb; + wxRect m_rect_higher_thumb; + wxRect m_rect_tick_action; + wxRect m_rect_one_layer_icon; + wxRect m_rect_revert_icon; + wxRect m_rect_cog_icon; + wxSize m_thumb_size; + int m_tick_icon_dim; + int m_lock_icon_dim; + int m_revert_icon_dim; + int m_cog_icon_dim; + long m_style; + float m_label_koef = 1.0; + + std::vector m_values; + TickCodeInfo m_ticks; + +// control's view variables + wxCoord SLIDER_MARGIN; // margin around slider + + wxPen DARK_ORANGE_PEN; + wxPen ORANGE_PEN; + wxPen LIGHT_ORANGE_PEN; + + wxPen DARK_GREY_PEN; + wxPen GREY_PEN; + wxPen LIGHT_GREY_PEN; + + std::vector m_line_pens; + std::vector m_segm_pens; +}; + +} // DoubleSlider; + +} // Slic3r + + + +#endif // slic3r_GUI_DoubleSlider_hpp_ diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp index 3efd9e3a2..97d57ef12 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.hpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -2,7 +2,7 @@ #define slic3r_GUI_ExtruderSequenceDialog_hpp_ #include "GUI_Utils.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" class wxTextCtrl; class wxFlexGridSizer; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 09d6eda2e..d847b297e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -60,6 +60,7 @@ #include #include #include +#include "DoubleSlider.hpp" #if ENABLE_RENDER_STATISTICS #include #endif // ENABLE_RENDER_STATISTICS @@ -910,7 +911,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D { if (custom_code.gcode != ColorChangeCode) continue; - auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon()); + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon()); if (lower_b == print_zs.end()) continue; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c60547abc..e42f8ed21 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -10,7 +10,7 @@ #include "GLCanvas3DManager.hpp" #include "GLCanvas3D.hpp" #include "PresetBundle.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" #include #include @@ -584,7 +584,7 @@ void Preview::update_view_type(bool slice_completed) void Preview::create_double_slider() { - m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100); + m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); m_slider->EnableTickManipulation(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF); m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); @@ -595,7 +595,7 @@ void Preview::create_double_slider() m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); - Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); model.custom_gcode_per_print_z = m_slider->GetTicksValues(); m_schedule_background_process(); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ef311e307..cc8f15325 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -15,7 +15,6 @@ class wxChoice; class wxComboCtrl; class wxBitmapComboBox; class wxCheckBox; -class DoubleSlider; namespace Slic3r { @@ -25,6 +24,10 @@ class BackgroundSlicingProcess; class GCodePreviewData; class Model; +namespace DoubleSlider { + class Control; +}; + namespace GUI { class GLCanvas3D; @@ -103,7 +106,7 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - DoubleSlider* m_slider {nullptr}; + DoubleSlider::Control* m_slider {nullptr}; public: Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8baa0657e..f5df8fc4f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -84,6 +84,7 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" +#include "libslic3r/CustomGCode.hpp" using boost::optional; namespace fs = boost::filesystem; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 626d6f392..356dd458f 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -30,7 +30,6 @@ using Slic3r::GUI::from_u8; using Slic3r::GUI::into_u8; -wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); #ifndef __WXGTK__// msw_menuitem_bitmaps is used for MSW and OSX @@ -408,7 +407,7 @@ int em_unit(wxWindow* win) return Slic3r::GUI::wxGetApp().em_unit(); } -static float get_svg_scale_factor(wxWindow *win) +float get_svg_scale_factor(wxWindow *win) { #ifdef __APPLE__ // Note: win->GetContentScaleFactor() is not used anymore here because it tends to @@ -2276,1724 +2275,6 @@ bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& val return true; } -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- -DoubleSlider::DoubleSlider( wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos, - const wxSize& size, - long style, - const wxValidator& val, - const wxString& name) : - wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), m_higher_value (higherValue), - m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) -{ -#ifdef __WXOSX__ - is_osx = true; -#endif //__WXOSX__ - if (!is_osx) - SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - - const float scale_factor = get_svg_scale_factor(this); - - m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); - m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); - - m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); - m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); - m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); - m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); - - m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); - m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); - m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); - m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); - - m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); - m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); - - m_selection = ssUndef; - m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); - - // slider events - Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this); - Bind(wxEVT_CHAR, &DoubleSlider::OnChar, this); - Bind(wxEVT_LEFT_DOWN, &DoubleSlider::OnLeftDown, this); - Bind(wxEVT_MOTION, &DoubleSlider::OnMotion, this); - Bind(wxEVT_LEFT_UP, &DoubleSlider::OnLeftUp, this); - Bind(wxEVT_MOUSEWHEEL, &DoubleSlider::OnWheel, this); - Bind(wxEVT_ENTER_WINDOW,&DoubleSlider::OnEnterWin, this); - Bind(wxEVT_LEAVE_WINDOW,&DoubleSlider::OnLeaveWin, this); - Bind(wxEVT_KEY_DOWN, &DoubleSlider::OnKeyDown, this); - Bind(wxEVT_KEY_UP, &DoubleSlider::OnKeyUp, this); - Bind(wxEVT_RIGHT_DOWN, &DoubleSlider::OnRightDown,this); - Bind(wxEVT_RIGHT_UP, &DoubleSlider::OnRightUp, this); - - // control's view variables - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); - ORANGE_PEN = wxPen(wxColour(253, 126, 66)); - LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); - - DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); - GREY_PEN = wxPen(wxColour(164, 164, 164)); - LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); - - m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; - m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; - - const wxFont& font = GetFont(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); -} - -void DoubleSlider::msw_rescale() -{ - const wxFont& font = Slic3r::GUI::wxGetApp().normal_font(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); - - m_bmp_thumb_higher.msw_rescale(); - m_bmp_thumb_lower .msw_rescale(); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); - - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; - - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; - - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; - - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - SetMinSize(get_min_size()); - GetParent()->Layout(); -} - -int DoubleSlider::GetActiveValue() const -{ - return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; -} - -wxSize DoubleSlider::get_min_size() const -{ - const int min_side = is_horizontal() ? - (is_osx ? 8 : 6) * Slic3r::GUI::wxGetApp().em_unit() : - /*(is_osx ? 10 : 8)*/10 * Slic3r::GUI::wxGetApp().em_unit(); - - return wxSize(min_side, min_side); -} - -wxSize DoubleSlider::DoGetBestSize() const -{ - const wxSize size = wxControl::DoGetBestSize(); - if (size.x > 1 && size.y > 1) - return size; - return get_min_size(); -} - -void DoubleSlider::SetLowerValue(const int lower_val) -{ - m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetHigherValue(const int higher_val) -{ - m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetSelectionSpan(const int lower_val, const int higher_val) -{ - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) - m_is_one_layer = false; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetMaxValue(const int max_value) -{ - m_max_value = max_value; - Refresh(); - Update(); -} - -void DoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) -{ - int width; - int height; - get_size(&width, &height); - - wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; - wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; - wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; - wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; - - wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; - wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; - wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; - wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - - for (size_t id = 0; id < m_line_pens.size(); id++) - { - dc.SetPen(*m_line_pens[id]); - dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); - dc.SetPen(*m_segm_pens[id]); - dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); - if (is_horizontal()) - line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; - else - line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; - } -} - -double DoubleSlider::get_scroll_step() -{ - const wxSize sz = get_size(); - const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); -} - -// get position on the slider line from entered value -wxCoord DoubleSlider::get_position_from_value(const int value) -{ - const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; - return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); -} - -wxSize DoubleSlider::get_size() -{ - int w, h; - get_size(&w, &h); - return wxSize(w, h); -} - -void DoubleSlider::get_size(int *w, int *h) -{ - GetSize(w, h); - is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; -} - -double DoubleSlider::get_double_value(const SelectedSlider& selection) -{ - if (m_values.empty() || m_lower_value<0) - return 0.0; - if (m_values.size() <= m_higher_value) { - correct_higher_value(); - return m_values.back(); - } - return m_values[selection == ssLower ? m_lower_value : m_higher_value]; -} - -using t_custom_code = Slic3r::CustomGCode::Item; -Slic3r::CustomGCode::Info DoubleSlider::GetTicksValues() const -{ - Slic3r::CustomGCode::Info custom_gcode_per_print_z; - std::vector& values = custom_gcode_per_print_z.gcodes; - - const int val_size = m_values.size(); - if (!m_values.empty()) - for (const TICK_CODE& tick : m_ticks.ticks) { - if (tick.tick > val_size) - break; - values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); - } - - custom_gcode_per_print_z.mode = m_force_mode_apply ? m_mode : m_ticks.mode; - - return custom_gcode_per_print_z; -} - -void DoubleSlider::SetTicksValues(const Slic3r::CustomGCode::Info& custom_gcode_per_print_z) -{ - if (m_values.empty()) - { - m_ticks.mode = m_mode; - return; - } - - const bool was_empty = m_ticks.empty(); - - m_ticks.ticks.clear(); - const std::vector& heights = custom_gcode_per_print_z.gcodes; - for (auto h : heights) { - auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); - - if (it == m_values.end()) - continue; - - m_ticks.ticks.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); - } - - if (!was_empty && m_ticks.empty()) - // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one - post_ticks_changed_event(); - - m_ticks.mode = custom_gcode_per_print_z.mode; - - Refresh(); - Update(); -} - -void DoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) -{ - const double step = get_scroll_step(); - if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); - } - else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); - } -} - -void DoubleSlider::draw_focus_rect() -{ - if (!m_is_focused) - return; - const wxSize sz = GetSize(); - wxPaintDC dc(this); - const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - dc.SetPen(pen); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); - dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); -} - -void DoubleSlider::render() -{ - SetBackgroundColour(GetParent()->GetBackgroundColour()); - draw_focus_rect(); - - wxPaintDC dc(this); - dc.SetFont(m_font); - - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); - - // draw colored band on the background of a scroll line - // and only in a case of no-empty m_values - draw_colored_band(dc); - - // draw line - draw_scroll_line(dc, lower_pos, higher_pos); - - //draw color print ticks - draw_ticks(dc); - - // draw both sliders - draw_thumbs(dc, lower_pos, higher_pos); - - //draw lock/unlock - draw_one_layer_icon(dc); - - //draw revert bitmap (if it's shown) - draw_revert_icon(dc); - - //draw cog bitmap (if it's shown) - draw_cog_icon(dc); -} - -void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - // suppress add tick on first layer - if (tick == 0) - return; - - wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks.ticks.find(TICK_CODE{tick}) != m_ticks.ticks.end()) - icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; - if (m_selection == ssLower) - is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; - 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); - - //update rect of the tick action icon - m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); -} - -void DoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) -{ - if (m_selection == selection) { - //draw info line - dc.SetPen(DARK_ORANGE_PEN); - const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); - const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); - dc.DrawLine(pt_beg, pt_end); - - //draw action icon - if (m_is_enabled_tick_manipulation) - draw_action_icon(dc, pt_beg, pt_end); - } -} - -wxString DoubleSlider::get_label(const SelectedSlider& selection) const -{ - const int value = selection == ssLower ? m_lower_value : m_higher_value; - - if (m_label_koef == 1.0 && m_values.empty()) - return wxString::Format("%d", value); - if (value >= m_values.size()) - return "ErrVal"; - - const wxString str = m_values.empty() ? - wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); - return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); -} - -void DoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const -{ - if ( selection == ssUndef || - ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) - return; - wxCoord text_width, text_height; - const wxString label = get_label(selection); - dc.GetMultiLineTextExtent(label, &text_width, &text_height); - wxPoint text_pos; - if (selection ==ssLower) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : - wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); - else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); - dc.DrawText(label, text_pos); -} - -void DoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) -{ - wxCoord x_draw, y_draw; - if (selection == ssLower) { - if (is_horizontal()) { - x_draw = pos.x - m_thumb_size.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - else{ - if (is_horizontal()) { - x_draw = pos.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); - - // Update thumb rect - update_thumb_rect(x_draw, y_draw, selection); -} - -void DoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); - - // Draw thumb - draw_thumb_item(dc, pos, selection); - - // Draw info_line - draw_info_line_with_icon(dc, pos, selection); - - // Draw thumb text - draw_thumb_text(dc, pos, selection); -} - -void DoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); - const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); - - // Draw lower thumb - draw_thumb_item(dc, pos_l, ssLower); - // Draw lower info_line - draw_info_line_with_icon(dc, pos_l, ssLower); - - // Draw higher thumb - draw_thumb_item(dc, pos_h, ssHigher); - // Draw higher info_line - draw_info_line_with_icon(dc, pos_h, ssHigher); - // Draw higher thumb text - draw_thumb_text(dc, pos_h, ssHigher); - - // Draw lower thumb text - draw_thumb_text(dc, pos_l, ssLower); -} - -void DoubleSlider::draw_ticks(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks.ticks) - { - const wxCoord pos = get_position_from_value(tick.tick); - - is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : - dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); - is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : - dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); - - wxBitmap icon = wxNullBitmap; - - // Draw icon for "Pause print" or "Custom Gcode" - if (tick.gcode != Slic3r::ColorChangeCode && tick.gcode != Slic3r::ToolChangeCode) - icon = create_scaled_bitmap(this, tick.gcode == Slic3r::PausePrintCode ? "pause_print" : "edit_gcode"); - else - { - if ((tick.gcode == Slic3r::ColorChangeCode && ( - (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) )) || - (tick.gcode == Slic3r::ToolChangeCode && - (m_ticks.mode == t_mode::MultiAsSingle && m_mode != t_mode::MultiAsSingle ) )) - icon = create_scaled_bitmap(this, "error_tick"); - } - - if (!icon.IsNull()) - { - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; - is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; - - dc.DrawBitmap(icon, x_draw, y_draw); - } - } -} - -std::string DoubleSlider::get_color_for_tool_change_tick(std::set::const_iterator it) const -{ - const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; - - auto it_n = it; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ColorChangeCode && it_n->extruder == current_extruder) - return it_n->color; - } - - return it->color; -} - -std::string DoubleSlider::get_color_for_color_change_tick(std::set::const_iterator it) const -{ - const int def_extruder = std::max(1, m_only_extruder); - auto it_n = it; - bool is_tool_change = false; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ToolChangeCode) { - is_tool_change = true; - if (it_n->extruder == it->extruder) - return it->color; - break; - } - } - if (!is_tool_change && it->extruder == def_extruder) - return it->color; - - return ""; -} - -void DoubleSlider::draw_colored_band(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - int height, width; - get_size(&width, &height); - - const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - - wxRect main_band = is_horizontal() ? - wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), - width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : - wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, - lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); - - auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) { - dc.SetPen(clr); - dc.SetBrush(clr); - dc.DrawRectangle(band_rc); - }; - - // don't color a band for MultiExtruder mode - if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) - { - draw_band(dc, GetParent()->GetBackgroundColour(), main_band); - return; - } - - const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; - draw_band(dc, wxColour(Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); - - std::set::const_iterator tick_it = m_ticks.ticks.begin(); - - while (tick_it != m_ticks.ticks.end()) - { - if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == Slic3r::ColorChangeCode ) || - (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == Slic3r::ToolChangeCode || tick_it->gcode == Slic3r::ColorChangeCode)) ) - { - const wxCoord pos = get_position_from_value(tick_it->tick); - is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : - main_band.SetBottom(pos - 1); - - const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : - tick_it->gcode == Slic3r::ToolChangeCode ? - get_color_for_tool_change_tick(tick_it) : - get_color_for_color_change_tick(tick_it); - - if (!clr_str.empty()) - draw_band(dc, wxColour(clr_str), main_band); - } - ++tick_it; - } -} - -void DoubleSlider::draw_one_layer_icon(wxDC& dc) -{ - const wxBitmap& icon = m_is_one_layer ? - m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; - is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - - dc.DrawBitmap(icon, x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); -} - -void DoubleSlider::draw_revert_icon(wxDC& dc) -{ - if (m_ticks.empty() || !m_is_enabled_tick_manipulation) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; - is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - - dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); -} - -void DoubleSlider::draw_cog_icon(wxDC& dc) -{ - if (m_mode != t_mode::MultiExtruder) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; - is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; - - dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); -} - -void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) -{ - const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); - if (selection == ssLower) - m_rect_lower_thumb = rect; - else - m_rect_higher_thumb = rect; -} - -int DoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y) -{ - const int height = get_size().y; - const double step = get_scroll_step(); - - if (is_horizontal()) - return int(double(x - SLIDER_MARGIN) / step + 0.5); - - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); -} - -void DoubleSlider::detect_selected_slider(const wxPoint& pt) -{ - m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : - is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; -} - -bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) -{ - return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && - rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); -} - -int DoubleSlider::is_point_near_tick(const wxPoint& pt) -{ - for (auto tick : m_ticks.ticks) { - const wxCoord pos = get_position_from_value(tick.tick); - - if (is_horizontal()) { - if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick.tick; - } - else { - if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick.tick; - } - } - return -1; -} - -void DoubleSlider::ChangeOneLayerLock() -{ - m_is_one_layer = !m_is_one_layer; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnLeftDown(wxMouseEvent& event) -{ - if (HasCapture()) - return; - this->CaptureMouse(); - - wxClientDC dc(this); - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) - { - const auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - if (it == m_ticks.ticks.end()) - m_force_add_tick = true; - else - m_force_delete_tick = true; - return; - } - - m_is_left_down = true; - if (is_point_in_rect(pos, m_rect_one_layer_icon)) { - // switch on/off one layer mode - m_is_one_layer = !m_is_one_layer; - if (!m_is_one_layer) { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - } - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - } - else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) { - // discard all color changes - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - m_ticks.ticks.clear(); - post_ticks_changed_event(); - } - else if (is_point_in_rect(pos, m_rect_cog_icon) && m_mode == t_mode::MultiExtruder) { - // show dialog for set extruder sequence - m_force_edit_extruder_sequence = true; - return; - } - else - detect_selected_slider(pos); - - if (!m_selection) { - const int tick_val = is_point_near_tick(pos); - /* Set current thumb position to the nearest tick (if it is) - * OR to a value corresponding to the mouse click - * */ - const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : - get_value_from_position(pos.x, pos.y); - if (mouse_val >= 0) - { - // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { - if ( mouse_val <= m_lower_value ) { - SetLowerValue(mouse_val); - correct_lower_value(); - m_selection = ssLower; - } - else { - SetHigherValue(mouse_val); - correct_higher_value(); - m_selection = ssHigher; - } - } - } - - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::correct_lower_value() -{ - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; - - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) - m_higher_value = m_lower_value; -} - -void DoubleSlider::correct_higher_value() -{ - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; - - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) - m_lower_value = m_higher_value; -} - -wxString DoubleSlider::get_tooltip(IconFocus icon_focus) -{ - wxString tooltip(wxEmptyString); - if (m_is_one_layer_icon_focesed) - tooltip = _(L("One layer mode")); - - if (icon_focus == ifRevert) - tooltip = _(L("Discard all custom changes")); - if (icon_focus == ifCog) - tooltip = _(L("Set extruder sequence for whole print")); - else if (m_is_action_icon_focesed) - { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto tick_code_it = m_ticks.ticks.find(TICK_CODE{tick}); - tooltip = tick_code_it == m_ticks.ticks.end() ? (m_mode == t_mode::MultiAsSingle ? - _(L("For add change extruder use left mouse button click")) : - _(L("For add color change use left mouse button click")) ) + "\n" + - _(L("For add another code use right mouse button click")) : - tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ? - _(L("For Delete color change use left mouse button click\n" - "For Edit color use right mouse button click")) : - from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ): - tick_code_it->gcode == Slic3r::PausePrintCode ? - _(L("Delete pause")) : - tick_code_it->gcode == Slic3r::ToolChangeCode ? - from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) : - from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n" - "For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str()); - } - - return tooltip; -} - -void DoubleSlider::OnMotion(wxMouseEvent& event) -{ - bool action = false; - - const wxClientDC dc(this); - const wxPoint pos = event.GetLogicalPosition(dc); - - m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - IconFocus icon_focus = ifNone; - - if (!m_is_left_down && !m_is_one_layer) { - m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - icon_focus = ifRevert; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - icon_focus = ifCog; - } - else if (m_is_left_down || m_is_right_down) { - if (m_selection == ssLower) { - int current_value = m_lower_value; - m_lower_value = get_value_from_position(pos.x, pos.y); - correct_lower_value(); - action = (current_value != m_lower_value); - } - else if (m_selection == ssHigher) { - int current_value = m_higher_value; - m_higher_value = get_value_from_position(pos.x, pos.y); - correct_higher_value(); - action = (current_value != m_higher_value); - } - } - Refresh(); - Update(); - event.Skip(); - - // Set tooltips with information for each icon - this->SetToolTip(get_tooltip(icon_focus)); - - if (action) - { - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - e.SetString("moving"); - ProcessWindowEvent(e); - } -} - -void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - const int initial_extruder = std::max(1 , get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value)); - - wxMenu* change_extruder_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_active_extruder = i == initial_extruder; - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_active_extruder ? " (" + _(L("active")) + ")" : ""); - - if (m_mode == t_mode::MultiAsSingle) - append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ToolChangeCode, i); }, "", menu, - [is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater()); - } - - const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? - (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : - _(L("Change extruder (N/A)")); - - wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); - change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, "change_extruder")); - - Slic3r::GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { - enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, - change_extruder_menu_item->GetId()); - } -} - -void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - std::set used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); - - wxMenu* add_color_change_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder - used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_used_extruder ? " (" + _(L("used")) + ")" : ""); - - append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode, i); }, "", menu, - [is_used_extruder]() { return is_used_extruder; }, Slic3r::GUI::wxGetApp().plater()); - } - - const wxString menu_name = switch_current_code ? - from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()) : - from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()); - wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); - } -} - -void DoubleSlider::OnLeftUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_left_down = false; - - if (m_force_delete_tick) - { - delete_current_tick(); - m_force_delete_tick = false; - } - else - if (m_force_add_tick) - { - add_current_tick(); - m_force_add_tick = false; - } - else - if (m_force_edit_extruder_sequence) { - edit_extruder_sequence(); - m_force_edit_extruder_sequence = false; - } - - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::enter_window(wxMouseEvent& event, const bool enter) -{ - m_is_focused = enter; - Refresh(); - Update(); - event.Skip(); -} - -// "condition" have to be true for: -// - value increase (if wxSL_VERTICAL) -// - value decrease (if wxSL_HORIZONTAL) -void DoubleSlider::move_current_thumb(const bool condition) -{ -// m_is_one_layer = wxGetKeyState(WXK_CONTROL); - int delta = condition ? -1 : 1; - if (is_horizontal()) - delta *= -1; - - if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); - } - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnWheel(wxMouseEvent& event) -{ - // Set nearest to the mouse thumb as a selected, if there is not selected thumb - if (m_selection == ssUndef) - { - const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); - - if (is_horizontal()) - m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= - abs(pt.x - m_rect_higher_thumb.GetLeft()) ? - ssLower : ssHigher; - else - m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= - abs(pt.y - m_rect_higher_thumb.GetBottom()) ? - ssLower : ssHigher; - } - - move_current_thumb(event.GetWheelRotation() > 0); -} - -void DoubleSlider::OnKeyDown(wxKeyEvent &event) -{ - const int key = event.GetKeyCode(); - if (key == WXK_NUMPAD_ADD) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. - // To avoid this case we should suppress second add_tick() call. - m_ticks.suppress_plus(true); - add_current_tick(true); - } - else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. - // To avoid this case we should suppress second delete_tick() call. - m_ticks.suppress_minus(true); - delete_current_tick(); - } - else if (is_horizontal()) - { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - m_selection = key == WXK_UP ? ssHigher : ssLower; - Refresh(); - } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) { - m_selection = key == WXK_LEFT ? ssHigher : ssLower; - Refresh(); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } - - event.Skip(); // !Needed to have EVT_CHAR generated as well -} - -void DoubleSlider::OnKeyUp(wxKeyEvent &event) -{ - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::OnChar(wxKeyEvent& event) -{ - const int key = event.GetKeyCode(); - if (key == '+' && !m_ticks.suppressed_plus()) { - add_current_tick(true); - m_ticks.suppress_plus(false); - } - else if (key == '-' && !m_ticks.suppressed_minus()) { - delete_current_tick(); - m_ticks.suppress_minus(false); - } -} - -void DoubleSlider::OnRightDown(wxMouseEvent& event) -{ - if (HasCapture()) return; - this->CaptureMouse(); - - const wxClientDC dc(this); - - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) - { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - if (m_ticks.ticks.find(TICK_CODE{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick - // show context menu on OnRightUp() - m_show_context_menu = true; - else - // show "Edit" and "Delete" menu on OnRightUp() - m_show_edit_menu = true; - return; - } - - detect_selected_slider(event.GetLogicalPosition(dc)); - if (!m_selection) - return; - - if (m_selection == ssLower) - m_higher_value = m_lower_value; - else - m_lower_value = m_higher_value; - - // set slider to "one layer" mode - m_is_right_down = m_is_one_layer = true; - - Refresh(); - Update(); - event.Skip(); -} - -int DoubleSlider::get_extruder_for_tick(int tick) -{ - int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? m_only_extruder : 0; - if (m_ticks.empty()) - return default_initial_extruder; - - auto it = m_ticks.ticks.lower_bound(TICK_CODE{tick}); - while (it != m_ticks.ticks.begin()) { - --it; - if(it->gcode == Slic3r::ToolChangeCode) - return it->extruder; - } - - return default_initial_extruder; -} - -std::set DoubleSlider::get_used_extruders_for_tick(int tick) -{ - if (m_mode == t_mode::MultiExtruder) - { - // #ys_FIXME: get tool ordering from _correct_ place - const Slic3r::ToolOrdering& tool_ordering = Slic3r::GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - - if (tool_ordering.empty()) - return {}; - - std::set used_extruders; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), Slic3r::LayerTools(m_values[tick])); - for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) - { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) - used_extruders.emplace(extruder+1); - } - - return used_extruders; - } - - const int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(m_only_extruder, 1) : 1; - if (m_ticks.empty()) - return {default_initial_extruder}; - - std::set used_extruders; - const std::set& ticks = m_ticks.ticks; - - auto it_start = ticks.lower_bound(TICK_CODE{tick}); - auto it = it_start; - if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode && - tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, - // we shouldn't change color for extruder, which will be deleted - { - used_extruders.emplace(it->extruder); - if (tick < it->tick) - used_extruders.emplace(default_initial_extruder); - } - - while (it != ticks.begin()) { - --it; - if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) { - used_extruders.emplace(it->extruder); - break; - } - } - - if (it == ticks.begin() && used_extruders.empty()) - used_extruders.emplace(default_initial_extruder); - - for (it = it_start; it != ticks.end(); ++it) - if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) - used_extruders.emplace(it->extruder); - - return used_extruders; -} - -void DoubleSlider::OnRightUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; - - if (m_show_context_menu) { - wxMenu menu; - - if (m_mode == t_mode::SingleExtruder) - append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", - [this](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu, - [](){return true;}, this); - else - { - append_change_extruder_menu_item(&menu); - append_add_color_change_menu_item(&menu); - } - - append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", - [this](wxCommandEvent&) { add_code_as_tick(Slic3r::PausePrintCode); }, "pause_print", &menu, - []() {return true; }, this); - - append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", - [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu, - []() {return true; }, this); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_context_menu = false; - } - else if (m_show_edit_menu) { - wxMenu menu; - - std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - const bool is_color_change = it->gcode == Slic3r::ColorChangeCode; - - if (it->gcode == Slic3r::ToolChangeCode) - append_add_color_change_menu_item(&menu, true); - else - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Edit pause print message")) : - _(L("Edit custom G-code")), "", - [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); - - if (it->gcode == Slic3r::ColorChangeCode && m_mode == t_mode::MultiAsSingle) - append_change_extruder_menu_item(&menu, true); - - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Delete color change")) : - it->gcode == Slic3r::ToolChangeCode ? _(L("Delete tool change")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : - _(L("Delete custom G-code")), "", - [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_edit_menu = false; - } - - Refresh(); - Update(); - event.Skip(); -} - -static std::string get_new_color(const std::string& color) -{ - wxColour clr(color); - if (!clr.IsOk()) - clr = wxColour(0, 0, 0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(nullptr, data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - return ""; -} - -static std::string get_custom_code(const std::string& code_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, - wxTextEntryDialogStyle | wxTE_MULTILINE); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return dlg.GetValue().ToStdString(); -} - -static std::string get_pause_print_msg(const std::string& msg_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display during pause print"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), - wxTextEntryDialogStyle); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return into_u8(dlg.GetValue()); -} - -void DoubleSlider::add_code_as_tick(std::string code, int selected_extruder/* = -1*/) -{ - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - if ( !check_ticks_changed_event(code) ) - return; - - const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); - const auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if ( it == m_ticks.ticks.end() ) { - // try to add tick - if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) - return; - } - else if (code == Slic3r::ToolChangeCode || code == Slic3r::ColorChangeCode) { - // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly - if (!m_ticks.switch_code_for_tick(it, code, extruder)) - return; - } - else - return; - - post_ticks_changed_event(code); -} - -void DoubleSlider::add_current_tick(bool call_from_keyboard /*= false*/) -{ - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if (it != m_ticks.ticks.end() || // this tick is already exist - !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? Slic3r::ToolChangeCode : Slic3r::ColorChangeCode)) - return; - - if (m_mode == t_mode::SingleExtruder) - add_code_as_tick(Slic3r::ColorChangeCode); - else - { - wxMenu menu; - - if (m_mode == t_mode::MultiAsSingle) - append_change_extruder_menu_item(&menu); - else - append_add_color_change_menu_item(&menu); - - wxPoint pos = wxDefaultPosition; - /* Menu position will be calculated from mouse click position, but... - * if function is called from keyboard (pressing "+"), we should to calculate it - * */ - if (call_from_keyboard) - { - int width, height; - get_size(&width, &height); - - const wxCoord coord = 0.75 * (is_horizontal() ? height : width); - this->GetPosition(&width, &height); - - pos = is_horizontal() ? - wxPoint(get_position_from_value(tick), height + coord) : - wxPoint(width + coord, get_position_from_value(tick)); - } - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu, pos); - } -} - -void DoubleSlider::delete_current_tick() -{ - if (m_selection == ssUndef) - return; - - auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - if (it == m_ticks.ticks.end() || - !check_ticks_changed_event(it->gcode)) - return; - - const std::string code = it->gcode; - m_ticks.ticks.erase(it); - post_ticks_changed_event(code); -} - -void DoubleSlider::edit_tick() -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if (it == m_ticks.ticks.end() || - !check_ticks_changed_event(it->gcode)) - return; - - const std::string code = it->gcode; - if (m_ticks.edit_tick(it, m_values[it->tick])) - post_ticks_changed_event(code); -} - -void DoubleSlider::edit_extruder_sequence() -{ - if (!check_ticks_changed_event(Slic3r::ToolChangeCode)) - return; - - Slic3r::GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); - if (dlg.ShowModal() != wxID_OK) - return; - - const ExtrudersSequence& from_dlg_val = dlg.GetValue(); - if (m_extruders_sequence == from_dlg_val) - return; - - m_extruders_sequence = from_dlg_val; - - m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); - - int tick = 0; - double value = 0.0; - int extruder = 0; - const int extr_cnt = m_extruders_sequence.extruders.size(); - - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - while (tick <= m_max_value) - { - const int cur_extruder = m_extruders_sequence.extruders[extruder]; - m_ticks.ticks.emplace(TICK_CODE{tick, Slic3r::ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); - - extruder++; - if (extruder == extr_cnt) - extruder = 0; - if (m_extruders_sequence.is_mm_intervals) - { - value += m_extruders_sequence.interval_by_mm; - auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); - - if (val_it == m_values.end()) - break; - - tick = val_it - m_values.begin(); - } - else - tick += m_extruders_sequence.interval_by_layers; - } - - post_ticks_changed_event(Slic3r::ToolChangeCode); -} - -void DoubleSlider::post_ticks_changed_event(const std::string& gcode /*= ""*/) -{ - m_force_mode_apply = (gcode.empty() || gcode == Slic3r::ColorChangeCode || gcode == Slic3r::ToolChangeCode); - - wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); -} - -bool DoubleSlider::check_ticks_changed_event(const std::string& gcode) -{ - if ( m_ticks.mode == m_mode || - (gcode != Slic3r::ColorChangeCode && gcode != Slic3r::ToolChangeCode) || - (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) || // All ColorChanges will be applied for 1st extruder - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) ) // Just mark ColorChanges for all unused extruders - return true; - - if ((m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) ) - { - if (!m_ticks.has_tick_with_code(Slic3r::ColorChangeCode)) - return true; - - wxString message = (m_ticks.mode == t_mode::SingleExtruder ? - _(L("The last color change data was saved for a single extruder printer profile.")) : - _(L("The last color change data was saved for a multiple extruder printer profile.")) - ) + "\n" + - _(L("Your current changes will cause a deletion of all saved color changes.")) + "\n\n\t" + - _(L("Are you sure you want to continue?")); - - wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO); - if (msg.ShowModal() == wxID_YES) { - m_ticks.erase_all_ticks_with_code(Slic3r::ColorChangeCode); - post_ticks_changed_event(Slic3r::ColorChangeCode); - } - return false; - } - // m_ticks_mode == t_mode::MultiAsSingle - if( m_ticks.has_tick_with_code(Slic3r::ToolChangeCode) ) - { - wxString message = m_mode == t_mode::SingleExtruder ? ( - _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + - _(L("Select YES if you want to delete all saved tool changes, \n\t" - "NO if you want all tool changes switch to color changes, \n\t" - "or CANCEL for do nothing")) + "\n\n\t" + - _(L("Do you want to delete all saved tool changes?")) - ) : ( // t_mode::MultiExtruder - _(L("The last color change data was saved for a multi extruder printing with tool changes for whole print.")) + "\n\n" + - _(L("Your current changes will cause a deletion of all saved tool changes.")) + "\n\n\t" + - _(L("Are you sure you want to continue?")) ) ; - - wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO | (m_mode == t_mode::SingleExtruder ? wxCANCEL : 0)); - const int answer = msg.ShowModal(); - if (answer == wxID_YES) { - m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); - post_ticks_changed_event(Slic3r::ToolChangeCode); - } - else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { - m_ticks.switch_code(Slic3r::ToolChangeCode, Slic3r::ColorChangeCode); - post_ticks_changed_event(Slic3r::ColorChangeCode); - } - return false; - } - - return true; -} - - -std::string DoubleSlider::TICK_CODE_INFO::get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder) -{ - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - std::string color = colors[extruder - 1]; - - if (code == Slic3r::ColorChangeCode) - { - if (!ticks.empty()) - { - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); - while (before_tick_it != ticks.begin()) { - --before_tick_it; - if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { - color = before_tick_it->color; - break; - } - } - } - - color = get_new_color(color); - } - return color; -} - -bool DoubleSlider::TICK_CODE_INFO::add_tick(const int tick, std::string& code, const int extruder, double print_z) -{ - std::string color; - if (code.empty()) // custom Gcode - { - code = get_custom_code(custom_gcode, print_z); - if (code.empty()) - return false; - custom_gcode = code; - } - else if (code == Slic3r::PausePrintCode) - { - /* PausePrintCode doesn't need a color, so - * this field is used for save a short message shown on Printer display - * */ - color = get_pause_print_msg(pause_print_msg, print_z); - if (color.empty()) - return false; - pause_print_msg = color; - } - else - { - color = get_color_for_tick(TICK_CODE{ tick }, code, extruder); - if (color.empty()) - return false; - } - - ticks.emplace(TICK_CODE{ tick, code, extruder, color }); - return true; -} - -bool DoubleSlider::TICK_CODE_INFO::edit_tick(std::set::iterator it, double print_z) -{ - std::string edited_value; - if (it->gcode == Slic3r::ColorChangeCode) - edited_value = get_new_color(it->color); - else if (it->gcode == Slic3r::PausePrintCode) - edited_value = get_pause_print_msg(it->color, print_z); - else - edited_value = get_custom_code(it->gcode, print_z); - - if (edited_value.empty()) - return false; - - TICK_CODE changed_tick = *it; - if (it->gcode == Slic3r::ColorChangeCode || it->gcode == Slic3r::PausePrintCode) { - if (it->color == edited_value) - return false; - changed_tick.color = edited_value; - } - else { - if (it->gcode == edited_value) - return false; - changed_tick.gcode = edited_value; - } - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -void DoubleSlider::TICK_CODE_INFO::switch_code(const std::string& code_from, const std::string& code_to) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) - if (it->gcode == code_from) - { - TICK_CODE tick = *it; - tick.gcode = code_to; - tick.extruder = 1; - ticks.erase(it); - it = ticks.emplace(tick).first; - } - else - ++it; -} - -bool DoubleSlider::TICK_CODE_INFO::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) -{ - const std::string color = get_color_for_tick(*it, code_to, extruder); - if (color.empty()) - return false; - - TICK_CODE changed_tick = *it; - changed_tick.gcode = code_to; - changed_tick.extruder = extruder; - changed_tick.color = color; - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -void DoubleSlider::TICK_CODE_INFO::erase_all_ticks_with_code(const std::string& gcode) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { - if (it->gcode == gcode) - it = ticks.erase(it); - else - ++it; - } -} - -bool DoubleSlider::TICK_CODE_INFO::has_tick_with_code(const std::string& gcode) -{ - for (const TICK_CODE& tick : ticks) - if (tick.gcode == gcode) - return true; - - return false; -} - // ---------------------------------------------------------------------------- // LockButton diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 322358ffe..a6f8862ac 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -5,19 +5,15 @@ #include #include #include -#include #include #include #include -#include #include #include #include #include #include -#include "libslic3r/Model.hpp" -#include "libslic3r/GCodeWriter.hpp" namespace Slic3r { enum class ModelVolumeType : int; @@ -49,12 +45,15 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, wxEvtHandler* event_handler); +void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win); + class wxDialog; class wxBitmapComboBox; void edit_tooltip(wxString& tooltip); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector& btn_ids); int em_unit(wxWindow* win); +float get_svg_scale_factor(wxWindow* win); wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); @@ -742,311 +741,6 @@ private: }; -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- - -// custom message the slider sends to its parent to notify a tick-change: -wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - -enum SelectedSlider { - ssUndef, - ssLower, - ssHigher -}; -enum TicksAction{ - taOnIcon, - taAdd, - taDel -}; - -class DoubleSlider : public wxControl -{ - enum IconFocus { - ifNone, - ifRevert, - ifCog - }; -public: - DoubleSlider( - wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxSL_VERTICAL, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxEmptyString); - ~DoubleSlider() {} - - using t_mode = Slic3r::CustomGCode::Mode; - - /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. - * So, let use same value as a permissible error for layer height. - */ - static double epsilon() { return 0.0011;} - - void msw_rescale(); - - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } - int GetActiveValue() const; - wxSize get_min_size() const ; - double GetLowerValueD() { return get_double_value(ssLower); } - double GetHigherValueD() { return get_double_value(ssHigher); } - wxSize DoGetBestSize() const override; - void SetLowerValue(const int lower_val); - void SetHigherValue(const int higher_val); - // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetSelectionSpan(const int lower_val, const int higher_val); - void SetMaxValue(const int max_value); - void SetKoefForLabels(const double koef) { m_label_koef = koef; } - void SetSliderValues(const std::vector& values) { m_values = values; } - void ChangeOneLayerLock(); - Slic3r::CustomGCode::Info GetTicksValues() const; - void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); - void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } - void DisableTickManipulation() { EnableTickManipulation(false); } - - void SetManipulationMode(t_mode mode) { m_mode = mode; } - t_mode GetManipulationMode() const { return m_mode; } - - void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) - { - m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : - only_extruder < 0 ? t_mode::SingleExtruder : - t_mode::MultiAsSingle; - m_only_extruder = only_extruder; - } - - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } - bool is_one_layer() const { return m_is_one_layer; } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } - bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } - - void OnPaint(wxPaintEvent& ) { render();} - void OnLeftDown(wxMouseEvent& event); - void OnMotion(wxMouseEvent& event); - void OnLeftUp(wxMouseEvent& event); - void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } - void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } - void OnWheel(wxMouseEvent& event); - void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); - void OnChar(wxKeyEvent &event); - void OnRightDown(wxMouseEvent& event); - void OnRightUp(wxMouseEvent& event); - - void add_code_as_tick(std::string code, int selected_extruder = -1); - // add default action for tick, when press "+" - void add_current_tick(bool call_from_keyboard = false); - // delete current tick, when press "-" - void delete_current_tick(); - void edit_tick(); - void edit_extruder_sequence(); - - struct TICK_CODE - { - bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } - bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } - - int tick = 0; - std::string gcode = Slic3r::ColorChangeCode; - int extruder = 0; - std::string color; - }; - -protected: - - void render(); - void draw_focus_rect(); - void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); - void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); - void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); - void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); - void draw_ticks(wxDC& dc); - void draw_colored_band(wxDC& dc); - void draw_one_layer_icon(wxDC& dc); - void draw_revert_icon(wxDC& dc); - void draw_cog_icon(wxDC &dc); - void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); - void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); - void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - - void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); - void detect_selected_slider(const wxPoint& pt); - void correct_lower_value(); - void correct_higher_value(); - void move_current_thumb(const bool condition); - void enter_window(wxMouseEvent& event, const bool enter); - -private: - - bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); - int is_point_near_tick(const wxPoint& pt); - - double get_scroll_step(); - wxString get_label(const SelectedSlider& selection) const; - void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - wxCoord get_position_from_value(const int value); - wxSize get_size(); - void get_size(int *w, int *h); - double get_double_value(const SelectedSlider& selection); - wxString get_tooltip(IconFocus icon_focus); - - std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; - std::string get_color_for_color_change_tick(std::set::const_iterator it) const; - int get_extruder_for_tick(int tick); - std::set get_used_extruders_for_tick(int tick); - - void post_ticks_changed_event(const std::string& gcode = ""); - bool check_ticks_changed_event(const std::string& gcode); - void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false); - void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); - - bool is_osx { false }; - wxFont m_font; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - ScalableBitmap m_bmp_thumb_higher; - ScalableBitmap m_bmp_thumb_lower; - ScalableBitmap m_bmp_add_tick_on; - ScalableBitmap m_bmp_add_tick_off; - ScalableBitmap m_bmp_del_tick_on; - ScalableBitmap m_bmp_del_tick_off; - ScalableBitmap m_bmp_one_layer_lock_on; - ScalableBitmap m_bmp_one_layer_lock_off; - ScalableBitmap m_bmp_one_layer_unlock_on; - ScalableBitmap m_bmp_one_layer_unlock_off; - ScalableBitmap m_bmp_revert; - ScalableBitmap m_bmp_cog; - SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; - bool m_is_one_layer = false; - bool m_is_focused = false; - bool m_is_action_icon_focesed = false; - bool m_is_one_layer_icon_focesed = false; - bool m_is_enabled_tick_manipulation = true; - bool m_show_context_menu = false; - bool m_show_edit_menu = false; - bool m_force_edit_extruder_sequence = false; - bool m_force_mode_apply = true; - bool m_force_add_tick = false; - bool m_force_delete_tick = false; - t_mode m_mode = t_mode::SingleExtruder; - int m_only_extruder = -1; - - wxRect m_rect_lower_thumb; - wxRect m_rect_higher_thumb; - wxRect m_rect_tick_action; - wxRect m_rect_one_layer_icon; - wxRect m_rect_revert_icon; - wxRect m_rect_cog_icon; - wxSize m_thumb_size; - int m_tick_icon_dim; - int m_lock_icon_dim; - int m_revert_icon_dim; - int m_cog_icon_dim; - long m_style; - float m_label_koef = 1.0; - -// control's view variables - wxCoord SLIDER_MARGIN; // margin around slider - - wxPen DARK_ORANGE_PEN; - wxPen ORANGE_PEN; - wxPen LIGHT_ORANGE_PEN; - - wxPen DARK_GREY_PEN; - wxPen GREY_PEN; - wxPen LIGHT_GREY_PEN; - - std::vector m_line_pens; - std::vector m_segm_pens; - std::vector m_values; - - struct TICK_CODE_INFO - { - std::set ticks; - t_mode mode = t_mode::SingleExtruder; - - bool empty() const { return ticks.empty(); } - void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } - - bool add_tick (const int tick, std::string &code, int extruder, double print_z); - bool edit_tick (std::set::iterator it, double print_z); - void switch_code(const std::string& code_from, const std::string& code_to); - bool switch_code_for_tick (std::set::iterator it, const std::string& code_to, const int extruder); - void erase_all_ticks_with_code (const std::string& gcode); - bool has_tick_with_code (const std::string& gcode); - - void suppress_plus (bool suppress) { m_suppress_plus = suppress;} - void suppress_minus(bool suppress) { m_suppress_minus = suppress;} - bool suppressed_plus () { return m_suppress_plus ; } - bool suppressed_minus() { return m_suppress_minus; } - - private: - - std::string custom_gcode = ""; - std::string pause_print_msg = ""; - bool m_suppress_plus = false; - bool m_suppress_minus = false; - - std::string get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder); - } - m_ticks; - -public: - struct ExtrudersSequence - { - bool is_mm_intervals = true; - double interval_by_mm = 3.0; - int interval_by_layers = 10; - std::vector extruders = { 0 }; - - bool operator==(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals == this->is_mm_intervals ) && - (other.interval_by_mm == this->interval_by_mm ) && - (other.interval_by_layers == this->interval_by_layers ) && - (other.extruders == this->extruders ) ; - } - bool operator!=(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals != this->is_mm_intervals ) && - (other.interval_by_mm != this->interval_by_mm ) && - (other.interval_by_layers != this->interval_by_layers ) && - (other.extruders != this->extruders ) ; - } - - void add_extruder(size_t pos) - { - extruders.insert(extruders.begin() + pos+1, size_t(0)); - } - - void delete_extruder(size_t pos) - { - if (extruders.size() == 1) - return;// last item can't be deleted - extruders.erase(extruders.begin() + pos); - } - } - m_extruders_sequence; -}; - - // ---------------------------------------------------------------------------- // LockButton // ---------------------------------------------------------------------------- From 8bf53370c18cd750f0a750d449089efa206ea4bc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 16:40:18 +0100 Subject: [PATCH 27/28] Bumped up version number 2.2.0-alpha3, so that the updated bundled profiles may require this version (2.2.0-alpha3) or newer. This change is needed for the clients to be forced to update the configurations to see the print bed, as the print bed model and image are no more hard coded, but they are referenced from the vendor specific Preset Bundle. --- resources/profiles/PrusaResearch.idx | 2 ++ resources/profiles/PrusaResearch.ini | 2 +- version.inc | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index c451bb8bf..e84d5dd2a 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.2.0-alpha3 +1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. min_slic3r_version = 2.2.0-alpha0 1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. 1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 4f5c0482b..aa4b7cff0 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.1.1-alpha2 +config_version = 1.1.1-alpha3 # Where to get the updates from? config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% diff --git a/version.inc b/version.inc index dbd97b655..997724f0b 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.2.0-alpha2") +set(SLIC3R_VERSION "2.2.0-alpha3") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,2,0,0") set(SLIC3R_RC_VERSION_DOTS "2.2.0.0") From a0e6afcee871d74de2d7dacb1e798215e62646ca Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 23 Jan 2020 16:52:24 +0100 Subject: [PATCH 28/28] Update of PrusaSlicer.idx to force the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, so they will see the print bed. --- resources/profiles/PrusaResearch.idx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index e84d5dd2a..9ac22c59d 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,5 +1,8 @@ min_slic3r_version = 2.2.0-alpha3 1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. +# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, +# so they will see the print bed. +max_slic3r_version = 2.2.0-alpha2 min_slic3r_version = 2.2.0-alpha0 1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. 1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0