diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d4fcdd2e0..deb2bb08d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -834,6 +834,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; bool all_contained = true; + bool contained_min_one = false; + for (GLVolume* volume : this->volumes) { if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) @@ -843,6 +845,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M bool contained = print_volume.contains(bb); all_contained &= contained; + if (contained) + contained_min_one = true; + volume->is_outside = !contained; if ((state == ModelInstance::PVS_Inside) && volume->is_outside) @@ -855,7 +860,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (out_state != nullptr) *out_state = state; - return all_contained; + return /*all_contained*/ contained_min_one; // #ys_FIXME_delete_after_testing } void GLVolumeCollection::reset_outside_state() diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index a2299e7bf..a15d3faef 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -123,6 +123,9 @@ public: // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // and it does not account for the OctoPrint scheduling. bool finished() const { return m_print->finished(); } + + // set status line + void set_status(const std::string & status_str) const { m_print->set_status(100, status_str); } private: void thread_proc(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 855365130..a3a523d94 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3272,7 +3272,7 @@ bool GLCanvas3D::Gizmos::generate_icons_texture() const } #endif // ENABLE_SVG_ICONS -const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 }; +const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 120, 120, 120 };//{ 9, 91, 134 }; const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; GLCanvas3D::WarningTexture::WarningTexture() @@ -3306,16 +3306,23 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool // Look at the end of our vector and generate proper texture. std::string text; + bool red_colored = false; switch (m_warnings.back()) { case ObjectOutside : text = L("Detected object outside print volume"); break; case ToolpathOutside : text = L("Detected toolpath outside print volume"); break; case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break; + case ObjectClashed: { + text = L("Detected object outside print volume\n" + "Resolve a clash to continue slicing/export process correctly"); + red_colored = true; + break; + } } - _generate(text, canvas); // GUI::GLTexture::reset() is called at the beginning of generate(...) + _generate(text, canvas, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...) } -bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanvas3D& canvas) +bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored/* = false*/) { reset(); @@ -3332,7 +3339,8 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanva // calculates texture size wxCoord w, h; - memDC.GetTextExtent(msg, &w, &h); +// memDC.GetTextExtent(msg, &w, &h); + memDC.GetMultiLineTextExtent(msg, &w, &h); int pow_of_two_size = next_highest_power_of_2(std::max(w, h)); @@ -3349,8 +3357,9 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanva memDC.Clear(); // draw message - memDC.SetTextForeground(*wxWHITE); - memDC.DrawText(msg, 0, 0); + memDC.SetTextForeground(red_colored ? wxColour(255,72,65/*204,204*/) : *wxWHITE); +// memDC.DrawText(msg, 0, 0); + memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER); memDC.SelectObject(wxNullBitmap); @@ -4579,23 +4588,33 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (!m_volumes.empty()) { ModelInstance::EPrintVolumeState state; - bool contained = m_volumes.check_outside_state(m_config, &state); - if (!contained) - { - _set_warning_texture(WarningTexture::ObjectOutside, true); - post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside)); - } - else - { - m_volumes.reset_outside_state(); - _set_warning_texture(WarningTexture::ObjectOutside, false); - post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty())); - } + const bool contained_min_one = m_volumes.check_outside_state(m_config, &state); + + _set_warning_texture(WarningTexture::ObjectClashed, state == ModelInstance::PVS_Partly_Outside); + _set_warning_texture(WarningTexture::ObjectOutside, state == ModelInstance::PVS_Fully_Outside); + + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, + contained_min_one && !m_model->objects.empty() && state != ModelInstance::PVS_Partly_Outside)); + +// #ys_FIXME_delete_after_testing +// bool contained = m_volumes.check_outside_state(m_config, &state); +// if (!contained) +// { +// _set_warning_texture(WarningTexture::ObjectOutside, true); +// post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside)); +// } +// else +// { +// m_volumes.reset_outside_state(); +// _set_warning_texture(WarningTexture::ObjectOutside, false); +// post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty())); +// } } else { _set_warning_texture(WarningTexture::ObjectOutside, false); + _set_warning_texture(WarningTexture::ObjectClashed, false); post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7c39ab490..07b3907a3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -794,7 +794,8 @@ private: enum Warning { ObjectOutside, ToolpathOutside, - SomethingNotShown + SomethingNotShown, + ObjectClashed }; // Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously, @@ -813,7 +814,7 @@ private: std::vector m_warnings; // Generates the texture with given text. - bool _generate(const std::string& msg, const GLCanvas3D& canvas); + bool _generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored = false); }; class LegendTexture : public GUI::GLTexture diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 194399316..73ff19a9d 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -534,20 +534,7 @@ void GUI_App::save_mode(const /*ConfigOptionMode*/int mode) // Update view mode according to selected menu void GUI_App::update_mode() { - wxWindowUpdateLocker noUpdates(&sidebar()); - - const ConfigOptionMode mode = wxGetApp().get_mode(); - - obj_list()->get_sizer()->Show(mode > comSimple); - sidebar().set_mode_value(mode); -// sidebar().show_buttons(mode == comExpert); - obj_list()->unselect_objects(); - obj_list()->update_selections(); - obj_list()->update_object_menu(); - - sidebar().update_mode_sizer(mode); - - sidebar().Layout(); + sidebar().update_mode(); for (auto tab : tabs_list) tab->update_visibility(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index cddf081eb..1d1982ffa 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -881,9 +881,10 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const // Update the UI based on the current preferences. void MainFrame::update_ui_from_settings() { - bool bp_on = wxGetApp().app_config->get("background_processing") == "1"; + const bool bp_on = wxGetApp().app_config->get("background_processing") == "1"; // m_menu_item_reslice_now->Enable(!bp_on); m_plater->sidebar().show_reslice(!bp_on); + m_plater->sidebar().show_export(bp_on); m_plater->sidebar().Layout(); if (m_plater) m_plater->update_ui_from_settings(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 808ad81c8..c01d89352 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -513,6 +513,12 @@ ConfigOptionsGroup* FreqChangedParams::get_og(const bool is_fff) // Sidebar / private +enum class ActionButtonType : int { + abReslice, + abExport, + abSendGCode +}; + struct Sidebar::priv { Plater *plater; @@ -675,7 +681,13 @@ Sidebar::Sidebar(Plater *parent) // Events p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); }); - p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->reslice(); }); + p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) + { + const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); + p->plater->reslice(); + if (export_gcode_after_slicing) + p->plater->export_gcode(); + }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); } @@ -773,9 +785,15 @@ void Sidebar::update_presets(Preset::Type preset_type) wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); } -void Sidebar::update_mode_sizer(const Slic3r::ConfigOptionMode& mode) +void Sidebar::update_mode_sizer() const { - p->mode_sizer->SetMode(mode); + p->mode_sizer->SetMode(m_mode); +} + +void Sidebar::update_reslice_btn_tooltip() const +{ + const wxString tooltip = m_mode == comSimple ? wxString("") : _(L("Hold Shift to Slice & Export G-code")); + p->btn_reslice->SetToolTip(tooltip); } ObjectManipulation* Sidebar::obj_manipul() @@ -968,8 +986,9 @@ void Sidebar::enable_buttons(bool enable) p->btn_send_gcode->Enable(enable); } -void Sidebar::show_reslice(bool show) { p->btn_reslice->Show(show); } -void Sidebar::show_send(bool show) { p->btn_send_gcode->Show(show); } +void Sidebar::show_reslice(bool show) const { p->btn_reslice->Show(show); } +void Sidebar::show_export(bool show) const { p->btn_export_gcode->Show(show); } +void Sidebar::show_send(bool show) const { p->btn_send_gcode->Show(show); } bool Sidebar::is_multifilament() { @@ -977,6 +996,24 @@ bool Sidebar::is_multifilament() } +void Sidebar::update_mode() +{ + m_mode = wxGetApp().get_mode(); + + update_reslice_btn_tooltip(); + update_mode_sizer(); + + wxWindowUpdateLocker noUpdates(this); + + p->object_list->get_sizer()->Show(m_mode > comSimple); + + p->object_list->unselect_objects(); + p->object_list->update_selections(); + p->object_list->update_object_menu(); + + Layout(); +} + std::vector& Sidebar::combos_filament() { return p->combos_filament; @@ -1060,6 +1097,8 @@ struct Plater::priv wxTimer background_process_timer; + std::string label_btn_export; + static const std::regex pattern_bundle; static const std::regex pattern_3mf; static const std::regex pattern_zip_amf; @@ -1148,6 +1187,7 @@ struct Plater::priv void on_3dcanvas_mouse_dragging_finished(SimpleEvent&); void update_object_menu(); + void show_action_buttons(const bool is_ready_to_slice) const; // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, @@ -2118,6 +2158,35 @@ unsigned int Plater::priv::update_background_process(bool force_validation) // jinak background_process.running() -> Zobraz "Slicing ..." // jinak pokud ! background_process.empty() && ! background_process.finished() -> je neco ke slajsovani (povol tlacitko) "Slice Now" + if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0) + { + const wxString invalid_str = _(L("Invalid data")); + for (auto btn : {ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport}) + sidebar->set_btn_label(btn, invalid_str); + } + else + { + if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 || + (return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 ) + background_process.set_status("Ready to slice"); + + sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export)); + sidebar->set_btn_label(ActionButtonType::abSendGCode, _(L("Send G-code"))); + + const wxString slice_string = background_process.running() && wxGetApp().get_mode() == comSimple ? + _(L("Slicing")) + dots : _(L("Slice now")); + sidebar->set_btn_label(ActionButtonType::abReslice, slice_string); + + if (background_process.finished()) + show_action_buttons(false); + else if (!background_process.empty() && + !background_process.running()) /* Do not update buttons if background process is running + * This condition is important for SLA mode especially, + * when this function is called several times during calculations + * */ + show_action_buttons(true); + } + return return_state; } @@ -2430,6 +2499,14 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) this->update_sla_scene(); break; } + + if (canceled) { + if (wxGetApp().get_mode() == comSimple) + sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now"); + show_action_buttons(true); + } + else if (wxGetApp().get_mode() == comSimple) + show_action_buttons(false); } void Plater::priv::on_layer_editing_toggled(bool enable) @@ -2798,6 +2875,37 @@ void Plater::priv::update_object_menu() view3D->update_toolbar_items_visibility(); } +void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const +{ + 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(); + + // when a background processing is ON, export_btn and/or send_btn are showing + if (wxGetApp().app_config->get("background_processing") == "1") + { + sidebar->show_reslice(false); + sidebar->show_export(true); + sidebar->show_send(send_gcode_shown); + } + else + { + sidebar->show_reslice(is_ready_to_slice); + sidebar->show_export(!is_ready_to_slice); + sidebar->show_send(send_gcode_shown && !is_ready_to_slice); + } +} + +void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const +{ + switch (btn_type) + { + case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break; + case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break; + case ActionButtonType::abSendGCode: p->btn_send_gcode->SetLabelText(label); break; + } +} + // Plater / Public Plater::Plater(wxWindow *parent, MainFrame *main_frame) @@ -3124,6 +3232,22 @@ void Plater::reslice() this->p->background_process.set_task(PrintBase::TaskParams()); // Only restarts if the state is valid. this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); + + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) + return; + + if (p->background_process.running()) + { + if (wxGetApp().get_mode() == comSimple) + p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slicing")) + dots); + else + { + p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slice now"))); + p->show_action_buttons(false); + } + } + else if (!p->background_process.empty() && !p->background_process.idle()) + p->show_action_buttons(true); } void Plater::reslice_SLA_supports(const ModelObject &object) @@ -3322,6 +3446,8 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) } //FIXME for SLA synchronize //p->background_process.apply(Model)! + + p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export"); // #ys_FIXME_rename } void Plater::changed_object(int obj_idx) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 4d489c82a..78035d0ab 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -38,6 +38,7 @@ class GLCanvas3D; using t_optgroups = std::vector >; class Plater; +enum class ActionButtonType : int; class PresetComboBox : public wxBitmapComboBox { @@ -61,7 +62,7 @@ private: class Sidebar : public wxPanel { - /*ConfigOptionMode*/int m_mode; + ConfigOptionMode m_mode; public: Sidebar(Plater *parent); Sidebar(Sidebar &&) = delete; @@ -73,7 +74,8 @@ public: void init_filament_combo(PresetComboBox **combo, const int extr_idx); void remove_unused_filament_combos(const int current_extruder_count); void update_presets(Slic3r::Preset::Type preset_type); - void update_mode_sizer(const Slic3r::ConfigOptionMode& mode); + void update_mode_sizer() const; + void update_reslice_btn_tooltip() const; ObjectManipulation* obj_manipul(); ObjectList* obj_list(); @@ -86,10 +88,12 @@ public: void show_info_sizer(); void show_sliced_info_sizer(const bool show); void enable_buttons(bool enable); - void show_reslice(bool show); - void show_send(bool show); + void set_btn_label(const ActionButtonType btn_type, const wxString& label) const; + void show_reslice(bool show) const; + void show_export(bool show) const; + void show_send(bool show) const; bool is_multifilament(); - void set_mode_value(const /*ConfigOptionMode*/int mode) { m_mode = mode; } + void update_mode(); std::vector& combos_filament(); private: